# Test — Flight Selection, Persistence & Memory Soak

**Task**: AZ-463_test_flight_selection_persistence
**Name**: Flight selection persistence + 100-selection soak + 1-hour SSE soak
**Description**: Implement the blackbox tests that pin the `PUT /api/annotations/settings/user` persistence path for the selected flight, the rehydration-on-boot path, and the two memory-soak tests (100 selections + 1-hour live-GPS SSE) that catch listener leaks.
**Complexity**: 3 points
**Dependencies**: AZ-456_test_infrastructure
**Component**: 10_app-shell + 05_flights (Blackbox Tests)
**Tracker**: AZ-463
**Epic**: AZ-455

## Problem

The selected-flight state is the SPA's most-touched mutable singleton (per `<FlightContext>`). It must persist via the suite, rehydrate on boot, and NOT leak SSE listeners or `<FlightContext>` consumers across rapid selections. Past regressions in this surface manifested as memory creep over long sessions, hard to catch without a soak.

## Outcome

- 4 scenarios pass — selection persistence, rehydration, listener leak guard, memory soak.
- Soak tests run in CI but are gated to `e2e` and tagged `Long-running` so they don't gate every commit.

## Scope

### Included

| Scenario | Profile | Source file |
|----------|---------|-------------|
| FT-P-16 — flight selection persists via `PUT /api/annotations/settings/user` | fast + e2e | blackbox-tests.md |
| FT-P-17 — selected-flight rehydration on boot | fast + e2e | blackbox-tests.md |
| NFT-RES-LIM-06 — live-GPS SSE 1-hour soak — no listener leak, no memory creep | e2e (long-running) | resource-limit-tests.md |
| NFT-RES-LIM-07 — 100 sequential flight selections — no leaked SSEs, no leaked Contexts | e2e (long-running) | resource-limit-tests.md |

### Excluded

- Live-GPS SSE timing (covered in 03_test_sse_lifecycle).
- Flight CRUD / waypoint shape (covered in subsequent feature-cycle tasks; out of scope for the baseline test suite).

## Acceptance Criteria

**AC-1: Persistence wire pattern**
FT-P-16 captures the outbound `PUT /api/annotations/settings/user` body and asserts the new `selectedFlightId` is in the payload (per the wire contract).

**AC-2: Rehydration**
FT-P-17 boots a fresh `<App>` against a seed where `op_alice` has `selectedFlightId` set; asserts the SPA renders that flight as initially selected without explicit user action.

**AC-3: Listener leak guard**
NFT-RES-LIM-07 performs 100 sequential `select(flightA) → select(flightB)` cycles; asserts the active EventSource count never exceeds 1 at the end of each cycle and the total `<FlightContext>` consumer count remains bounded.

**AC-4: Memory soak**
NFT-RES-LIM-06 maintains a live-GPS SSE open for 1 hour with the simulator emitting at 1 Hz; asserts the heap snapshot at t=3600 s is within 10% of the heap snapshot at t=60 s (allows for fixture growth, rejects leaks).

## System Under Test Boundary

- System under test: `<FlightContext>` + `<FlightsPage>` + `src/api/sse.ts` + `src/api/client.ts`.
- Allowed stubs: MSW for fast; real `flights/` + `annotations/` services for e2e.
- Disallowed: stubbing `<FlightContext>` or its consumers; reading React state directly.
- Expected observables per `results_report.md` rows for FT-P-16, 17 + the resource-limit row binding for the soak tests (rows 64-65 region per traceability-matrix.md).

## Constraints

- Long-running tests (NFT-RES-LIM-06, 07) tagged `@long-running` in the Playwright config; CI only runs them on `dev`/`stage` merges, not on every commit.
- Memory measurements via `page.evaluate(() => performance.memory.usedJSHeapSize)` in Chromium; Firefox skipped for these two (Firefox does not expose `performance.memory`).

## Risks & Mitigation

**Risk 1 — Soak test flakiness on shared CI runners**
- *Risk*: A noisy neighbor on the runner inflates heap measurements, false-failing the soak.
- *Mitigation*: 10% tolerance + retry once on `dev`/`stage`; manual approval required on `main`.
