250 lines
8.3 KiB
Go
250 lines
8.3 KiB
Go
package topics
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
|
|
"lingniu-vehicle-ingest/go/vehicle-gateway/internal/envelope"
|
|
)
|
|
|
|
func TestValidateKafkaRawFieldsRejectsWrongFamilyPrefix(t *testing.T) {
|
|
err := ValidateKafkaRawFields(
|
|
map[string]string{"JT808": FieldsJT808},
|
|
map[string]string{"JT808": FieldsJT808},
|
|
)
|
|
if err == nil {
|
|
t.Fatal("ValidateKafkaRawFields() error = nil, want raw prefix rejection")
|
|
}
|
|
if !strings.Contains(err.Error(), "raw kafka topic") {
|
|
t.Fatalf("error = %q, want raw kafka topic hint", err)
|
|
}
|
|
|
|
err = ValidateKafkaRawFields(
|
|
map[string]string{"JT808": RawJT808},
|
|
map[string]string{"JT808": RawJT808},
|
|
)
|
|
if err == nil {
|
|
t.Fatal("ValidateKafkaRawFields() error = nil, want fields prefix rejection")
|
|
}
|
|
if !strings.Contains(err.Error(), "fields kafka topic") {
|
|
t.Fatalf("error = %q, want fields kafka topic hint", err)
|
|
}
|
|
}
|
|
|
|
func TestValidateRawFieldsDisjointRejectsSharedValue(t *testing.T) {
|
|
err := ValidateRawFieldsDisjoint(
|
|
map[string]string{"raw-jt808": "vehicle.same.jt808"},
|
|
map[string]string{"fields-jt808": "vehicle.same.jt808"},
|
|
"nats subject",
|
|
)
|
|
if err == nil {
|
|
t.Fatal("ValidateRawFieldsDisjoint() error = nil, want duplicate rejection")
|
|
}
|
|
if !strings.Contains(err.Error(), "must be different") {
|
|
t.Fatalf("error = %q, want disjoint hint", err)
|
|
}
|
|
}
|
|
|
|
func TestValidateKnownFieldsTopicProtocolRejectsMismatchedProtocol(t *testing.T) {
|
|
err := ValidateKnownFieldsTopicProtocol(FieldsGB32960, "JT808")
|
|
if err == nil {
|
|
t.Fatal("ValidateKnownFieldsTopicProtocol() error = nil, want mismatch rejection")
|
|
}
|
|
if !strings.Contains(err.Error(), "expects protocol GB32960") {
|
|
t.Fatalf("error = %q, want expected protocol hint", err)
|
|
}
|
|
}
|
|
|
|
func TestValidateKnownRawTopicProtocolRejectsMismatchedProtocol(t *testing.T) {
|
|
err := ValidateKnownRawTopicProtocol(RawJT808, "GB32960")
|
|
if err == nil {
|
|
t.Fatal("ValidateKnownRawTopicProtocol() error = nil, want mismatch rejection")
|
|
}
|
|
if !strings.Contains(err.Error(), "expects protocol JT808") {
|
|
t.Fatalf("error = %q, want expected protocol hint", err)
|
|
}
|
|
}
|
|
|
|
func TestValidateKnownTopicProtocolAllowsMatchingAndCustomTopics(t *testing.T) {
|
|
if err := ValidateKnownRawTopicProtocol(RawGB32960, "GB32960"); err != nil {
|
|
t.Fatalf("ValidateKnownRawTopicProtocol() matching topic error = %v", err)
|
|
}
|
|
if err := ValidateKnownRawTopicProtocol("vehicle.raw.custom.g7", "JT808"); err != nil {
|
|
t.Fatalf("ValidateKnownRawTopicProtocol() custom topic error = %v", err)
|
|
}
|
|
if err := ValidateKnownFieldsTopicProtocol(FieldsJT808, "JT808"); err != nil {
|
|
t.Fatalf("ValidateKnownFieldsTopicProtocol() matching topic error = %v", err)
|
|
}
|
|
if err := ValidateKnownFieldsTopicProtocol("vehicle.fields.custom.g7", "JT808"); err != nil {
|
|
t.Fatalf("ValidateKnownFieldsTopicProtocol() custom topic error = %v", err)
|
|
}
|
|
}
|
|
|
|
func TestValidateRawEnvelopeRejectsExplicitNonRawEventKind(t *testing.T) {
|
|
status, err := ValidateRawEnvelope(RawJT808, envelope.FrameEnvelope{
|
|
Protocol: envelope.ProtocolJT808,
|
|
EventKind: envelope.EventKindFields,
|
|
})
|
|
if err == nil {
|
|
t.Fatal("ValidateRawEnvelope() error = nil, want event kind mismatch")
|
|
}
|
|
if status != "event_kind_mismatch" {
|
|
t.Fatalf("status = %q, want event_kind_mismatch", status)
|
|
}
|
|
if !strings.Contains(err.Error(), "expects event kind RAW") {
|
|
t.Fatalf("error = %q, want RAW hint", err)
|
|
}
|
|
}
|
|
|
|
func TestValidateRawEnvelopeAllowsHistoricalEmptyAndRawEventKind(t *testing.T) {
|
|
for _, kind := range []envelope.EventKind{"", envelope.EventKindRaw} {
|
|
if status, err := ValidateRawEnvelope(RawGB32960, envelope.FrameEnvelope{Protocol: envelope.ProtocolGB32960, EventKind: kind}); err != nil {
|
|
t.Fatalf("ValidateRawEnvelope(%q) status=%q error=%v", kind, status, err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestValidateFieldsEnvelopeRejectsExplicitNonFieldsEventKind(t *testing.T) {
|
|
status, err := ValidateFieldsEnvelope(FieldsYutongMQTT, envelope.FrameEnvelope{
|
|
Protocol: envelope.ProtocolYutongMQTT,
|
|
EventKind: envelope.EventKindRaw,
|
|
})
|
|
if err == nil {
|
|
t.Fatal("ValidateFieldsEnvelope() error = nil, want event kind mismatch")
|
|
}
|
|
if status != "event_kind_mismatch" {
|
|
t.Fatalf("status = %q, want event_kind_mismatch", status)
|
|
}
|
|
if !strings.Contains(err.Error(), "expects event kind FIELDS") {
|
|
t.Fatalf("error = %q, want FIELDS hint", err)
|
|
}
|
|
}
|
|
|
|
func TestValidateFieldsEnvelopeRequiresFieldsContract(t *testing.T) {
|
|
status, err := ValidateFieldsEnvelope(FieldsJT808, envelope.FrameEnvelope{
|
|
Protocol: envelope.ProtocolJT808,
|
|
EventKind: envelope.EventKindFields,
|
|
Fields: map[string]any{"jt808.location.total_mileage_km": 10241.2},
|
|
})
|
|
if err == nil {
|
|
t.Fatal("ValidateFieldsEnvelope() error = nil, want missing field mapping")
|
|
}
|
|
if status != "missing_field_mapping" {
|
|
t.Fatalf("status = %q, want missing_field_mapping", status)
|
|
}
|
|
|
|
status, err = ValidateFieldsEnvelope(FieldsJT808, envelope.FrameEnvelope{
|
|
Protocol: envelope.ProtocolJT808,
|
|
EventKind: envelope.EventKindFields,
|
|
FieldMapping: "2026-07-03.v1",
|
|
})
|
|
if err == nil {
|
|
t.Fatal("ValidateFieldsEnvelope() error = nil, want missing fields")
|
|
}
|
|
if status != "missing_fields" {
|
|
t.Fatalf("status = %q, want missing_fields", status)
|
|
}
|
|
|
|
status, err = ValidateFieldsEnvelope(FieldsJT808, envelope.FrameEnvelope{
|
|
Protocol: envelope.ProtocolJT808,
|
|
EventKind: envelope.EventKindFields,
|
|
FieldMapping: "2026-07-03.v1",
|
|
Fields: map[string]any{"jt808.location.total_mileage_km": 10241.2},
|
|
})
|
|
if err != nil || status != "" {
|
|
t.Fatalf("ValidateFieldsEnvelope() status=%q error=%v, want ok", status, err)
|
|
}
|
|
}
|
|
|
|
func TestValidateFieldsEnvelopeRejectsNonProtocolFieldNames(t *testing.T) {
|
|
for _, tt := range []struct {
|
|
name string
|
|
topic string
|
|
protocol envelope.Protocol
|
|
fields map[string]any
|
|
}{
|
|
{
|
|
name: "normalized core field",
|
|
topic: FieldsJT808,
|
|
protocol: envelope.ProtocolJT808,
|
|
fields: map[string]any{"total_mileage_km": 10241.2},
|
|
},
|
|
{
|
|
name: "other protocol namespace",
|
|
topic: FieldsGB32960,
|
|
protocol: envelope.ProtocolGB32960,
|
|
fields: map[string]any{"jt808.location.latitude": 30.1},
|
|
},
|
|
{
|
|
name: "empty suffix",
|
|
topic: FieldsYutongMQTT,
|
|
protocol: envelope.ProtocolYutongMQTT,
|
|
fields: map[string]any{"yutong_mqtt.": 1},
|
|
},
|
|
} {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
status, err := ValidateFieldsEnvelope(tt.topic, envelope.FrameEnvelope{
|
|
Protocol: tt.protocol,
|
|
EventKind: envelope.EventKindFields,
|
|
FieldMapping: "2026-07-03.v1",
|
|
Fields: tt.fields,
|
|
})
|
|
if err == nil {
|
|
t.Fatal("ValidateFieldsEnvelope() error = nil, want invalid field rejection")
|
|
}
|
|
if status != "invalid_field_name" {
|
|
t.Fatalf("status = %q, want invalid_field_name", status)
|
|
}
|
|
if !strings.Contains(err.Error(), "expects every field name") {
|
|
t.Fatalf("error = %q, want protocol namespace hint", err)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestValidateFieldsEnvelopeAcceptsProtocolNamespaces(t *testing.T) {
|
|
for _, tt := range []struct {
|
|
topic string
|
|
protocol envelope.Protocol
|
|
field string
|
|
}{
|
|
{topic: FieldsGB32960, protocol: envelope.ProtocolGB32960, field: "gb32960.vehicle.total_mileage_km"},
|
|
{topic: FieldsJT808, protocol: envelope.ProtocolJT808, field: "jt808.location.total_mileage_km"},
|
|
{topic: FieldsYutongMQTT, protocol: envelope.ProtocolYutongMQTT, field: "yutong_mqtt.root.data.total_mileage"},
|
|
} {
|
|
status, err := ValidateFieldsEnvelope(tt.topic, envelope.FrameEnvelope{
|
|
Protocol: tt.protocol,
|
|
EventKind: envelope.EventKindFields,
|
|
FieldMapping: "2026-07-03.v1",
|
|
Fields: map[string]any{tt.field: 1},
|
|
})
|
|
if err != nil || status != "" {
|
|
t.Fatalf("ValidateFieldsEnvelope(%s) status=%q error=%v", tt.protocol, status, err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestValidateKafkaRawFieldsRejectsKnownProtocolMismatch(t *testing.T) {
|
|
err := ValidateKafkaRawFields(
|
|
map[string]string{"JT808": RawGB32960},
|
|
map[string]string{"JT808": FieldsJT808},
|
|
)
|
|
if err == nil {
|
|
t.Fatal("ValidateKafkaRawFields() error = nil, want known raw protocol mismatch")
|
|
}
|
|
if !strings.Contains(err.Error(), "must match protocol") {
|
|
t.Fatalf("error = %q, want protocol mismatch hint", err)
|
|
}
|
|
|
|
err = ValidateKafkaRawFields(
|
|
map[string]string{"JT808": RawJT808},
|
|
map[string]string{"JT808": FieldsGB32960},
|
|
)
|
|
if err == nil {
|
|
t.Fatal("ValidateKafkaRawFields() error = nil, want known fields protocol mismatch")
|
|
}
|
|
if !strings.Contains(err.Error(), "must match protocol") {
|
|
t.Fatalf("error = %q, want protocol mismatch hint", err)
|
|
}
|
|
}
|