package realtime import ( "testing" "lingniu-vehicle-ingest/go/vehicle-gateway/internal/envelope" ) func TestRealtimeKVFieldsFromGB32960ParsedDomains(t *testing.T) { rows := realtimeKVFields(envelope.FrameEnvelope{ Protocol: envelope.ProtocolGB32960, VIN: "VIN001", EventTimeMS: 1000, ReceivedAtMS: 1100, EventID: "event-1", }, map[string]any{ "header": map[string]any{"vin": "VIN001", "command": "0x02"}, "data_units": []any{ map[string]any{"type": "0x01", "name": "vehicle", "value": map[string]any{"soc_percent": 88.0}}, map[string]any{"type": "0x30", "name": "gd_fc_stack", "value": map[string]any{ "stack_count": 1, "summaries": []any{ map[string]any{"stack_water_outlet_temp_c": 63, "hydrogen_inlet_pressure_kpa": 130}, }, }}, }, }) values := kvMap(rows) if values["header/vin"] != "VIN001" || values["header/command"] != "0x02" { t.Fatalf("gb32960 header kv missing: %#v", values) } if values["data_units/0.name"] != "vehicle" || values["data_units/0.type"] != "0x01" || values["data_units/0.value.soc_percent"] != "88" { t.Fatalf("vehicle soc kv missing: %#v", values) } if values["data_units/1.value.summaries.0.stack_water_outlet_temp_c"] != "63" || values["data_units/1.value.summaries.0.hydrogen_inlet_pressure_kpa"] != "130" { t.Fatalf("stack pressure kv missing: %#v", values) } } func TestRealtimeKVFieldsFromJT808ParsedFields(t *testing.T) { jtRows := realtimeKVFields(envelope.FrameEnvelope{ Protocol: envelope.ProtocolJT808, VIN: "VIN001", Fields: map[string]any{ envelope.FieldLongitude: 121.1, envelope.FieldLatitude: 30.2, envelope.FieldTotalMileageKM: 10241.2, }, }, map[string]any{ "header": map[string]any{"message_id": "0x0200", "phone": "13307795425"}, "location": map[string]any{ "longitude": 121.1, "latitude": 30.2, "total_mileage_km": 10241.2, "additional": []any{map[string]any{"id": "0x01", "value_hex": "00077235"}}, }, }) jtValues := kvMap(jtRows) if jtValues["header/message_id"] != "0x0200" || jtValues["header/phone"] != "13307795425" { t.Fatalf("jt808 header kv missing: %#v", jtValues) } if jtValues["location/total_mileage_km"] != "10241.2" || jtValues["location/longitude"] != "121.1" || jtValues["location/additional.0.id"] != "0x01" { t.Fatalf("jt808 location kv missing: %#v", jtValues) } if jtValues["location/soc_percent"] != "" { t.Fatalf("jt808 kv should not include standardized env.Fields-only values: %#v", jtValues) } } func TestRealtimeKVFieldsFromYutongMQTTParsedFields(t *testing.T) { mqttRows := realtimeKVFields(envelope.FrameEnvelope{ Protocol: envelope.ProtocolYutongMQTT, VIN: "VIN002", Fields: map[string]any{ envelope.FieldSOCPercent: 76, "gear": 3, }, }, map[string]any{ "endpoint": "yutong", "topic": "/ytforward/shln/3", "data": map[string]any{ "ACC_PEDAL_APT": 14, "BATTERY_CAPACITY_SOC": 78.4, "CURRENT_OF_FC": 54.9, "HYDROGEN_LOW_PRESSURE": 1.41, "TOTAL_MILEAGE": 119925000, "fuelCellCoolInTempt": 59, }, "root": map[string]any{ "device": "LMRKH9AC2R1004087", "version": "1.0", }, }) mqttValues := kvMap(mqttRows) if mqttValues["data/ACC_PEDAL_APT"] != "14" || mqttValues["data/CURRENT_OF_FC"] != "54.9" || mqttValues["data/HYDROGEN_LOW_PRESSURE"] != "1.41" || mqttValues["data/TOTAL_MILEAGE"] != "119925000" || mqttValues["data/fuelCellCoolInTempt"] != "59" { t.Fatalf("mqtt raw data kv missing: %#v", mqttValues) } if mqttValues["root/device"] != "LMRKH9AC2R1004087" || mqttValues["metadata/endpoint"] != "yutong" || mqttValues["metadata/topic"] != "/ytforward/shln/3" { t.Fatalf("mqtt metadata kv missing: %#v", mqttValues) } if mqttValues["data/soc_percent"] != "" || mqttValues["data/gear"] != "" { t.Fatalf("mqtt kv should not include standardized env.Fields-only values: %#v", mqttValues) } } func kvMap(rows []RealtimeKVField) map[string]string { out := map[string]string{} for _, row := range rows { out[row.Domain+"/"+row.Field] = row.Value } return out }