Merge backfill into Sync now, replace Full backfill with a destructive Reset all

Sync now now runs Backfill (resumes from the watermark, so a widened history
horizon is picked up automatically) then IncrementalSync then detail-fill, in
one click. "Full backfill" is gone -- in its place, "Reset all" (styled as a
destructive action, gated by a confirm dialog) wipes every synced activity
and its laps/kind assignments and rewinds the backfill watermark, so the next
sync performs a genuinely fresh pull instead of trying to patch up existing
rows with newer schema fields.
This commit is contained in:
2026-07-19 12:41:38 +02:00
parent af41aa0f7f
commit 7f7e4b10f0
10 changed files with 235 additions and 12 deletions

View File

@@ -427,6 +427,47 @@ func TestBackfill_SecondRunIsANoOpOnceHorizonFullyCovered(t *testing.T) {
}
}
func TestResetAll_AllowsFreshBackfillAfterwards(t *testing.T) {
db := openTestDB(t)
ctx := context.Background()
m := &mock.Client{Activities: []garmin.Activity{
{ActivityID: 1, ActivityType: garmin.ActivityType{TypeKey: "running"}, StartTimeGMT: "2026-07-05 06:00:00", Distance: 5000, Duration: 1500},
}}
svc := NewService(m, db, Config{BackfillHorizonDays: 10, BackfillWindowDays: 10},
fixedNow(time.Date(2026, 7, 11, 0, 0, 0, 0, time.UTC)))
if err := svc.Backfill(ctx); err != nil {
t.Fatalf("first Backfill: %v", err)
}
firstCallCount := m.GetActivitiesCalls
if err := svc.ResetAll(ctx); err != nil {
t.Fatalf("ResetAll: %v", err)
}
activities, err := db.ListActivities(ctx, store.ActivityFilter{})
if err != nil {
t.Fatalf("ListActivities: %v", err)
}
if len(activities) != 0 {
t.Fatalf("expected 0 activities after ResetAll, got %d", len(activities))
}
if err := svc.Backfill(ctx); err != nil {
t.Fatalf("Backfill after reset: %v", err)
}
if m.GetActivitiesCalls <= firstCallCount {
t.Errorf("expected Backfill after ResetAll to call GetActivities again (fresh pull), call count stayed at %d", m.GetActivitiesCalls)
}
activities, err = db.ListActivities(ctx, store.ActivityFilter{})
if err != nil {
t.Fatalf("ListActivities after re-backfill: %v", err)
}
if len(activities) != 1 {
t.Fatalf("expected 1 activity re-fetched after reset+backfill, got %d", len(activities))
}
}
func TestBackfill_ResumesFromWatermarkWhenHorizonGrows(t *testing.T) {
db := openTestDB(t)
ctx := context.Background()