- Remove duplicated Garmin fields from storage; decode display-only fields (activity name/type, lap duration/HR, structured workout raw JSON) from RawJSON at API-response time instead of storing redundant columns - Add a fully configurable chart color system (pace/HR main-line colors, 4 effort-kind colors, tint/darken/brighten intensity knobs) under Profile > Chart colors - Rename training types and fix their display order (Easy, Long, 60'/30' Threshold, Tempo, Intervals, MAS Test, Race) everywhere they're listed - Add an Efficiency Factor progression metric; fix Progression chart axes to use tight non-zero-based domains, m:ss/km pace formatting, and rounded ticks instead of raw floating-point labels - Expose the raw get_workout_by_id() payload in the raw-data viewer alongside activity/lap/detail JSON; enlarge the modal and shrink array indentation for readability - Fix "last sync" reporting a meaningless activity count: record one combined sync run per manual "Sync now" and count genuinely new activities instead of re-listing whatever Garmin returned for the queried window - Let a Review Queue activity be manually cleared back to Unclassified, and make "Reset all" available even while disconnected from Garmin Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
72 lines
2.3 KiB
Go
72 lines
2.3 KiB
Go
package store
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
)
|
|
|
|
func TestResetAllSyncedData_DeletesActivitiesCascadesAndRewindsWatermark(t *testing.T) {
|
|
db := openTestDB(t)
|
|
ctx := context.Background()
|
|
|
|
activityID, err := db.UpsertActivity(ctx, Activity{GarminActivityID: 1, StartTimeUTC: "2026-07-11 06:00:00", RawJSON: "{}"})
|
|
if err != nil {
|
|
t.Fatalf("UpsertActivity: %v", err)
|
|
}
|
|
kindID, err := db.CreateWorkoutKind(ctx, WorkoutKind{Name: "Test Reset Kind", RuleJSON: `{"match":"all","conditions":[]}`, IsActive: true})
|
|
if err != nil {
|
|
t.Fatalf("CreateWorkoutKind: %v", err)
|
|
}
|
|
if err := db.ReplaceLaps(ctx, activityID, []Lap{{LapIndex: 1}}); err != nil {
|
|
t.Fatalf("ReplaceLaps: %v", err)
|
|
}
|
|
if _, err := db.InsertKindAssignment(ctx, KindAssignment{
|
|
ActivityID: activityID, WorkoutKindID: &kindID, AssignmentSource: AssignmentSourceRuleEngine,
|
|
Status: AssignmentStatusAssigned, CandidateKindsJSON: "[]",
|
|
}); err != nil {
|
|
t.Fatalf("InsertKindAssignment: %v", err)
|
|
}
|
|
if err := db.UpdateSyncState(ctx, "2020-01-01", true); err != nil {
|
|
t.Fatalf("UpdateSyncState: %v", err)
|
|
}
|
|
|
|
if err := db.ResetAllSyncedData(ctx); err != nil {
|
|
t.Fatalf("ResetAllSyncedData: %v", err)
|
|
}
|
|
|
|
activities, err := db.ListActivities(ctx, ActivityFilter{})
|
|
if err != nil {
|
|
t.Fatalf("ListActivities: %v", err)
|
|
}
|
|
if len(activities) != 0 {
|
|
t.Errorf("expected 0 activities after reset, got %d", len(activities))
|
|
}
|
|
laps, err := db.LapsForActivity(ctx, activityID)
|
|
if err != nil {
|
|
t.Fatalf("LapsForActivity: %v", err)
|
|
}
|
|
if len(laps) != 0 {
|
|
t.Errorf("expected laps to cascade-delete, got %d", len(laps))
|
|
}
|
|
if _, ok, err := db.CurrentAssignment(ctx, activityID); err != nil || ok {
|
|
t.Errorf("expected kind assignment to cascade-delete, ok=%v err=%v", ok, err)
|
|
}
|
|
|
|
state, err := db.GetSyncState(ctx)
|
|
if err != nil {
|
|
t.Fatalf("GetSyncState: %v", err)
|
|
}
|
|
if state.EarliestSyncedDate != nil || state.BackfillComplete {
|
|
t.Errorf("expected watermark rewound to fresh state, got %+v", state)
|
|
}
|
|
|
|
// Workout kinds (the user's taxonomy) must survive a reset.
|
|
kind, ok, err := db.GetWorkoutKind(ctx, kindID)
|
|
if err != nil || !ok {
|
|
t.Fatalf("GetWorkoutKind after reset: ok=%v err=%v", ok, err)
|
|
}
|
|
if kind.Name != "Test Reset Kind" {
|
|
t.Errorf("workout kind was unexpectedly affected by reset: %+v", kind)
|
|
}
|
|
}
|