# Test — Network Resilience (Offline, SSE Disconnect, Tainted-Canvas)

**Task**: AZ-478_test_network_resilience
**Name**: Network offline at boot + SSE server disconnect indicator + tainted-canvas fallback
**Description**: Implement the 3 blackbox tests covering network-loss paths: offline-at-boot error state (NO offline mode per E10), SSE server disconnect surfaces a connection-lost indicator, and annotation download falls back gracefully on a tainted canvas.
**Complexity**: 3 points
**Dependencies**: AZ-456_test_infrastructure
**Component**: 01_api-transport + 06_annotations (Blackbox Tests)
**Tracker**: AZ-478
**Epic**: AZ-455

## Problem

The SPA explicitly rejects offline mode (E10 / NFT-SEC-12) but must still degrade gracefully when the network drops mid-session. The two streams in scope are the SSE streams (must surface a connection-lost banner / indicator) and the annotation-download path (must avoid `SecurityError: tainted canvas` crashes).

## Outcome

- 3 scenarios pass.

## Scope

### Included

| Scenario | Profile | Source file | results_report row |
|----------|---------|-------------|--------------------|
| NFT-RES-03 — Network offline at boot — error state, no offline mode | fast + e2e | resilience-tests.md | per NFT-RES-03 |
| NFT-RES-09 — Annotation download tainted-canvas fallback | fast | resilience-tests.md | 96 |
| NFT-RES-10 — SSE server disconnect — UI surfaces a connection-lost indicator | fast + e2e | resilience-tests.md | 97 |

### Excluded

- 401 → refresh → retry network path (covered in 02_test_auth_token_handling).
- Settings save resilience (covered in 22_test_settings_resilience).
- Upload 413 path (covered in 21_test_upload_size_cap).

## Acceptance Criteria

**AC-1: Offline at boot**
NFT-RES-03 — with all `/api/*` requests returning network errors, the SPA renders an error state and does NOT register a service worker / offline cache (defence in depth — NFT-SEC-12 catches the SW separately).

**AC-2: Tainted-canvas fallback**
NFT-RES-09 — annotation download via canvas-to-data-URL on a tainted canvas does not crash the page; a user-visible fallback (alternative download path or in-DOM error) is rendered. Asserted per `results_report.md` row 96.

**AC-3: Disconnect indicator**
NFT-RES-10 — when an SSE EventSource fires `error` with `readyState === 2` (CLOSED), within 2 s a connection-lost indicator is visible in the DOM with the i18n-keyed text. Asserted per `results_report.md` row 97.

## System Under Test Boundary

- System under test: `<App>` boot path, SSE consumers (`<FlightsPage>`, `<AnnotationsPage>`), annotation download handler in `<AnnotationsPage>`.
- Allowed stubs: MSW for `/api/*` (fast); for e2e, suite `flights/` / `annotations/` services are killed mid-session to drive the disconnect path.
- Disallowed: stubbing the SPA components.
- Tainted canvas is induced in fast by drawing an `<img crossOrigin="anonymous">` against a stub that intentionally omits CORS headers.

## Constraints

- Service-worker registration assertion: `navigator.serviceWorker.getRegistrations()` returns `[]` at all times (defence in depth, also enforced by NFT-SEC-12).
