feat: build vehicle data platform and production pipeline
This commit is contained in:
@@ -5,6 +5,7 @@ import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
@@ -133,6 +134,50 @@ func TestRepositoryStoresFullParsedOnlyInRealtimeRawAndMergesGB32960Units(t *tes
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepositoryNormalizesFarFutureEventTime(t *testing.T) {
|
||||
repo, closeFn := newTestRepository(t)
|
||||
defer closeFn()
|
||||
ctx := context.Background()
|
||||
received := time.Date(2026, 7, 12, 9, 30, 0, 0, time.UTC).UnixMilli()
|
||||
futureEvent := time.Date(2026, 7, 13, 9, 30, 0, 0, time.UTC).UnixMilli()
|
||||
|
||||
if err := repo.Update(ctx, envelope.FrameEnvelope{
|
||||
Protocol: envelope.ProtocolJT808,
|
||||
MessageID: "0x0200",
|
||||
VIN: "VIN001",
|
||||
EventTimeMS: futureEvent,
|
||||
ReceivedAtMS: received,
|
||||
Parsed: map[string]any{"location": map[string]any{"longitude": 121.1}},
|
||||
ParsedFields: map[string]any{
|
||||
"jt808.location.longitude": 121.1,
|
||||
"jt808.location.latitude": 30.1,
|
||||
"jt808.location.total_mileage_km": 10241.2,
|
||||
},
|
||||
Fields: map[string]any{
|
||||
envelope.FieldLongitude: 121.1,
|
||||
envelope.FieldLatitude: 30.1,
|
||||
envelope.FieldTotalMileageKM: 10241.2,
|
||||
},
|
||||
}); err != nil {
|
||||
t.Fatalf("Update() error = %v", err)
|
||||
}
|
||||
|
||||
protocol, err := repo.GetProtocol(ctx, "VIN001", envelope.ProtocolJT808)
|
||||
if err != nil {
|
||||
t.Fatalf("GetProtocol() error = %v", err)
|
||||
}
|
||||
if protocol.EventTimeMS != received {
|
||||
t.Fatalf("protocol event time = %d, want received %d", protocol.EventTimeMS, received)
|
||||
}
|
||||
meta, err := repo.client.HGetAll(ctx, realtimeKVMetaKey(envelope.ProtocolJT808, "VIN001")).Result()
|
||||
if err != nil {
|
||||
t.Fatalf("HGetAll meta error = %v", err)
|
||||
}
|
||||
if meta["event_time_ms"] != strconv.FormatInt(received, 10) {
|
||||
t.Fatalf("rt-kv event_time_ms = %q, want received %d", meta["event_time_ms"], received)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepositoryMergesNestedGB32960MotorSlicesBySerialNo(t *testing.T) {
|
||||
repo, closeFn := newTestRepository(t)
|
||||
defer closeFn()
|
||||
@@ -337,6 +382,13 @@ func TestRepositoryWritesRealtimeKVHashes(t *testing.T) {
|
||||
}},
|
||||
},
|
||||
},
|
||||
ParsedFields: map[string]any{
|
||||
"gb32960.vehicle.type": "0x01",
|
||||
"gb32960.vehicle.soc_percent": 88.0,
|
||||
"gb32960.gd_fc_stack.type": "0x30",
|
||||
"gb32960.gd_fc_stack.stack_count": 1,
|
||||
"gb32960.gd_fc_stack.stack_water_outlet_temp_c": 63,
|
||||
},
|
||||
}); err != nil {
|
||||
t.Fatalf("Update() error = %v", err)
|
||||
}
|
||||
@@ -358,6 +410,13 @@ func TestRepositoryWritesRealtimeKVHashes(t *testing.T) {
|
||||
if typeKV["gb32960.vehicle.soc_percent"] != "number" || typeKV["gb32960.gd_fc_stack.type"] != "string" {
|
||||
t.Fatalf("types kv = %#v", typeKV)
|
||||
}
|
||||
timeKV, err := repo.client.HGetAll(ctx, realtimeKVTimesKey(envelope.ProtocolGB32960, "VIN001")).Result()
|
||||
if err != nil {
|
||||
t.Fatalf("times kv HGetAll error = %v", err)
|
||||
}
|
||||
if timeKV["gb32960.vehicle.soc_percent"] != "1000" || timeKV["gb32960.gd_fc_stack.stack_water_outlet_temp_c"] != "1000" {
|
||||
t.Fatalf("times kv = %#v", timeKV)
|
||||
}
|
||||
metaKV, err := repo.client.HGetAll(ctx, "vehicle:rt-kv:GB32960:VIN001:meta").Result()
|
||||
if err != nil {
|
||||
t.Fatalf("meta kv HGetAll error = %v", err)
|
||||
@@ -388,6 +447,10 @@ func TestRepositoryFastUpdateOnlyWritesPermanentKVAndMinuteOnline(t *testing.T)
|
||||
map[string]any{"type": "0x01", "name": "vehicle", "value": map[string]any{"soc_percent": 88.0}},
|
||||
},
|
||||
},
|
||||
ParsedFields: map[string]any{
|
||||
"gb32960.vehicle.type": "0x01",
|
||||
"gb32960.vehicle.soc_percent": 88.0,
|
||||
},
|
||||
}); err != nil {
|
||||
t.Fatalf("FastUpdate() error = %v", err)
|
||||
}
|
||||
@@ -406,9 +469,19 @@ func TestRepositoryFastUpdateOnlyWritesPermanentKVAndMinuteOnline(t *testing.T)
|
||||
if typeKV["gb32960.vehicle.soc_percent"] != "number" {
|
||||
t.Fatalf("types kv = %#v", typeKV)
|
||||
}
|
||||
timeKV, err := repo.client.HGetAll(ctx, realtimeKVTimesKey(envelope.ProtocolGB32960, "VIN001")).Result()
|
||||
if err != nil {
|
||||
t.Fatalf("times kv HGetAll error = %v", err)
|
||||
}
|
||||
if timeKV["gb32960.vehicle.soc_percent"] != "1000" {
|
||||
t.Fatalf("times kv = %#v", timeKV)
|
||||
}
|
||||
if ttl := repo.client.TTL(ctx, "vehicle:rt-kv:GB32960:VIN001:values").Val(); ttl != -1 {
|
||||
t.Fatalf("kv ttl should not expire, got %v", ttl)
|
||||
}
|
||||
if ttl := repo.client.TTL(ctx, realtimeKVTimesKey(envelope.ProtocolGB32960, "VIN001")).Val(); ttl != -1 {
|
||||
t.Fatalf("kv times ttl should not expire, got %v", ttl)
|
||||
}
|
||||
if ttl := repo.client.TTL(ctx, "vehicle:online:GB32960:VIN001").Val(); ttl <= 0 || ttl > time.Minute {
|
||||
t.Fatalf("online ttl = %v, want within 1 minute", ttl)
|
||||
}
|
||||
@@ -430,12 +503,138 @@ func TestRepositoryFastUpdateOnlyWritesPermanentKVAndMinuteOnline(t *testing.T)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepositoryFastUpdateDoesNotLetOlderFramesOverwriteNewerFields(t *testing.T) {
|
||||
repo, closeFn := newTestRepository(t)
|
||||
defer closeFn()
|
||||
ctx := context.Background()
|
||||
|
||||
firstResult, err := repo.FastUpdateWithResult(ctx, envelope.FrameEnvelope{
|
||||
Protocol: envelope.ProtocolJT808,
|
||||
MessageID: "0x0200",
|
||||
VIN: "VIN001",
|
||||
EventTimeMS: 2000,
|
||||
ReceivedAtMS: 2100,
|
||||
ParsedFields: map[string]any{
|
||||
"jt808.location.speed_kmh": 50,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("newer FastUpdate() error = %v", err)
|
||||
}
|
||||
if firstResult.FieldsSeen != 1 || firstResult.FieldsWritten != 1 || firstResult.FieldsSkippedStale != 0 {
|
||||
t.Fatalf("newer result = %#v, want 1/1/0", firstResult)
|
||||
}
|
||||
if firstResult.EnvelopesSeen != 1 || firstResult.EnvelopesUpdated != 1 {
|
||||
t.Fatalf("newer envelope result = %#v, want seen=1 updated=1", firstResult)
|
||||
}
|
||||
secondResult, err := repo.FastUpdateWithResult(ctx, envelope.FrameEnvelope{
|
||||
Protocol: envelope.ProtocolJT808,
|
||||
MessageID: "0x0200",
|
||||
VIN: "VIN001",
|
||||
EventTimeMS: 1000,
|
||||
ReceivedAtMS: 2200,
|
||||
ParsedFields: map[string]any{
|
||||
"jt808.location.speed_kmh": 10,
|
||||
"jt808.location.latitude": 30.5,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("older FastUpdate() error = %v", err)
|
||||
}
|
||||
if secondResult.FieldsSeen != 2 || secondResult.FieldsWritten != 1 || secondResult.FieldsSkippedStale != 1 {
|
||||
t.Fatalf("older result = %#v, want 2/1/1", secondResult)
|
||||
}
|
||||
if secondResult.EnvelopesSeen != 1 || secondResult.EnvelopesUpdated != 1 {
|
||||
t.Fatalf("older envelope result = %#v, want seen=1 updated=1", secondResult)
|
||||
}
|
||||
|
||||
values, err := repo.client.HGetAll(ctx, realtimeKVValuesKey(envelope.ProtocolJT808, "VIN001")).Result()
|
||||
if err != nil {
|
||||
t.Fatalf("values HGetAll error = %v", err)
|
||||
}
|
||||
if values["jt808.location.speed_kmh"] != "50" {
|
||||
t.Fatalf("older frame overwrote speed: %#v", values)
|
||||
}
|
||||
if values["jt808.location.latitude"] != "30.5" {
|
||||
t.Fatalf("older frame should still add previously unseen latitude: %#v", values)
|
||||
}
|
||||
times, err := repo.client.HGetAll(ctx, realtimeKVTimesKey(envelope.ProtocolJT808, "VIN001")).Result()
|
||||
if err != nil {
|
||||
t.Fatalf("times HGetAll error = %v", err)
|
||||
}
|
||||
if times["jt808.location.speed_kmh"] != "2000" || times["jt808.location.latitude"] != "1000" {
|
||||
t.Fatalf("field times = %#v", times)
|
||||
}
|
||||
meta, err := repo.client.HGetAll(ctx, realtimeKVMetaKey(envelope.ProtocolJT808, "VIN001")).Result()
|
||||
if err != nil {
|
||||
t.Fatalf("meta HGetAll error = %v", err)
|
||||
}
|
||||
if meta["event_time_ms"] != "2000" {
|
||||
t.Fatalf("meta should keep latest frame time: %#v", meta)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepositoryFastUpdateDoesNotLetOlderReceivedFrameOverwriteOnlineState(t *testing.T) {
|
||||
repo, closeFn := newTestRepository(t)
|
||||
defer closeFn()
|
||||
ctx := context.Background()
|
||||
|
||||
if err := repo.FastUpdate(ctx, envelope.FrameEnvelope{
|
||||
Protocol: envelope.ProtocolJT808,
|
||||
MessageID: "0x0200",
|
||||
VIN: "VIN001",
|
||||
SourceEndpoint: "newer.example:808",
|
||||
EventTimeMS: 2000,
|
||||
ReceivedAtMS: 5000,
|
||||
ParsedFields: map[string]any{
|
||||
"jt808.location.speed_kmh": 50,
|
||||
},
|
||||
}); err != nil {
|
||||
t.Fatalf("newer FastUpdate() error = %v", err)
|
||||
}
|
||||
if err := repo.FastUpdate(ctx, envelope.FrameEnvelope{
|
||||
Protocol: envelope.ProtocolJT808,
|
||||
MessageID: "0x0200",
|
||||
VIN: "VIN001",
|
||||
SourceEndpoint: "older.example:808",
|
||||
EventTimeMS: 1000,
|
||||
ReceivedAtMS: 4000,
|
||||
ParsedFields: map[string]any{
|
||||
"jt808.location.latitude": 30.5,
|
||||
},
|
||||
}); err != nil {
|
||||
t.Fatalf("older FastUpdate() error = %v", err)
|
||||
}
|
||||
|
||||
state, err := repo.client.HGetAll(ctx, "vehicle:online-state:JT808:VIN001").Result()
|
||||
if err != nil {
|
||||
t.Fatalf("online state HGetAll error = %v", err)
|
||||
}
|
||||
if state["last_seen_ms"] != "5000" || state["offline_after_ms"] != "65000" || state["source_endpoint"] != "newer.example:808" {
|
||||
t.Fatalf("older received frame should not overwrite online state: %#v", state)
|
||||
}
|
||||
score, err := repo.client.ZScore(ctx, "vehicle:last_seen", "JT808:VIN001").Result()
|
||||
if err != nil {
|
||||
t.Fatalf("last_seen ZScore error = %v", err)
|
||||
}
|
||||
if score != 5000 {
|
||||
t.Fatalf("last_seen score = %v, want 5000", score)
|
||||
}
|
||||
status, err := repo.IsOnline(ctx, "VIN001")
|
||||
if err != nil {
|
||||
t.Fatalf("IsOnline() error = %v", err)
|
||||
}
|
||||
if status.LastSeenMS != 5000 || status.SourceEndpoint != "newer.example:808" {
|
||||
t.Fatalf("online status should keep newer received frame: %#v", status)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepositoryFastUpdateBatchWritesMultipleRealtimeProjections(t *testing.T) {
|
||||
repo, closeFn := newTestRepository(t)
|
||||
defer closeFn()
|
||||
ctx := context.Background()
|
||||
|
||||
err := repo.FastUpdateBatch(ctx, []envelope.FrameEnvelope{
|
||||
result, err := repo.FastUpdateBatchWithResult(ctx, []envelope.FrameEnvelope{
|
||||
{
|
||||
Protocol: envelope.ProtocolGB32960,
|
||||
VIN: "VIN001",
|
||||
@@ -444,6 +643,10 @@ func TestRepositoryFastUpdateBatchWritesMultipleRealtimeProjections(t *testing.T
|
||||
Parsed: map[string]any{
|
||||
"data_units": []any{map[string]any{"type": "0x01", "name": "vehicle", "value": map[string]any{"soc_percent": 88.0}}},
|
||||
},
|
||||
ParsedFields: map[string]any{
|
||||
"gb32960.vehicle.type": "0x01",
|
||||
"gb32960.vehicle.soc_percent": 88.0,
|
||||
},
|
||||
},
|
||||
{
|
||||
Protocol: envelope.ProtocolJT808,
|
||||
@@ -452,12 +655,17 @@ func TestRepositoryFastUpdateBatchWritesMultipleRealtimeProjections(t *testing.T
|
||||
ReceivedAtMS: 2200,
|
||||
MessageID: "0x0200",
|
||||
Parsed: map[string]any{"location": map[string]any{"longitude": 121.1, "latitude": 30.2}},
|
||||
ParsedFields: map[string]any{"jt808.location.longitude": 121.1, "jt808.location.latitude": 30.2},
|
||||
Fields: map[string]any{envelope.FieldLongitude: 121.1, envelope.FieldLatitude: 30.2},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("FastUpdateBatch() error = %v", err)
|
||||
}
|
||||
if result.FieldsSeen != 4 || result.FieldsWritten != 4 || result.FieldsSkippedStale != 0 ||
|
||||
result.EnvelopesSeen != 2 || result.EnvelopesUpdated != 2 {
|
||||
t.Fatalf("FastUpdateBatchWithResult() result = %#v, want fields 4/4/0 and envelopes 2/2", result)
|
||||
}
|
||||
|
||||
gbValues, err := repo.client.HGetAll(ctx, "vehicle:rt-kv:GB32960:VIN001:values").Result()
|
||||
if err != nil {
|
||||
@@ -481,6 +689,55 @@ func TestRepositoryFastUpdateBatchWritesMultipleRealtimeProjections(t *testing.T
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepositoryFastUpdateReportsSkippedEnvelopesWithoutWritingRedis(t *testing.T) {
|
||||
repo, closeFn := newTestRepository(t)
|
||||
defer closeFn()
|
||||
ctx := context.Background()
|
||||
|
||||
result, err := repo.FastUpdateBatchWithResult(ctx, []envelope.FrameEnvelope{
|
||||
{
|
||||
Protocol: envelope.ProtocolGB32960,
|
||||
MessageID: "0x01",
|
||||
VIN: "VIN001",
|
||||
EventTimeMS: 1000,
|
||||
ReceivedAtMS: 1000,
|
||||
Parsed: map[string]any{"platform_login": map[string]any{"username": "Hyundai"}},
|
||||
},
|
||||
{
|
||||
Protocol: envelope.ProtocolJT808,
|
||||
MessageID: "0x0200",
|
||||
EventTimeMS: 2000,
|
||||
ReceivedAtMS: 2000,
|
||||
ParsedFields: map[string]any{"jt808.location.speed_kmh": 30},
|
||||
},
|
||||
{
|
||||
Protocol: envelope.ProtocolJT808,
|
||||
MessageID: "0x0200",
|
||||
VIN: "VIN002",
|
||||
EventTimeMS: 3000,
|
||||
ReceivedAtMS: 3000,
|
||||
Parsed: map[string]any{"location": map[string]any{"speed_kmh": 30}},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("FastUpdateBatchWithResult() error = %v", err)
|
||||
}
|
||||
if result.EnvelopesSeen != 3 || result.EnvelopesUpdated != 0 ||
|
||||
result.EnvelopesSkippedNonRealtime != 1 || result.EnvelopesSkippedMissingVIN != 1 ||
|
||||
result.EnvelopesSkippedMissingFields != 1 {
|
||||
t.Fatalf("result = %#v, want non-realtime, missing vin and missing parsed fields skips", result)
|
||||
}
|
||||
if result.FieldsSeen != 0 || result.FieldsWritten != 0 || result.FieldsSkippedStale != 0 {
|
||||
t.Fatalf("field result = %#v, want no field writes", result)
|
||||
}
|
||||
if repo.client.Exists(ctx, "vehicle:rt-kv:GB32960:VIN001:values").Val() != 0 {
|
||||
t.Fatal("non-realtime frame should not write realtime kv")
|
||||
}
|
||||
if repo.client.Exists(ctx, "vehicle:online:JT808:VIN002").Val() != 0 {
|
||||
t.Fatal("missing parsed fields frame should not mark vehicle online")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepositoryListsOnlineStatusesAndPipelineSummary(t *testing.T) {
|
||||
repo, closeFn := newTestRepository(t)
|
||||
defer closeFn()
|
||||
@@ -495,6 +752,7 @@ func TestRepositoryListsOnlineStatusesAndPipelineSummary(t *testing.T) {
|
||||
Parsed: map[string]any{
|
||||
"data_units": []any{map[string]any{"type": "0x01", "name": "vehicle", "value": map[string]any{"soc_percent": 88.0}}},
|
||||
},
|
||||
ParsedFields: map[string]any{"gb32960.vehicle.soc_percent": 88.0},
|
||||
},
|
||||
{
|
||||
Protocol: envelope.ProtocolJT808,
|
||||
@@ -503,6 +761,7 @@ func TestRepositoryListsOnlineStatusesAndPipelineSummary(t *testing.T) {
|
||||
ReceivedAtMS: 2200,
|
||||
MessageID: "0x0200",
|
||||
Parsed: map[string]any{"location": map[string]any{"longitude": 121.1, "latitude": 30.2}},
|
||||
ParsedFields: map[string]any{"jt808.location.longitude": 121.1, "jt808.location.latitude": 30.2},
|
||||
Fields: map[string]any{envelope.FieldLongitude: 121.1, envelope.FieldLatitude: 30.2},
|
||||
},
|
||||
} {
|
||||
@@ -548,6 +807,7 @@ func TestOnlineIndexHandlerReturnsPagedOnlineStatuses(t *testing.T) {
|
||||
Parsed: map[string]any{
|
||||
"data_units": []any{map[string]any{"type": "0x01", "name": "vehicle", "value": map[string]any{"soc_percent": 88.0}}},
|
||||
},
|
||||
ParsedFields: map[string]any{"gb32960.vehicle.soc_percent": 88.0},
|
||||
}); err != nil {
|
||||
t.Fatalf("FastUpdate() error = %v", err)
|
||||
}
|
||||
@@ -579,6 +839,7 @@ func TestPipelineDebugHandlerReturnsProtocolSummary(t *testing.T) {
|
||||
EventTimeMS: 3000,
|
||||
ReceivedAtMS: 3300,
|
||||
Parsed: map[string]any{"data": map[string]any{"TOTAL_MILEAGE": 1}},
|
||||
ParsedFields: map[string]any{"yutong_mqtt.data.total_mileage": 1},
|
||||
Fields: map[string]any{envelope.FieldTotalMileageKM: 1},
|
||||
}); err != nil {
|
||||
t.Fatalf("FastUpdate() error = %v", err)
|
||||
@@ -728,6 +989,82 @@ func TestRepositoryUsesEnvelopeParsedFieldsForRealtimeKV(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepositoryDropsNonPositiveTotalMileageFromRealtimeKV(t *testing.T) {
|
||||
repo, closeFn := newTestRepository(t)
|
||||
defer closeFn()
|
||||
ctx := context.Background()
|
||||
|
||||
if err := repo.Update(ctx, envelope.FrameEnvelope{
|
||||
Protocol: envelope.ProtocolJT808,
|
||||
VIN: "VIN001",
|
||||
EventTimeMS: 1000,
|
||||
ReceivedAtMS: 1100,
|
||||
MessageID: "0x0200",
|
||||
ParsedFields: map[string]any{
|
||||
"jt808.location.latitude": "31.259555",
|
||||
"jt808.location.longitude": "119.892413",
|
||||
"jt808.location.total_mileage_km": "0",
|
||||
},
|
||||
Fields: map[string]any{
|
||||
envelope.FieldLatitude: 31.259555,
|
||||
envelope.FieldLongitude: 119.892413,
|
||||
envelope.FieldTotalMileageKM: 0,
|
||||
},
|
||||
}); err != nil {
|
||||
t.Fatalf("Update() error = %v", err)
|
||||
}
|
||||
|
||||
values, err := repo.client.HGetAll(ctx, realtimeKVValuesKey(envelope.ProtocolJT808, "VIN001")).Result()
|
||||
if err != nil {
|
||||
t.Fatalf("HGetAll() error = %v", err)
|
||||
}
|
||||
if _, exists := values["jt808.location.total_mileage_km"]; exists {
|
||||
t.Fatalf("realtime kv should drop non-positive mileage: %#v", values)
|
||||
}
|
||||
if values["jt808.location.latitude"] != "31.259555" {
|
||||
t.Fatalf("realtime kv should keep valid fields: %#v", values)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepositoryDropsNonPositiveRawTotalMileageFromRealtimeKV(t *testing.T) {
|
||||
repo, closeFn := newTestRepository(t)
|
||||
defer closeFn()
|
||||
ctx := context.Background()
|
||||
|
||||
if err := repo.Update(ctx, envelope.FrameEnvelope{
|
||||
Protocol: envelope.ProtocolYutongMQTT,
|
||||
VIN: "LMRKH9AC3R1004101",
|
||||
EventTimeMS: 1000,
|
||||
ReceivedAtMS: 1100,
|
||||
MessageID: "MQTT",
|
||||
Parsed: map[string]any{
|
||||
"data": map[string]any{
|
||||
"LATITUDE": 30.590921,
|
||||
"LONGITUDE": 121.075044,
|
||||
"TOTAL_MILEAGE": 0,
|
||||
},
|
||||
},
|
||||
ParsedFields: map[string]any{
|
||||
"yutong_mqtt.data.latitude": 30.590921,
|
||||
"yutong_mqtt.data.longitude": 121.075044,
|
||||
"yutong_mqtt.data.total_mileage": 0,
|
||||
},
|
||||
}); err != nil {
|
||||
t.Fatalf("Update() error = %v", err)
|
||||
}
|
||||
|
||||
values, err := repo.client.HGetAll(ctx, realtimeKVValuesKey(envelope.ProtocolYutongMQTT, "LMRKH9AC3R1004101")).Result()
|
||||
if err != nil {
|
||||
t.Fatalf("HGetAll() error = %v", err)
|
||||
}
|
||||
if _, exists := values["yutong_mqtt.data.total_mileage"]; exists {
|
||||
t.Fatalf("realtime kv should drop non-positive raw mileage: %#v", values)
|
||||
}
|
||||
if values["yutong_mqtt.data.latitude"] != "30.590921" {
|
||||
t.Fatalf("realtime kv should keep valid fields: %#v", values)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepositorySkipsFramesWithoutVIN(t *testing.T) {
|
||||
repo, closeFn := newTestRepository(t)
|
||||
defer closeFn()
|
||||
|
||||
Reference in New Issue
Block a user