fix(mileage): recover sparse source daily distance

This commit is contained in:
lingniu
2026-07-19 22:57:31 +08:00
parent e45daff528
commit 4c64c4c738
11 changed files with 364 additions and 80 deletions

View File

@@ -87,6 +87,70 @@ func TestGPSMileagePointFromEnvelopeUsesNaturalDayAndStableSource(t *testing.T)
}
}
func TestGPSMileagePointFromYutongEnvelopeRequiresMovement(t *testing.T) {
loc := time.FixedZone("Asia/Shanghai", 8*3600)
eventTime := time.Date(2026, 7, 19, 8, 0, 0, 0, loc)
env := envelope.FrameEnvelope{
EventID: "evt-yutong-gps",
Protocol: envelope.ProtocolYutongMQTT,
VIN: "LMRKH9AC9R1004099",
DeviceID: "LMRKH9AC9R1004099",
SourceEndpoint: "mqtt://yutong/ytforward/shln/2",
EventTimeMS: eventTime.UnixMilli(),
Fields: map[string]any{
"yutong_mqtt.data.longitude": 121.4737,
"yutong_mqtt.data.latitude": 31.2304,
"yutong_mqtt.data.meter_speed": 18,
},
}
identity := SourceIdentity{
Protocol: envelope.ProtocolYutongMQTT,
SourceIP: "mqtt",
SourceCode: "yutong",
SourceKind: "PLATFORM",
PlatformName: "宇通",
}
point, ok := GPSMileagePointFromEnvelope(env, identity, loc)
if !ok {
t.Fatal("moving Yutong location must be eligible for GPS mileage fallback")
}
if point.Protocol != envelope.ProtocolYutongMQTT || point.SourceKey != "YUTONG_MQTT:LMRKH9AC9R1004099@PLATFORM:yutong" {
t.Fatalf("point = %+v", point)
}
env.Fields["yutong_mqtt.data.meter_speed"] = 0
if _, ok := GPSMileagePointFromEnvelope(env, identity, loc); ok {
t.Fatal("stationary Yutong jitter must not enter GPS mileage fallback")
}
}
func TestWriterThrottlesHighFrequencyGPSMileageStateWrites(t *testing.T) {
writer := NewWriter(&recordingExec{}, time.FixedZone("Asia/Shanghai", 8*3600))
writer.SetGPSAccumulationInterval(10 * time.Second)
start := time.Date(2026, 7, 19, 8, 0, 0, 0, time.FixedZone("Asia/Shanghai", 8*3600))
point := GPSMileagePoint{
VIN: "LMRKH9AC9R1004099",
StatDate: "2026-07-19",
Protocol: envelope.ProtocolYutongMQTT,
SourceKey: "YUTONG_MQTT:LMRKH9AC9R1004099@PLATFORM:yutong",
EventTime: start,
}
if !writer.shouldAccumulateGPS(point) {
t.Fatal("first point must be accepted")
}
writer.markGPSAccumulated(point)
point.EventTime = start.Add(5 * time.Second)
if writer.shouldAccumulateGPS(point) {
t.Fatal("high-frequency point inside the 10s interval must be throttled")
}
point.EventTime = start.Add(11 * time.Second)
if !writer.shouldAccumulateGPS(point) {
t.Fatal("point after the 10s interval must be accepted")
}
if got := writer.CacheStats().GPSAccumulationEntries; got != 1 {
t.Fatalf("GPS cache entries = %d, want 1", got)
}
}
func TestAccumulateJT808GPSMileagePersistsEstimateAndProjects(t *testing.T) {
db, mock, err := sqlmock.New()
if err != nil {