feat(platform): harden telemetry pipeline and unify Semi UI workspaces
This commit is contained in:
@@ -1091,6 +1091,91 @@ func TestWriterRetriesExpiredMissingPreviousDayBaseline(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestWriterRefreshesExpiredFoundPreviousDayBaseline(t *testing.T) {
|
||||
db, mock, err := sqlmock.New()
|
||||
if err != nil {
|
||||
t.Fatalf("sqlmock.New() error = %v", err)
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
loc := time.FixedZone("Asia/Shanghai", 8*3600)
|
||||
writer := NewWriter(db, loc)
|
||||
writer.SetBaselineHitTTL(time.Minute)
|
||||
candidate := SourceMileageSample{
|
||||
VIN: "LMRKH9AC6R1004111",
|
||||
StatDate: "2026-07-17",
|
||||
Protocol: envelope.ProtocolYutongMQTT,
|
||||
SourceKey: "YUTONG_MQTT:LMRKH9AC6R1004111@PLATFORM:yutong",
|
||||
}
|
||||
cacheKey := mileageCacheKey(MetricSample{
|
||||
VIN: candidate.VIN,
|
||||
Protocol: candidate.Protocol,
|
||||
StatDate: candidate.StatDate,
|
||||
SourceKey: candidate.SourceKey,
|
||||
})
|
||||
staleTime := time.Date(2026, 7, 12, 4, 12, 20, 0, loc)
|
||||
writer.baselineCache[cacheKey] = sourceBaselineCacheEntry{
|
||||
baseline: sourceBaseline{LatestTotalKM: 90778, LatestEventTime: staleTime},
|
||||
found: true,
|
||||
cachedAt: time.Now().Add(-2 * time.Minute),
|
||||
}
|
||||
|
||||
refreshedTime := time.Date(2026, 7, 16, 23, 59, 59, 0, loc)
|
||||
mock.ExpectQuery(`SELECT latest_total_mileage_km, latest_event_time FROM vehicle_daily_mileage_source`).
|
||||
WithArgs(candidate.VIN, candidate.StatDate, "YUTONG_MQTT", candidate.SourceKey).
|
||||
WillReturnRows(sqlmock.NewRows([]string{"latest_total_mileage_km", "latest_event_time"}).
|
||||
AddRow(91302.0, refreshedTime))
|
||||
|
||||
baseline, found, err := writer.previousBaseline(context.Background(), candidate)
|
||||
if err != nil || !found {
|
||||
t.Fatalf("previousBaseline() found=%v error=%v, want refreshed baseline", found, err)
|
||||
}
|
||||
if baseline.LatestTotalKM != 91302 || !baseline.LatestEventTime.Equal(refreshedTime) {
|
||||
t.Fatalf("baseline = %+v, want nearest refreshed baseline", baseline)
|
||||
}
|
||||
if err := mock.ExpectationsWereMet(); err != nil {
|
||||
t.Fatalf("sql expectations: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWriterMarkBaselineKeepsCacheAgeForUnchangedBoundary(t *testing.T) {
|
||||
db, _, err := sqlmock.New()
|
||||
if err != nil {
|
||||
t.Fatalf("sqlmock.New() error = %v", err)
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
loc := time.FixedZone("Asia/Shanghai", 8*3600)
|
||||
writer := NewWriter(db, loc)
|
||||
candidate := SourceMileageSample{
|
||||
VIN: "LMRKH9AC6R1004111",
|
||||
StatDate: "2026-07-17",
|
||||
Protocol: envelope.ProtocolYutongMQTT,
|
||||
SourceKey: "YUTONG_MQTT:LMRKH9AC6R1004111@PLATFORM:yutong",
|
||||
FirstTotalKM: 91302,
|
||||
FirstEventTime: time.Date(2026, 7, 16, 23, 59, 59, 0, loc),
|
||||
}
|
||||
sample := MetricSample{
|
||||
VIN: candidate.VIN,
|
||||
Protocol: candidate.Protocol,
|
||||
StatDate: candidate.StatDate,
|
||||
SourceKey: candidate.SourceKey,
|
||||
}
|
||||
cacheKey := mileageCacheKey(sample)
|
||||
cachedAt := time.Now().Add(-4 * time.Minute)
|
||||
writer.baselineCache[cacheKey] = sourceBaselineCacheEntry{
|
||||
baseline: sourceBaseline{LatestTotalKM: candidate.FirstTotalKM, LatestEventTime: candidate.FirstEventTime},
|
||||
found: true,
|
||||
cachedAt: cachedAt,
|
||||
}
|
||||
|
||||
writer.markBaselineWritten(sample, candidate)
|
||||
|
||||
if got := writer.baselineCache[cacheKey].cachedAt; !got.Equal(cachedAt) {
|
||||
t.Fatalf("cachedAt = %s, want unchanged %s", got, cachedAt)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWriterAppendUsesPreviousSourceBaselineForRealtimeCandidate(t *testing.T) {
|
||||
db, mock, err := sqlmock.New()
|
||||
if err != nil {
|
||||
|
||||
Reference in New Issue
Block a user