package identity import ( "context" "database/sql" "testing" "github.com/DATA-DOG/go-sqlmock" "lingniu-vehicle-ingest/go/vehicle-gateway/internal/envelope" ) func TestCandidateKeysPrioritizesPhoneDevicePlate(t *testing.T) { keys := CandidateKeys(envelope.FrameEnvelope{ Phone: "013307795425", DeviceID: "D1", Plate: "豫A12345", VehicleKeyHint: "D1", }) got := []string{} for _, key := range keys { got = append(got, key.Column+"="+key.Value) } want := []string{"phone=013307795425", "phone=13307795425", "device_id=D1", "plate=豫A12345", "vin=D1"} if len(got) != len(want) { t.Fatalf("candidate count = %d got=%#v", len(got), got) } for i := range want { if got[i] != want[i] { t.Fatalf("candidate[%d] = %q, want %q", i, got[i], want[i]) } } } func TestCandidateKeysAddsTrimmedPhoneVariant(t *testing.T) { keys := CandidateKeys(envelope.FrameEnvelope{ Phone: "013079963379", }) got := []string{} for _, key := range keys { got = append(got, key.Column+"="+key.Value) } want := []string{"phone=013079963379", "phone=13079963379"} if len(got) != len(want) { t.Fatalf("candidate count = %d got=%#v", len(got), got) } for i := range want { if got[i] != want[i] { t.Fatalf("candidate[%d] = %q, want %q", i, got[i], want[i]) } } } func TestMySQLResolverFillsVINFromPhone(t *testing.T) { db, mock := newMockDB(t) defer db.Close() mock.ExpectQuery("SELECT vin FROM vehicle_identity_binding WHERE phone = \\?"). WithArgs("013307795425"). WillReturnRows(sqlmock.NewRows([]string{"vin"}).AddRow("LNBVIN00000000001")) resolver := NewMySQLResolver(db, "vehicle_identity_binding") env, err := resolver.Resolve(context.Background(), envelope.FrameEnvelope{ Protocol: envelope.ProtocolJT808, Phone: "013307795425", Parsed: map[string]any{}, }) if err != nil { t.Fatalf("Resolve() error = %v", err) } if env.VIN != "LNBVIN00000000001" { t.Fatalf("vin = %q", env.VIN) } identity, ok := env.Parsed["identity"].(map[string]any) if !ok || identity["source"] != "phone" { t.Fatalf("identity metadata = %#v", env.Parsed["identity"]) } if err := mock.ExpectationsWereMet(); err != nil { t.Fatalf("sql expectations: %v", err) } } func TestMySQLResolverFallsBackToDeviceID(t *testing.T) { db, mock := newMockDB(t) defer db.Close() mock.ExpectQuery("SELECT vin FROM vehicle_identity_binding WHERE phone = \\?"). WithArgs("013307795425"). WillReturnError(sql.ErrNoRows) mock.ExpectQuery("SELECT vin FROM vehicle_identity_binding WHERE phone = \\?"). WithArgs("13307795425"). WillReturnError(sql.ErrNoRows) mock.ExpectQuery("SELECT vin FROM vehicle_identity_binding WHERE device_id = \\?"). WithArgs("D1"). WillReturnRows(sqlmock.NewRows([]string{"vin"}).AddRow("LNBVIN00000000002")) resolver := NewMySQLResolver(db, "vehicle_identity_binding") env, err := resolver.Resolve(context.Background(), envelope.FrameEnvelope{ Protocol: envelope.ProtocolJT808, Phone: "013307795425", DeviceID: "D1", }) if err != nil { t.Fatalf("Resolve() error = %v", err) } if env.VIN != "LNBVIN00000000002" { t.Fatalf("vin = %q", env.VIN) } if err := mock.ExpectationsWereMet(); err != nil { t.Fatalf("sql expectations: %v", err) } } func newMockDB(t *testing.T) (*sql.DB, sqlmock.Sqlmock) { t.Helper() db, mock, err := sqlmock.New() if err != nil { t.Fatalf("sqlmock.New() error = %v", err) } return db, mock }