package yutongmqtt import ( "errors" "testing" "lingniu-vehicle-ingest/go/vehicle-gateway/internal/envelope" ) func TestParseMessageMapsYutongPayloadToEnvelope(t *testing.T) { payload := []byte(`{ "device": "LTEST000000000001", "time": "20260413100000", "plateNo": "豫A12345", "data": { "METER_SPEED": 52.3, "TOTAL_MILEAGE": 123456.7, "BATTERY_CAPACITY_SOC": 70, "LONGITUDE": 116397128, "LATITUDE": 39916527, "GPSDirection": 88 } }`) env, err := ParseMessage("endpoint-a", "/ytforward/shln/dev1", payload, 1782745114999) if err != nil { t.Fatalf("ParseMessage() error = %v", err) } if env.Protocol != envelope.ProtocolYutongMQTT { t.Fatalf("protocol = %q", env.Protocol) } if env.VIN != "LTEST000000000001" || env.DeviceID != "LTEST000000000001" { t.Fatalf("identity fields = vin:%q device:%q", env.VIN, env.DeviceID) } if env.Plate != "豫A12345" { t.Fatalf("plate = %q", env.Plate) } assertFloatField(t, env, envelope.FieldSpeedKMH, 52.3) assertFloatField(t, env, envelope.FieldTotalMileageKM, 123.4567) assertFloatField(t, env, envelope.FieldSOCPercent, 70) assertFloatField(t, env, envelope.FieldLongitude, 116.397128) assertFloatField(t, env, envelope.FieldLatitude, 39.916527) assertFloatField(t, env, "direction_deg", 88) if env.EventTimeMS == 1782745114999 { t.Fatal("event time should come from device time") } if env.RawText == "" || env.Parsed["data"] == nil { t.Fatalf("raw/parsed missing: %#v", env) } } func TestParseMessageUsesDeviceAsVehicleKeyWhenNotVIN(t *testing.T) { payload := []byte(`{"device":"YT-DEVICE-001","time":"20260413100000","data":{"METER_SPEED":"12.3"}}`) env, err := ParseMessage("endpoint-a", "/ytforward/shln/YT-DEVICE-001", payload, 1782745114999) if err != nil { t.Fatalf("ParseMessage() error = %v", err) } if env.VIN != "" { t.Fatalf("non-vin device should not be treated as vin: %q", env.VIN) } if env.VehicleKey() != "YT-DEVICE-001" { t.Fatalf("vehicle key = %q", env.VehicleKey()) } assertFloatField(t, env, envelope.FieldSpeedKMH, 12.3) } func TestParseMessageNormalizesProductionMileageMeters(t *testing.T) { payload := []byte(`{ "device":"LMRKH9AC3R1004101", "time":"2026-07-02 01:02:04.085", "data":{"TOTAL_MILEAGE":56905000,"METER_SPEED":19} }`) env, err := ParseMessage("yutong", "/ytforward/shln/1", payload, 1782940000000) if err != nil { t.Fatalf("ParseMessage() error = %v", err) } assertFloatField(t, env, envelope.FieldTotalMileageKM, 56905) if env.EventTimeMS != 1782925324085 { t.Fatalf("event time = %d, want 1782925324085", env.EventTimeMS) } } func TestParseMessageTreatsSmallYutongMileageAsMeters(t *testing.T) { payload := []byte(`{ "device":"LMRKH9AC3R1004101", "time":"2026-07-02 01:02:04.085", "data":{"TOTAL_MILEAGE":1024,"METER_SPEED":19} }`) env, err := ParseMessage("yutong", "/ytforward/shln/1", payload, 1782940000000) if err != nil { t.Fatalf("ParseMessage() error = %v", err) } assertFloatField(t, env, envelope.FieldTotalMileageKM, 1.024) } func TestParseMessageRejectsMalformedJSON(t *testing.T) { _, err := ParseMessage("endpoint-a", "/bad", []byte("{bad-json"), 1782745114999) if !errors.Is(err, ErrInvalidJSON) { t.Fatalf("error = %v", err) } } func assertFloatField(t *testing.T, env envelope.FrameEnvelope, key string, want float64) { t.Helper() got, ok := env.Fields[key].(float64) if !ok { t.Fatalf("field %s missing or not float64: %#v", key, env.Fields[key]) } if got != want { t.Fatalf("field %s = %v, want %v", key, got, want) } }