refactor: merge internal/sync into internal/garmin, regroup api files and routes

Garmin auth/sync routes move under /api/garmin/*; sync.Service becomes
garmin.Sync with garmin.SyncConfig/ClientConfig; applog becomes
internal/log; the test mock moves into the garmin package as MockClient
(breaking the test-only import cycle the merge created); stale test
URLs and type names updated to match.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-04 16:04:18 +02:00
parent 19c9aecdeb
commit e2b2bf9611
61 changed files with 2007 additions and 982 deletions

View File

@@ -24,7 +24,7 @@ frontend `SyncModal` component polls it and replaces all inline sync UI in `Garm
- Frontend: `npm run build` (`tsc -b && vite build`) and `npm run lint` (oxlint) must be clean
before any commit. No frontend test suite exists -- verify new UI manually in a browser against
`cmd/seedsample` data.
- Reset all (`ResetAll`/`handleSyncReset`) is explicitly out of scope -- do not touch it beyond
- Reset all (`ResetAll`/`handleGarminSyncReset`) is explicitly out of scope -- do not touch it beyond
what's incidentally required (none is required).
- Follow this repo's existing JSON convention exactly: hand-built response maps use snake_case
keys (`in_progress`, `activities_pending_details`), but a Go struct serialized directly (like
@@ -40,7 +40,7 @@ frontend `SyncModal` component polls it and replaces all inline sync UI in `Garm
- Modify: `backend/internal/sync/service.go:99-123` (delete `Backfill`), `:194-209` (delete
`IncrementalSync`), `:224-232` (fix `FullSync`'s doc comment)
- Modify: `backend/internal/store/syncruns.go:9-11` (delete `SyncKindBackfill`/`SyncKindIncremental`)
- Modify: `backend/internal/sync/service_test.go` (rename/rewrite tests listed below)
- Modify: `../../../backend/internal/garmin/sync_test.go` (rename/rewrite tests listed below)
**Interfaces:**
- Consumes: nothing new.
@@ -58,7 +58,7 @@ check, not new information).
- [ ] **Step 2: Delete `Backfill` and `IncrementalSync`, fix `FullSync`'s doc comment**
In `backend/internal/sync/service.go`, delete this entire method (lines 99-123):
In `../../../backend/internal/garmin/sync.go`, delete this entire method (lines 99-123):
```go
// Backfill pages backward in Config.BackfillWindowDays windows until
@@ -187,7 +187,7 @@ const (
)
```
- [ ] **Step 4: Update `backend/internal/sync/service_test.go`'s callers**
- [ ] **Step 4: Update `../../../backend/internal/garmin/sync_test.go`'s callers**
Rename and rewrite (drop the redundant SyncRun assertion -- `TestFullSync_RecordsOneCombinedSyncRun`
already covers SyncRun recording thoroughly):
@@ -261,7 +261,7 @@ Expected: all packages pass, `gofmt -l .` prints nothing.
- [ ] **Step 6: Commit**
```bash
git add backend/internal/sync/service.go backend/internal/sync/service_test.go backend/internal/store/syncruns.go
git add backend/internal/sync/sync.go backend/internal/sync/sync_test.go backend/internal/store/syncruns.go
git commit -m "$(cat <<'EOF'
refactor(sync): remove dead Backfill/IncrementalSync exported wrappers
@@ -475,7 +475,7 @@ EOF
`fillActivityWorkout`)
- Modify: `backend/internal/sync/mapping.go:113-125` (`alignWorkoutTargets` signature)
- Modify: `backend/internal/garmin/mock/mock.go` (add `WorkoutErrByID`)
- Modify: `backend/internal/sync/service_test.go` (update `alignWorkoutTargets` call sites,
- Modify: `../../../backend/internal/garmin/sync_test.go` (update `alignWorkoutTargets` call sites,
rewrite `TestFillPendingDetails_ReportsLiveProgress`, add new tests)
**Interfaces:**
@@ -490,7 +490,7 @@ EOF
`alignWorkoutTargets` only ever uses `len(laps)`, never any lap's actual content -- change its
signature to take the count directly, since Task 3's `fillActivityWorkout` (added in Step 4)
needs to call it without holding a `[]garmin.Lap` (it only has `[]store.Lap` read back from the
DB). In `backend/internal/sync/service_test.go`, update all three call sites:
DB). In `../../../backend/internal/garmin/sync_test.go`, update all three call sites:
```go
targets := alignWorkoutTargets(len(laps), workout)
@@ -509,7 +509,7 @@ a `[]garmin.Lap`, not an `int` -- `len(laps)` is an `int`, mismatched argument t
- [ ] **Step 3: Change `alignWorkoutTargets`'s signature**
In `backend/internal/sync/mapping.go`, replace:
In `../../../backend/internal/garmin/mapping.go`, replace:
```go
func alignWorkoutTargets(laps []garmin.Lap, workout garmin.Workout) []*garmin.WorkoutStep {
@@ -553,7 +553,7 @@ one-extra-trailing-lap case stays accurate as-is.)
- [ ] **Step 4: Run to verify it passes**
Run: `cd backend && go test ./internal/sync/... -run TestAlignWorkoutTargets -v`
Expected: PASS (note: `fillActivityDetails` in `service.go` still calls
Expected: PASS (note: `fillActivityDetails` in `sync.go` still calls
`alignWorkoutTargets(splits.Laps, workout)` at this point -- that call site is rewritten in Step 6
below, so the package won't fully build until then; run this test with `-run
TestAlignWorkoutTargets` specifically, or expect a build failure from the not-yet-updated call
@@ -561,7 +561,7 @@ site if running the whole package).
- [ ] **Step 5: Add phase-aware `Progress` and update `FullSync`**
In `backend/internal/sync/service.go`, replace:
In `../../../backend/internal/garmin/sync.go`, replace:
```go
// Progress reports how far a currently-running (or just-finished)
@@ -843,7 +843,7 @@ func (c *Client) GetWorkoutByID(ctx context.Context, workoutID int64) (garmin.Wo
- [ ] **Step 8: Rewrite `TestFillPendingDetails_ReportsLiveProgress` for phase-awareness, add two new tests**
Replace `TestFillPendingDetails_ReportsLiveProgress` in `backend/internal/sync/service_test.go`
Replace `TestFillPendingDetails_ReportsLiveProgress` in `../../../backend/internal/garmin/sync_test.go`
with:
```go
@@ -1045,9 +1045,9 @@ func TestFillPendingDetails_OneWorkoutFetchFailureDoesNotAbortTheWorkoutsPass(t
}
```
`fmt` is already imported in `service_test.go`'s package (`package sync` imports it transitively
`fmt` is already imported in `sync_test.go`'s package (`package sync` imports it transitively
via other test helpers)? Check: if `go vet`/`go build` complains `fmt` is undefined in
`service_test.go`, add `"fmt"` to that file's import block.
`sync_test.go`, add `"fmt"` to that file's import block.
- [ ] **Step 9: Run the full `internal/sync` test suite**
@@ -1063,7 +1063,7 @@ Expected: all pass, `gofmt -l .` prints nothing.
- [ ] **Step 11: Commit**
```bash
git add backend/internal/sync/service.go backend/internal/sync/service_test.go backend/internal/sync/mapping.go backend/internal/garmin/mock/mock.go
git add backend/internal/sync/sync.go backend/internal/sync/sync_test.go backend/internal/sync/mapping.go backend/internal/garmin/mock/mock.go
git commit -m "$(cat <<'EOF'
feat(sync): split FillPendingDetails into activities/workouts phases
@@ -1095,7 +1095,7 @@ EOF
### Task 4: Reshape `GET /api/sync/status` and update frontend types
**Files:**
- Modify: `backend/internal/api/sync.go:69-102` (`handleSyncStatus`)
- Modify: `backend/internal/api/sync.go:69-102` (`handleGarminSyncStatus`)
- Modify: `backend/internal/api/api_test.go` (add a new test)
- Modify: `frontend/src/types/api.ts:197-207` (`DetailFillProgress`/`SyncStatus`, `SyncRun.Kind`)
@@ -1165,7 +1165,7 @@ handler doesn't produce `workouts_pending` or a nested `progress` object yet (st
- [ ] **Step 3: Update the handler**
In `backend/internal/api/sync.go`, replace `handleSyncStatus`:
In `backend/internal/api/sync.go`, replace `handleGarminSyncStatus`:
```go
func (s *Server) handleSyncStatus(w http.ResponseWriter, r *http.Request) {
@@ -1526,7 +1526,7 @@ EOF
- [ ] **Step 1: Replace the whole file**
`GarminConnection.tsx` keeps its connect/MFA/Reset-all logic completely unchanged. It loses:
`syncStatus` state, `syncStatusRef`, the `syncProgressLabel` helper, `syncStatus` polling inside
`garminSyncStatus` state, `syncStatusRef`, the `syncProgressLabel` helper, `garminSyncStatus` polling inside
`refreshStatus`/the mount `useEffect`, and all inline sync-progress/last-sync JSX. It gains a
`showSyncModal` boolean and renders `<SyncModal>` when true.