Files
lingniu-vehicle-ingest/vehicle-data-platform/apps/api/internal/businessscope/model_test.go

94 lines
3.1 KiB
Go

package businessscope
import (
"testing"
"time"
)
func validCandidate(row int, vehicleID, customerID int64, vin string) Candidate {
return Candidate{
RowNumber: row, VehicleID: vehicleID, VehiclePresent: true, VIN: vin, PlateNumber: "沪A00001",
CustomerID: customerID, CustomerPresent: true, CustomerProfileExists: true,
ContractID: vehicleID + 1000, ContractPresent: true, ContractProfileExists: true,
EffectiveCustomerID: customerID, EffectiveCustomerSet: true,
ContractCode: "HT-1", ProjectName: "项目", OperationStatus: "1",
ScopeStartAt: time.Date(2026, 7, 1, 8, 0, 0, 0, time.FixedZone("CST", 8*3600)),
}
}
func TestBuildSnapshotClassifiesAndNormalizes(t *testing.T) {
candidates := []Candidate{
validCandidate(1, 10, 100, " lvin0001 "),
func() Candidate {
value := validCandidate(2, 11, 101, "LVIN0002")
value.CustomerProfileExists = false
return value
}(),
func() Candidate {
value := validCandidate(3, 12, 102, "LVIN0003")
value.EffectiveCustomerID = 999
return value
}(),
}
snapshot, err := BuildSnapshot(candidates, time.Now())
if err != nil {
t.Fatal(err)
}
if snapshot.Candidates != 3 || len(snapshot.Items) != 1 || len(snapshot.Rejections) != 2 {
t.Fatalf("unexpected accounting: %#v", snapshot)
}
if snapshot.Items[0].VIN != "LVIN0001" {
t.Fatalf("VIN not normalized: %q", snapshot.Items[0].VIN)
}
if snapshot.Rejections[0].ReasonCode != ReasonCustomerProfileMissing || snapshot.Rejections[1].ReasonCode != ReasonCustomerMismatch {
t.Fatalf("unexpected reasons: %#v", snapshot.Rejections)
}
}
func TestBuildSnapshotRejectsEveryDuplicateVehicle(t *testing.T) {
snapshot, err := BuildSnapshot([]Candidate{
validCandidate(1, 10, 100, "LVIN0001"),
validCandidate(2, 10, 100, "LVIN0001"),
}, time.Now())
if err != nil {
t.Fatal(err)
}
if len(snapshot.Items) != 0 || len(snapshot.Rejections) != 2 {
t.Fatalf("duplicates must all be quarantined: %#v", snapshot)
}
for _, rejection := range snapshot.Rejections {
if rejection.ReasonCode != ReasonDuplicateVehicle {
t.Fatalf("unexpected duplicate reason: %#v", rejection)
}
}
}
func TestSnapshotVersionIsContentAddressed(t *testing.T) {
first := []Candidate{validCandidate(1, 10, 100, "LVIN0001"), validCandidate(2, 11, 101, "LVIN0002")}
second := []Candidate{first[1], first[0]}
a, err := BuildSnapshot(first, time.Now())
if err != nil {
t.Fatal(err)
}
b, err := BuildSnapshot(second, time.Now().Add(time.Hour))
if err != nil {
t.Fatal(err)
}
if a.SourceVersion != b.SourceVersion || a.Checksum != b.Checksum {
t.Fatalf("same content produced different versions: %s %s", a.SourceVersion, b.SourceVersion)
}
}
func TestValidateSnapshotUsesCountAndRatioGates(t *testing.T) {
snapshot := Snapshot{Candidates: 639, Items: make([]ScopeItem, 613), Rejections: make([]Rejection, 26)}
if err := ValidateSnapshot(snapshot, 100, 0.10); err != nil {
t.Fatalf("production baseline should pass: %v", err)
}
if err := ValidateSnapshot(snapshot, 20, 0.10); err == nil {
t.Fatal("rejection count limit should fail")
}
if err := ValidateSnapshot(snapshot, 100, 0.01); err == nil {
t.Fatal("rejection ratio limit should fail")
}
}