fix spec: correct file paths for feed assembly, route files
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -10,7 +10,7 @@
|
||||
|
||||
### 1a. Retry Utility
|
||||
|
||||
**File:** `src/shared/retry.ts`
|
||||
**File:** `src/shared/retry.ts` (isomorphic — used by both server API clients and client feed assembly)
|
||||
|
||||
Generic `withRetry<T>(fn, opts)`:
|
||||
- Exponential backoff, configurable max attempts (default 3), base delay (default 500ms)
|
||||
@@ -18,6 +18,8 @@ Generic `withRetry<T>(fn, opts)`:
|
||||
- Only retries on transient failures: network errors, 5xx, 429
|
||||
- Does not retry 4xx or Zod validation errors
|
||||
|
||||
Placed in `src/shared/` because both server (2a, 2c) and client (3a feed fetches) consume it.
|
||||
|
||||
**Tests:** Unit tests for retry logic, backoff timing, abort behavior, non-retryable error passthrough.
|
||||
|
||||
### 1b. Error Boundary Component
|
||||
@@ -48,11 +50,14 @@ Generic `withRetry<T>(fn, opts)`:
|
||||
|
||||
**Files:** `src/server/shared/jobs/poll-checker.ts`, `src/server/shared/jobs/legislation-syncer.ts`
|
||||
|
||||
- Replace `Promise.all` with `Promise.allSettled` for batch vote/vorgänge fetching
|
||||
- Log failures, continue with successful results (partial success)
|
||||
- Structured log format: `{ job, action, id, error }` for parseable journalctl output
|
||||
Both jobs use sequential `for` loops with individual `await` calls. The legislation-syncer already has per-item try-catch; the poll-checker is missing try-catch around several inner awaits (e.g., `resolveMandateId`, vote fetching inside the per-device notification loop), which can break the entire iteration on a single failure.
|
||||
|
||||
**Tests:** Mock partial API failures, verify job completes with available data and logs errors.
|
||||
Fixes:
|
||||
- Poll-checker: wrap every `await` inside the per-poll and per-device loops in try-catch — log the error with structured format and continue to the next item
|
||||
- Legislation-syncer: verify existing try-catch coverage is complete, add structured logging
|
||||
- Structured log format for both: `{ job, action, id, error }` for parseable journalctl output
|
||||
|
||||
**Tests:** Mock individual API call failures mid-iteration, verify job processes remaining items and logs errors.
|
||||
|
||||
### 2c. Push Notification Error Handling
|
||||
|
||||
@@ -87,7 +92,7 @@ Fixes:
|
||||
|
||||
**Files:** `src/client/features/feed/lib/assemble-feed.ts`, `src/client/features/feed/hooks/use-feed.ts`
|
||||
|
||||
- Replace `Promise.all` with `Promise.allSettled` for poll/vote fetching
|
||||
- Replace `Promise.all` with `Promise.allSettled` for poll/vote fetching in `assemble-feed.ts` (the single assembly file that handles all feed types)
|
||||
- Surface partial failures as a non-blocking warning banner ("some data couldn't be loaded")
|
||||
- Await `saveFeedCache()` + wrap in try-catch (currently fire-and-forget)
|
||||
- Catch `loadFeedCache()` DB errors, fall back to empty cache
|
||||
@@ -101,11 +106,13 @@ Fixes:
|
||||
- Debounce follows sync: 300ms debounce when follows change rapidly
|
||||
- In-flight guard: if sync already running, queue the next one instead of concurrent requests
|
||||
|
||||
**Tests:** Simulate rapid follow/unfollow, verify only one sync request in-flight, final state correct.
|
||||
**Tests:** Use `vi.useFakeTimers()` to simulate rapid follow/unfollow within the debounce window. Mock `syncFollowsToBackend` to track calls. Verify only one sync request in-flight, final state includes all changes.
|
||||
|
||||
### 3c. IndexedDB Error Handling
|
||||
|
||||
**Files:** All modules in `src/client/shared/db/` — `follows.ts`, `feed-cache-db.ts`, `geo-cache-db.ts`, `push-state-db.ts`, `device.ts`
|
||||
**Files:** `src/client/shared/db/follows.ts`, `feed-cache-db.ts`, `geo-cache-db.ts`, `push-state-db.ts`, `device.ts`
|
||||
|
||||
Excluded: `client.ts` (PGlite singleton — init errors are already fatal by design) and `migrate-from-localstorage.ts` (one-time migration with its own error handling).
|
||||
|
||||
- Wrap all PGlite query calls with try-catch
|
||||
- On failure: log error, return sensible defaults (empty arrays, null)
|
||||
@@ -139,11 +146,11 @@ Fixes:
|
||||
### 4a. Route Error Boundaries
|
||||
|
||||
Wrap each major route with error boundary from 1b:
|
||||
- `bundestag`, `landtag` — "couldn't load votes"
|
||||
- `legislation.$legislationId` — "couldn't load legislation"
|
||||
- `politician.$politicianId` — "couldn't load politician profile"
|
||||
- `representatives` — "couldn't load representatives"
|
||||
- Root layout keeps last-resort catch-all
|
||||
- `src/client/routes/app/bundestag/index.tsx`, `src/client/routes/app/landtag/index.tsx` — "couldn't load votes"
|
||||
- `src/client/routes/app/legislation.$legislationId.tsx` — "couldn't load legislation"
|
||||
- `src/client/routes/app/politician.$politicianId.tsx` — "couldn't load politician profile"
|
||||
- `src/client/routes/app/representatives.tsx` — "couldn't load representatives"
|
||||
- `src/client/routes/__root.tsx` keeps last-resort catch-all
|
||||
|
||||
### 4b. Loading/Empty States
|
||||
|
||||
@@ -158,7 +165,7 @@ No additional tests beyond 1b's error boundary tests. This section is primarily
|
||||
|
||||
### 5a. Server Integration Tests
|
||||
|
||||
- **Legislation router:** Full request→response for GET /upcoming, GET /:id, POST /:id/vote, GET /:id/results/:deviceId, including 400/404 paths
|
||||
- **Legislation router:** Full request→response for GET /upcoming, GET /:id, POST /:id/vote, GET /:id/results/:deviceId, GET /dip-proxy/vorgaenge, GET /dip-proxy/vorgaenge/:id, including 400/404 paths
|
||||
- **Politician router:** Cache hit vs miss, invalid ID, AW API failure → 500
|
||||
- **Push router:** Subscribe → sync → test → unsubscribe lifecycle, invalid payloads
|
||||
|
||||
|
||||
Reference in New Issue
Block a user