fix(stats): recover durable daily mileage projections

This commit is contained in:
lingniu
2026-07-19 21:00:56 +08:00
parent 2b18a400b1
commit 5539c5e0c2
6 changed files with 174 additions and 0 deletions

View File

@@ -888,6 +888,51 @@ func TestWriterPendingProjectionFailureDoesNotBlockOtherVehicles(t *testing.T) {
}
}
func TestWriterReconcilesDurableProjectionAfterPendingQueueIsLost(t *testing.T) {
db, mock, err := sqlmock.New(sqlmock.QueryMatcherOption(sqlmock.QueryMatcherEqual))
if err != nil {
t.Fatalf("sqlmock.New() error = %v", err)
}
defer db.Close()
writer := NewWriter(db, time.FixedZone("Asia/Shanghai", 8*3600))
vin := "LMRKH9AC7R1004120"
statDate := "2026-07-19"
protocol := envelope.ProtocolJT808
mock.ExpectQuery(staleDailyMileageProjectionTargetsSQL).
WithArgs(statDate).
WillReturnRows(sqlmock.NewRows([]string{"vin", "protocol"}).AddRow(vin, string(protocol)))
expectProjectDailyMileageSQL(mock, vin, statDate, protocol)
mock.ExpectCommit()
result, err := writer.ReconcileDateProjections(context.Background(), statDate)
if err != nil {
t.Fatalf("ReconcileDateProjections() error = %v", err)
}
if result.Targets != 1 || result.Attempted != 1 || result.Written != 1 || result.Failed != 0 {
t.Fatalf("reconcile result = %+v, want one recovered projection", result)
}
if got := writer.CacheStats().PendingProjectionEntries; got != 0 {
t.Fatalf("startup reconciliation should not depend on pending memory, got %d entries", got)
}
if err := mock.ExpectationsWereMet(); err != nil {
t.Fatalf("sql expectations: %v", err)
}
}
func TestWriterProjectionReconciliationRequiresQueryableStore(t *testing.T) {
writer := NewWriter(&recordingExec{}, time.FixedZone("Asia/Shanghai", 8*3600))
result, err := writer.ReconcileDateProjections(context.Background(), "2026-07-19")
if err == nil || !strings.Contains(err.Error(), "query support") {
t.Fatalf("ReconcileDateProjections() error = %v, want query support failure", err)
}
if result != (ProjectionReconcileResult{}) {
t.Fatalf("reconcile result = %+v, want empty", result)
}
}
func TestWriterAppendProjectsImmediatelyForNewSource(t *testing.T) {
exec := &recordingExec{}
loc := time.FixedZone("Asia/Shanghai", 8*3600)