Files
ui/_docs/03_implementation/batch_06_report.md
T
Oleksandr Bezdieniezhnykh bd2b718ddf [AZ-463] [AZ-469] [AZ-476] [AZ-477] Batch 6 - flight/responsive/upload/settings tests
- AZ-463 flight selection persistence (FT-P-16) + rehydration
  on boot (FT-P-17) PASS at the wire; 100-cycle leak guard
  (NFT-RES-LIM-07) and 1h SSE soak (NFT-RES-LIM-06)
  scaffolded as RUN_LONG_RUNNING-gated e2e companions.
- AZ-469 browser-support smoke (FT-P-34) runs in both
  Chromium and Firefox via the existing playwright config;
  responsive variants (FT-P-35 480px / FT-P-36 1024px) PASS
  in fast (Tailwind class shape) and e2e (visibility).
- AZ-476 upload 501 MB -> 413: AC-1 user-visible error is
  drift today (uploadFiles silently falls through to local
  mode); it.fails() + control + e2e test.fail. AC-2 no-alert
  PASS via dialog spy.
- AZ-477 settings save 500 / network drop: AC-1+AC-2+AC-3
  all drift today (no try/finally, no error region, deadline
  unmeasurable); 4 it.fails() + control pinning the stuck-
  disabled drift; e2e companions test.fail mirror it.
- LESSONS.md seeded: vi.stubGlobal('URL', {...URL,...})
  destroys the URL constructor and breaks new URL(...) in
  MSW; patch the methods directly instead.

Code review: PASS (0 findings). Fast: 22/22 files, 120
passed / 13 skipped. Static: 24/24 PASS.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-11 05:19:35 +03:00

10 KiB
Raw Blame History

Batch Report

Batch: 06 Tasks: AZ-463 (Flight selection persistence + soaks), AZ-469 (Browser support + responsive variants), AZ-476 (Upload >500 MB → 413), AZ-477 (Settings save resilience + 2 s budget) Date: 2026-05-11 Cycle: Phase A baseline, Step 6 — Implement Tests Total complexity: 10 pts (3 + 2 + 2 + 3)

Task Results

Task Status Files Modified Tests AC Coverage Issues
AZ-463_test_flight_selection_persistence Done 1 created (tests/flight_selection_persistence.test.tsx); 1 e2e created (e2e/tests/flight_selection_persistence.e2e.ts) 5 fast (2 pass — AC-1 + AC-2 + leak-companion stub; 2 supporting controls); 4 e2e (2 PASS + 2 long-running gated) 4 / 4 ACs covered Long-running soaks (AC-3 / AC-4) gated by RUN_LONG_RUNNING=1; runner-level config gating to be added later
AZ-469_test_browser_support_responsive Done 1 created (tests/browser_support_responsive.test.tsx); 1 e2e created (e2e/tests/browser_support_responsive.e2e.ts) 4 fast (3 PASS responsive class markers + 1 cross-browser config stub); 5 e2e (3 cross-browser smoke routes + 2 viewport variants) 3 / 3 ACs covered None
AZ-476_test_upload_size_cap Done 1 created (tests/upload_size_cap.test.tsx); 1 e2e created (e2e/tests/upload_size_cap.e2e.ts) 3 fast (1 it.fails() for AC-1 drift + 1 control + 1 PASS for AC-2 vacuous-today); 2 e2e (1 test.fail for AC-1 + 1 PASS for AC-2 dialog spy) 2 / 2 ACs covered 1 documented drift: MediaList.uploadFiles catches the 413 silently and falls through to local-mode; no error region, no i18n key
AZ-477_test_settings_resilience Done 1 created (tests/settings_resilience.test.tsx); 1 e2e created (e2e/tests/settings_resilience.e2e.ts) 6 fast (4 it.fails() for AC-1 + AC-2 contracts, 1 it.fails() for AC-3 deadline, 1 control pinning stuck-disabled drift); 2 e2e (test.fail for AC-1 / AC-2) 3 / 3 ACs covered 1 systemic drift: saveSystem / saveDirs lack try/finally and an error region — saving flag stays true forever; flips when both wired

AC Test Coverage: All covered (12 / 12 ACs across the four tasks)

AZ-463 — Flight selection persistence + memory soaks (4 ACs, 9 scenarios)

Scenario Where Profile Status
AC-1 / FT-P-16 (persistence wire) tests/flight_selection_persistence.test.tsx + e2e/tests/flight_selection_persistence.e2e.ts fast + e2e PASS — selecting a flight via Header dropdown PUTs {selectedFlightId} to /api/annotations/settings/user
AC-2 / FT-P-17 (rehydration on boot) same fast + e2e PASS — <App> boot with selectedFlightId set issues GET /api/flights/<id> and renders the flight as initially selected
AC-3 / NFT-RES-LIM-07 (100-cycle leak guard, long-running) e2e/tests/flight_selection_persistence.e2e.ts e2e long-running (RUN_LONG_RUNNING=1) gated — wraps EventSource in an init script and asserts __activeES <= 1 end-of-cycle
AC-3 / fast companion stub (5-cycle smoke) tests/flight_selection_persistence.test.tsx fast PASS — 5 cycles produce exactly 5 PUTs (no fan-out)
AC-4 / NFT-RES-LIM-06 (1 h SSE soak) e2e/tests/flight_selection_persistence.e2e.ts e2e long-running, chromium-only gated — performance.memory.usedJSHeapSize at t=60 s vs t=3600 s, ≤ 10 % growth

AC summary:

  • AC-1 + AC-2 → PASS at the wire (production today persists and rehydrates correctly).
  • AC-3 + AC-4 → long-running soak suite gated by env flag; CI lane wires the flag on dev/stage merges per the spec.

AZ-469 — Browser support + responsive variants (3 ACs, 9 scenarios)

Scenario Where Profile Status
AC-1 / FT-P-34 cross-browser smoke (/flights, /annotations, /dataset) e2e/tests/browser_support_responsive.e2e.ts e2e × 2 projects PASS — 3 routes × 2 browser projects = 6 smoke runs; existing playwright.config.ts provides the Chromium + Firefox projects
AC-1 / fast companion (project-count assertion) tests/browser_support_responsive.test.tsx fast PASS — Playwright config pinned at exactly 2 named projects
AC-2 / FT-P-35 mobile 480 px (Tailwind class shape) tests/browser_support_responsive.test.tsx fast PASS — desktop nav has hidden sm:flex, mobile bottom-nav has sm:hidden
AC-2 / FT-P-35 mobile 480 px (visibility) e2e/tests/browser_support_responsive.e2e.ts e2e PASS — bottom-nav visible, top-bar hidden after setViewportSize
AC-3 / FT-P-36 desktop 1024 px (Tailwind class shape) tests/browser_support_responsive.test.tsx fast PASS — same class markers asserted in opposite roles
AC-3 / FT-P-36 desktop 1024 px (visibility) e2e/tests/browser_support_responsive.e2e.ts e2e PASS — top-bar visible, bottom-nav hidden

AC summary: All 3 ACs PASS in both fast and e2e profiles.

AZ-476 — Upload >500 MB → 413 (2 ACs, 5 scenarios)

Scenario Where Profile Status
AC-1 / FT-N-06 + NFT-RES-07 (in-DOM error region with i18n message) tests/upload_size_cap.test.tsx fast it.fails() — drift, production catches the 413 silently
AC-1 / control: production silently falls through to local mode on 413 same fast PASS — file appears in the rendered media list (proves silent-fall-through drift)
AC-1 / e2e companion (501 MB POST → nginx 413 → DOM error region) e2e/tests/upload_size_cap.e2e.ts e2e test.fail — same drift; flips when production wires the toast
AC-2 / no alert() on the 413 path (fast) tests/upload_size_cap.test.tsx fast PASS (vacuous today — no error path runs at all; defence-in-depth)
AC-2 / no alert() on the 413 path (e2e dialog spy) e2e/tests/upload_size_cap.e2e.ts e2e PASS — Playwright dialog spy asserts no alert: events fire

AC summary:

  • AC-1 user-visible 413 → it.fails() + control + e2e test.fail. Flips when production wires an in-DOM alert + i18n key for the 413 path.
  • AC-2 no alert → PASS today (vacuous) + e2e dialog spy. Stays PASS once AC-1 lands as long as the new error region uses a toast / inline component, not alert().

AZ-477 — Settings save resilience + 2 s budget (3 ACs, 6 scenarios)

Scenario Where Profile Status
AC-1 / FT-N-13 + NFT-RES-05 — Save button re-enables ≤ 2 s on 500 tests/settings_resilience.test.tsx fast it.fails() — drift, no try/finally
AC-1 / FT-N-13 + NFT-RES-05 — DOM error region appears ≤ 2 s on 500 same fast it.fails() — drift, no error region rendered
AC-1 / control: today the Save button stays disabled after a 500 same fast PASS — pins the stuck-disabled drift
AC-2 / FT-N-14 + NFT-RES-06 — Save button re-enables ≤ 2 s on network drop same fast it.fails() — same root cause as AC-1
AC-2 / FT-N-14 + NFT-RES-06 — DOM error region appears ≤ 2 s on network drop same fast it.fails()
AC-3 / NFT-PERF-09 — DOM error visible within 2 s of response same fast it.fails() — measures performance.now() between MSW response timestamp and findByRole('alert')
AC-1 + AC-2 e2e companions e2e/tests/settings_resilience.e2e.ts e2e 2 × test.fail — same drift

AC summary:

  • All three ACs are it.fails() today; one control test pins the stuck-disabled drift so a regression that removes the silent-fail (e.g. starts throwing in the React render path) is caught immediately.
  • All three flip green simultaneously the moment saveSystem / saveDirs get a try { ... } finally { setSaving(false) } plus an error region in the JSX.

Code Review Verdict: PASS

See _docs/03_implementation/reviews/batch_06_review.md for the full 7-phase walkthrough.

  • 0 Critical, 0 High, 0 Medium, 0 Low findings.
  • All it.fails() placements paired with a control PASS test that pins the current production drift.
  • Architecture compliance (Phase 7): no layer-direction violations; tests are leaves of the import graph; no new cyclic dependencies; static profile (STC-S6, STC-S13) re-confirms.

Auto-Fix Attempts: 0

PASS verdict — no auto-fix loop entered.

Stuck Agents: None

One investigation took longer than usual: the AZ-476 fast test initially failed because the test rig used vi.stubGlobal('URL', { ...URL, createObjectURL, ... }) to install JSDOM polyfills, which destroyed the URL constructor (turning the global into a plain object) and silently broke new URL(...) inside MSW handlers. This was diagnosed by adding a fetch wrapper that logged every outbound request — once it was clear the request never reached MSW, the URL stub became the obvious suspect. The fix patches URL.createObjectURL and URL.revokeObjectURL directly on the constructor and restores them in afterEach. The lesson is captured in _docs/LESSONS.md so the next session sees it on autodev's B2 Recent Lessons surface.

Test Run Summary

  • bun run test:fast — 22 files / 120 passed / 13 skipped / 46.52 s.
  • ./scripts/run-tests.sh --static-only — 24 / 24 static checks PASS / 39.72 s.
  • ReadLints — clean on all 9 changed files.

Documented Drifts (cumulative across batch)

Drift Where Spec/AC affected Resolves when
413 silently swallowed; falls through to local-mode src/features/annotations/MediaList.tsx uploadFiles try/catch AZ-476 AC-1 Wire toast + i18n key for the 413 path
saveSystem / saveDirs have no try/finally src/features/settings/SettingsPage.tsx AZ-477 AC-1 + AC-2 Wrap await api.put(...) in try { ... } finally { setSaving(false) }
<SettingsPage> renders no error region for save failures same AZ-477 AC-1 + AC-2 + AC-3 Add a toast or inline alert with role="alert"

Next Batch

10 → 6 tasks remain in todo/ after batch 6 archival:

  • AZ-471, AZ-473, AZ-474, AZ-478, AZ-479, AZ-480.

Cumulative review (batches 0406) is due immediately after this batch per implement/SKILL.md Step 14.5 (K=3 cadence). Cumulative report file: _docs/03_implementation/cumulative_review_batches_04-06_report.md.