Files
ui/_docs/02_document/tests/resilience-tests.md
T
Oleksandr Bezdieniezhnykh 510df68bcf [AZ-447] autodev Steps 1-4 baseline: docs, tests, refactor specs
Captures the full output of autodev existing-code Phase A through
Step 4 (Code Testability Revision) for the Azaion UI workspace:

- Step 1 Document: _docs/02_document/ (FINAL_report, architecture,
  glossary, components/, modules/, diagrams/, system-flows,
  module-layout) plus _docs/00_problem/ + _docs/01_solution/ +
  _docs/legacy/ + _docs/how_to_test + README.
- Step 2 Architecture Baseline: architecture_compliance_baseline.md.
- Step 3 Test Spec: _docs/02_document/tests/ (environment,
  test-data, blackbox/performance/resilience/security/
  resource-limit tests, traceability-matrix), enum_spec_snapshot,
  expected_results/results_report.md (98 rows), plus the
  run-tests.sh + run-performance-tests.sh runners.
- Step 4 Code Testability Revision: 01-testability-refactoring/
  run dir (list-of-changes C01-C07, deferred_to_refactor,
  analysis/research_findings + refactoring_roadmap) and the 7
  child task specs AZ-448..AZ-454 under _docs/02_tasks/todo/
  plus _dependencies_table.md.
- _docs/_autodev_state.md pins the cursor at Step 4 / refactor
  Phase 4 entry so /autodev resumes cleanly.

Epic AZ-447 (UI testability gates) tracks the 7 child tasks that
will land in subsequent commits.

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

8.5 KiB

Resilience Tests

Failure / recovery scenarios at the SPA's observable boundary: bearer expiry, refresh cookie loss, upstream 5xx, network partition, oversized uploads, SSE drop. Every fault injection is at the network / browser layer; the UI is observed for graceful behavior and recovery.

NFT-RES-01: 401 → refresh → retry recovery is transparent

Summary: An authenticated request that returns 401 mid-session is refreshed and retried without unmounting the routed view. Traces to: AC-01, AC-23

Preconditions:

  • Active session on /flights.

Fault injection:

  • Force a 401 on the next outbound authenticated request.

Steps:

Step Action Expected Behavior
1 Stub the next request to return 401 once first response 401
2 Observe the SPA's reaction POST /api/admin/auth/refresh with credentials:'include'; on 200, original request retried with new bearer
3 Inspect <ProtectedRoute> children not unmounted; re-render delta ≤ 1

Pass criteria: row 03 sequence; row 11 re-render bound (≤ 1); row 12 refresh count == 1 — all hold simultaneously. Expected result source: results_report.md rows 03, 11, 12.


NFT-RES-02: SSE bearer-rotation — both streams reconnect within 5 s

Summary: A bearer rotation during open SSE streams (live-GPS + annotation-status) tears them down and reopens them with the new token. Traces to: AC-24

Preconditions:

  • Two EventSources open.

Fault injection:

  • Trigger a server-driven rotation of the bearer (force a refresh).

Steps:

Step Action Expected Behavior
1 Rotate bearer new token in memory
2 Observe each EventSource closes, then reopens with new ?token=
3 Measure max reconnect time ≤ 5 000 ms

Pass criteria: both streams close+open exactly once; max reconnect ≤ 5 000 ms (results_report.md row 13). Status: quarantined until SSE reconnect-on-rotation ships (Step 8 hardening). Expected result source: results_report.md row 13.


NFT-RES-03: Network offline at boot — error state, no offline mode

Summary: With network disabled, app boot results in a user-visible error state — NOT a service worker-served cached UI. Traces to: AC-N3

Preconditions:

  • Browser network disabled (or all /api/* stubs respond with offline error).

Fault injection:

  • All outbound requests fail with net::ERR_INTERNET_DISCONNECTED (or equivalent).

Steps:

Step Action Expected Behavior
1 Load the SPA static assets served by nginx OK; API calls fail
2 Observe DOM login or general error surface present
3 Inspect navigator.serviceWorker.controller null

Pass criteria: row 93 — error/login-failed state present; no service worker controller. Expected result source: results_report.md row 93.


NFT-RES-04: ProtectedRoute loading timeout fallback after 10 s

Summary: The <ProtectedRoute> spinner has a bounded loading window; a stalled auth bootstrap surfaces a retry CTA / error. Traces to: AC-17

Preconditions:

  • Bootstrap refresh stubbed to never resolve.

Fault injection:

  • POST /api/admin/auth/refresh hangs (no response).

Steps:

Step Action Expected Behavior
1 Mount <ProtectedRoute> spinner rendered
2 Advance fake time to 10 s timeout fires
3 Inspect DOM retry CTA or error message present; spinner unmounted

Pass criteria: row 59 — fallback present, spinner absent. Status: quarantined until timeout fix lands (Step 4). Expected result source: results_report.md row 59.


NFT-RES-05: Settings save with upstream 500 — UI state recovers

Summary: A 500 on settings save surfaces an error and resets the saving flag. Traces to: AC-27

Preconditions:

  • Form filled in valid state on /settings.

Fault injection:

  • Upstream PUT /api/annotations/settings/system returns 500 after T ms.

Steps:

Step Action Expected Behavior
1 Click Save PUT issued
2 Stub responds 500 within 2 s failure
3 Inspect within 2 s toast / inline error; saving === false; no route navigation

Pass criteria: row 68. Status: quarantined until Step 4 try/finally fix. Expected result source: results_report.md row 68.


NFT-RES-06: Settings save with network drop — try/finally state reset

Summary: When the underlying fetch throws (network drop), saving resets and the user sees an error. Traces to: AC-27

Preconditions:

  • Form filled in valid state.

Fault injection:

  • Network drop mid-PUT (fetch rejects).

Steps:

Step Action Expected Behavior
1 Click Save PUT issued
2 Stub throws rejection delivered
3 Inspect saving === false; error surfaced

Pass criteria: row 69. Status: quarantined. Expected result source: results_report.md row 69.


NFT-RES-07: nginx 413 on oversized upload surfaces user-visible error

Summary: An upload that exceeds client_max_body_size 500M returns 413; the UI presents a user-facing message (no silent failure, no alert()). Traces to: AC-10

Preconditions:

  • Authenticated; <MediaList> open.

Fault injection:

  • Drop a 501 MB synthetic file.

Steps:

Step Action Expected Behavior
1 Upload 501 MB upload starts
2 nginx rejects 413 delivered
3 Inspect UI error containing the i18n "file too large" string; no alert() invoked

Pass criteria: row 39. Expected result source: results_report.md row 39.


Summary: When the refresh cookie is gone (or expired) and a 401 occurs, the SPA redirects the user to /login rather than silently looping refresh. Traces to: AC-01, AC-22

Preconditions:

  • Cookie cleared from the browser jar.

Fault injection:

  • 401 on any authenticated call; subsequent POST /api/admin/auth/refresh returns 401.

Steps:

Step Action Expected Behavior
1 Issue authenticated call 401
2 Refresh attempted 401 (no cookie)
3 Observe routing redirect to /login

Pass criteria: final URL /login; no infinite refresh loop (single refresh attempt). Derived from AC-01 + AC-22; no specific results_report row binds the loop bound — Phase 3 flags this and the loop bound is added to row 03 if accepted.


NFT-RES-09: Annotation download tainted-canvas fallback

Summary: When <canvas>.toBlob() raises a tainted-canvas exception (cross-origin video frame), the user sees an error rather than a silent no-op. Traces to: NFR (04_verification_log.md finding on handleDownload tainted-canvas risk)

Preconditions:

  • An annotation is loaded from a video sourced with CORS that taints the canvas.

Fault injection:

  • Cross-origin video source taints the canvas; toBlob throws.

Steps:

Step Action Expected Behavior
1 Click Download export attempted
2 toBlob throws error handled
3 Inspect UI user-visible error; no alert(); no silent swallow

Pass criteria: row 96 — error surfaced; no silent swallow; no fabricated blob; no alert(). Expected result source: results_report.md row 96.


NFT-RES-10: SSE server disconnect — UI surfaces a connection-lost indicator

Summary: When the suite server closes a live-GPS or status-events SSE without rotation, the UI does NOT show stale data and DOES indicate the connection lost. Traces to: AC-08, AC-09, AC-24

Preconditions:

  • One SSE stream open.

Fault injection:

  • Server force-closes the stream (no rotation).

Steps:

Step Action Expected Behavior
1 Drop the stream from the server error fires on EventSource
2 Observe DOM a connection-lost indicator is rendered (or stale-data badge)
3 Observe reconnect behavior EventSource auto-retries per browser default; if the SPA re-creates it, exactly one new instance

Pass criteria: row 97 — connection-lost indicator OR reconnect attempt within 10 s; stale data NOT rendered as live; reconnect attempts ≤ 1 in the 10 s window. Expected result source: results_report.md row 97.