mirror of
https://github.com/azaion/ui.git
synced 2026-06-22 11:01:10 +00:00
[AZ-458] [AZ-467] [AZ-468] [AZ-482] Batch 3 - SSE/RBAC/Header/security tests
Implements 4 blackbox-test tasks for AZ-455 Phase A baseline:
- AZ-458 SSE lifecycle + bearer rotation: 9 fast tests (8 pass, 1
QUARANTINE for annotation-status); 4 e2e scenarios (gated by suite
stack). Uses tests/helpers/sse-mock.ts with globalThis.EventSource
monkey-patch per AC-3 (no stub of src/api/sse.ts). AC-2 bearer
rotation captured as documented drift via it.fails() — FlightsPage
useEffect deps do not include the token today.
- AZ-467 ProtectedRoute spinner + timeout + RBAC: 9 new fast tests
extending the AZ-457 file (6 pass, 3 QUARANTINE), plus 3 e2e
scenarios. FT-P-32 spinner a11y is it.fails() drift; FT-P-33 timeout
and FT-N-03/05 RBAC redirects are it.skip QUARANTINE (no production
behavior today). Positive control: admin_carol reaches /admin.
- AZ-468 Header flight-dropdown a11y: 6 fast tests (5 pass, 1
QUARANTINE). FT-P-30/31 are it.fails() drift (aria-expanded /
role=listbox / aria-activedescendant currently missing); FT-N-09
is it.skip QUARANTINE (no document keydown handler exists).
- AZ-482 Secrets + banned-libs + AC-N1 anti-criterion: 3 new static
checks (STC-SEC13 legacy integrations, STC-SEC14 concurrent-edit,
STC-SEC1B dist/ OWM key) plus refactor of 4 existing checks
(STC-N2/N4/S13/S6) to read from tests/security/banned-deps.json
via scripts/check-banned-deps.mjs per AZ-482 constraint
("deny-list lives in tests/security/banned-deps.json so additions
are visible in code review"). All 22 static checks PASS.
Totals: 57 fast tests pass + 9 skipped; 22/22 static checks pass.
Self-review verdict PASS_WITH_WARNINGS — all five findings are
documented drifts captured by it.fails() / it.skip QUARANTINE +
control tests. See _docs/03_implementation/batch_03_report.md
for the per-task / per-AC matrix and recommended Phase B follow-up
production tasks (Header a11y; ProtectedRoute spinner/timeout/RBAC;
SSE bearer-rotation reconnect; AnnotationsPage SSE).
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1,76 +0,0 @@
|
||||
# Test — SSE Lifecycle & Bearer Rotation
|
||||
|
||||
**Task**: AZ-458_test_sse_lifecycle
|
||||
**Name**: SSE lifecycle + bearer-rotation reconnect
|
||||
**Description**: Implement every blackbox test that exercises the SPA's SSE streams — live-GPS, annotation-status — covering open/close lifecycle and the bearer-rotation reconnect (≤5 s) after a token refresh.
|
||||
**Complexity**: 5 points
|
||||
**Dependencies**: AZ-456_test_infrastructure
|
||||
**Component**: 01_api-transport + 05_flights + 06_annotations (Blackbox Tests)
|
||||
**Tracker**: AZ-458
|
||||
**Epic**: AZ-455
|
||||
|
||||
## Problem
|
||||
|
||||
The SPA holds two long-running `EventSource` connections — live-GPS (per `<FlightsPage>`) and annotation-status (per `<AnnotationsPage>`) — using the in-query-string bearer pattern (ADR-008). The open/close timing and the reconnect-on-refresh path must be deterministic; otherwise tests downstream see flapping streams and undefined ordering.
|
||||
|
||||
## Outcome
|
||||
|
||||
- 9 test scenarios pass, asserting open/close timing and bearer-rotation reconnect behavior.
|
||||
- Fast tests use `tests/helpers/sse-mock.ts` (per AZ-456 Risk 3); e2e tests use the embedded LiveGPS + annotation-status generators in the suite test images.
|
||||
- No test reads or manipulates `src/api/sse.ts` internals.
|
||||
|
||||
## Scope
|
||||
|
||||
### Included
|
||||
|
||||
| Scenario | Profile | Source file | results_report row |
|
||||
|----------|---------|-------------|--------------------|
|
||||
| FT-P-09 — annotation-status SSE opens on `<AnnotationsPage>` mount | fast + e2e | blackbox-tests.md | per FT-P-09 |
|
||||
| FT-P-10 — annotation-status SSE closes on unmount | fast | blackbox-tests.md | per FT-P-10 |
|
||||
| FT-P-18 — live-GPS SSE opens within 5 s of flight select | fast + e2e | blackbox-tests.md | per FT-P-18 |
|
||||
| FT-P-19 — live-GPS SSE closes within 1 s of deselect | fast | blackbox-tests.md | per FT-P-19 |
|
||||
| NFT-PERF-03 — SSE bearer-rotation reconnect ≤ 5 s | e2e | performance-tests.md | per NFT-PERF-03 |
|
||||
| NFT-PERF-04 — live-GPS SSE opens within 5 s of flight select | fast | performance-tests.md | per NFT-PERF-04 |
|
||||
| NFT-PERF-05 — live-GPS SSE closes within 1 s of deselect | fast | performance-tests.md | per NFT-PERF-05 |
|
||||
| NFT-PERF-06 — annotation-status SSE unsubscribes within 1 s on page unmount | fast | performance-tests.md | per NFT-PERF-06 |
|
||||
| NFT-RES-02 — SSE bearer rotation — both streams reconnect within 5 s | e2e | resilience-tests.md | per NFT-RES-02 |
|
||||
|
||||
### Excluded
|
||||
|
||||
- SSE server-disconnect indicator (covered in 23_test_network_resilience).
|
||||
- The 401-retry path on `fetch` (covered in 02_test_auth_token_handling).
|
||||
- The async-video detect SSE (covered in 06_test_detection_endpoints — quarantined as Phase B target).
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
**AC-1: Open/close timing**
|
||||
Given a freshly-mounted `<FlightsPage>` or `<AnnotationsPage>`,
|
||||
When the relevant SSE should open or close per the scenario,
|
||||
Then the event happens within the timing budget and the observable EventSource state matches (`readyState: 1` for open, closed connection for close).
|
||||
|
||||
**AC-2: Bearer rotation**
|
||||
Given an active SSE stream and an upcoming token refresh,
|
||||
When the bearer rotates,
|
||||
Then both live-GPS and annotation-status streams reconnect with the new bearer within 5 s; no event from before the rotation is replayed AFTER the new bearer is in use.
|
||||
|
||||
**AC-3: No internal stubs**
|
||||
The fast profile uses the published `sse-mock.ts` helper to simulate the wire-level SSE event stream. Tests MUST NOT replace `src/api/sse.ts` with a stub; if the SSE module misbehaves, the test FAILS.
|
||||
|
||||
## System Under Test Boundary
|
||||
|
||||
- System under test: `src/api/sse.ts` + the consumer hooks in `<FlightsPage>` / `<AnnotationsPage>` + the React tree.
|
||||
- Allowed stubs: the suite-side SSE endpoints — stubbed via the MSW SSE adapter / `sse-mock.ts` in `fast`, real `flights/` and `annotations/` services in `e2e`.
|
||||
- Disallowed: stubbing `src/api/sse.ts`, the SPA's SSE hooks, or the React tree.
|
||||
- Expected observables per `results_report.md` rows for FT-P-09, 10, 18, 19, NFT-PERF-03..06, NFT-RES-02.
|
||||
|
||||
## Constraints
|
||||
|
||||
- E2E tests rely on the embedded LiveGPS simulator and annotation-status event generator in the suite's test-mode images (per test-data.md).
|
||||
- Bearer in SSE URL `?token=` per ADR-008 — tests assert the URL pattern, not stripped headers.
|
||||
- Bearer-rotation tests MUST drive the rotation through `<AuthContext>`'s normal refresh path, not by directly calling `setToken`.
|
||||
|
||||
## Risks & Mitigation
|
||||
|
||||
**Risk 1 — MSW SSE polyfill brittleness (AZ-456 Risk 3)**
|
||||
- *Risk*: MSW 2.x does not have first-class SSE support. The `sse-mock.ts` helper introduces a small abstraction that, if buggy, makes tests pass that shouldn't.
|
||||
- *Mitigation*: every fast SSE scenario also has an e2e companion that exercises the real wire. If the two disagree, the e2e wins and the fast test is QUARANTINEd until the helper is fixed.
|
||||
@@ -1,68 +0,0 @@
|
||||
# Test — ProtectedRoute, RBAC & Auth Spinner
|
||||
|
||||
**Task**: AZ-467_test_protected_route_rbac
|
||||
**Name**: ProtectedRoute spinner + timeout + RBAC route gating
|
||||
**Description**: Implement the 7 blackbox tests that pin `<ProtectedRoute>`'s auth-loading spinner (a11y), the 10 s timeout fallback, and the client-side RBAC gating for `/admin` and `/settings` (defence in depth; server enforces too).
|
||||
**Complexity**: 4 points
|
||||
**Dependencies**: AZ-456_test_infrastructure
|
||||
**Component**: 02_auth + 03_shared-ui (Blackbox Tests)
|
||||
**Tracker**: AZ-467
|
||||
**Epic**: AZ-455
|
||||
|
||||
## Problem
|
||||
|
||||
`<ProtectedRoute>` is the single client-side gate; getting it wrong leaks RBAC. The spinner + timeout fallback prevents indefinite waits if the bootstrap refresh hangs. RBAC tests run with three seed users (Operator, Admin, integrator-Dave-without-SETTINGS).
|
||||
|
||||
## Outcome
|
||||
|
||||
- 7 scenarios pass: 4 RBAC paths + 3 spinner/timeout paths.
|
||||
|
||||
## Scope
|
||||
|
||||
### Included
|
||||
|
||||
| Scenario | Profile | Source file |
|
||||
|----------|---------|-------------|
|
||||
| FT-P-32 — ProtectedRoute spinner a11y | fast | blackbox-tests.md |
|
||||
| FT-P-33 — ProtectedRoute timeout fallback after 10 s | fast | blackbox-tests.md |
|
||||
| FT-N-03 — authenticated non-admin → `/admin` redirects to `/flights` | fast + e2e | blackbox-tests.md |
|
||||
| FT-N-05 — authenticated user without SETTINGS permission → `/settings` | fast + e2e | blackbox-tests.md |
|
||||
| NFT-SEC-05 — `/admin` blocks non-admins client-side | fast | security-tests.md |
|
||||
| NFT-SEC-06 — `/settings` route gate per RBAC | fast | security-tests.md |
|
||||
| NFT-RES-04 — ProtectedRoute loading timeout fallback after 10 s | fast | resilience-tests.md |
|
||||
|
||||
### Excluded
|
||||
|
||||
- Unauthenticated `/admin` (FT-N-04 — covered in 02_test_auth_token_handling).
|
||||
- Server-side enforcement (suite-level test, out of UI scope).
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
**AC-1: Spinner a11y**
|
||||
`role="status"` + `aria-live="polite"` + accessible label on the loading element.
|
||||
|
||||
**AC-2: Timeout fallback**
|
||||
After 10 s without bootstrap-refresh resolution, fallback UI appears with a retry affordance.
|
||||
|
||||
**AC-3: RBAC redirects**
|
||||
Operator → `/admin` redirects to `/flights`. integrator-Dave → `/settings` redirects per the policy. Operator + Admin → `/admin` reaches it normally.
|
||||
|
||||
**AC-4: Both fast + e2e**
|
||||
RBAC tests run in both profiles. Fast asserts redirect via Router state; e2e asserts via `page.url()`.
|
||||
|
||||
## System Under Test Boundary
|
||||
|
||||
- System under test: `<ProtectedRoute>` + Router + `<AuthContext>`.
|
||||
- Allowed stubs: MSW for `/api/admin/auth/me` returning the right RBAC payload per seed user; real `admin/` in e2e.
|
||||
- Disallowed: stubbing `<ProtectedRoute>`, `<AuthContext>`, or Router.
|
||||
- Expected observables per `results_report.md` rows for FT-P-32, 33, FT-N-03, 05, NFT-SEC-05, 06, NFT-RES-04.
|
||||
|
||||
## Constraints
|
||||
|
||||
- Use seed users `op_alice` (Operator), `admin_carol` (Admin), `integrator_dave` (no SETTINGS) per test-data.md.
|
||||
|
||||
## Risks & Mitigation
|
||||
|
||||
**Risk 1 — Timeout test flakiness**
|
||||
- *Risk*: 10 s wait is long for a fast suite; CI variance may flake.
|
||||
- *Mitigation*: use Vitest fake-timers to advance the clock instantaneously.
|
||||
@@ -1,50 +0,0 @@
|
||||
# Test — Header Flight Dropdown a11y
|
||||
|
||||
**Task**: AZ-468_test_header_dropdown
|
||||
**Name**: Header flight dropdown — closed/open a11y + Escape handler
|
||||
**Description**: Implement the 3 blackbox tests pinning the header flight dropdown's open/closed-state a11y attributes and the Escape-to-close handler-detachment behavior.
|
||||
**Complexity**: 2 points
|
||||
**Dependencies**: AZ-456_test_infrastructure
|
||||
**Component**: 10_app-shell (Header) (Blackbox Tests)
|
||||
**Tracker**: AZ-468
|
||||
**Epic**: AZ-455
|
||||
|
||||
## Problem
|
||||
|
||||
The header dropdown is keyboard-traversed dozens of times per session; an a11y regression makes the app unusable for keyboard / screen-reader users. The Escape handler must detach on close — a leak that hijacks Escape elsewhere.
|
||||
|
||||
## Outcome
|
||||
|
||||
- 3 scenarios pass.
|
||||
|
||||
## Scope
|
||||
|
||||
### Included
|
||||
|
||||
| Scenario | Profile | Source file |
|
||||
|----------|---------|-------------|
|
||||
| FT-P-30 — header flight dropdown closed-state a11y | fast | blackbox-tests.md |
|
||||
| FT-P-31 — header flight dropdown open-state a11y | fast | blackbox-tests.md |
|
||||
| FT-N-09 — header dropdown Escape — close + handler detached | fast | blackbox-tests.md |
|
||||
|
||||
### Excluded
|
||||
|
||||
- Flight selection logic itself (covered in 08_test_flight_selection_persistence).
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
**AC-1: Closed state**
|
||||
`aria-expanded="false"`; trigger has accessible name; no `aria-activedescendant`.
|
||||
|
||||
**AC-2: Open state**
|
||||
`aria-expanded="true"`; `role="listbox"` (or `menu`); option list has roles; `aria-activedescendant` points to a real id.
|
||||
|
||||
**AC-3: Escape detach**
|
||||
After Escape closes the dropdown, the document-level Escape handler installed by the dropdown is removed (tracked via `addEventListener` / `removeEventListener` spies). No leakage into other components' Escape handlers.
|
||||
|
||||
## System Under Test Boundary
|
||||
|
||||
- System under test: `<Header>` flight dropdown + Escape handler.
|
||||
- Allowed stubs: MSW for flights list endpoint.
|
||||
- Disallowed: reading dropdown React state.
|
||||
- Expected observables per `results_report.md` rows for FT-P-30, 31, FT-N-09.
|
||||
@@ -1,67 +0,0 @@
|
||||
# Test — Secrets in Source + Banned-Lib Hygiene + Anti-Criterion
|
||||
|
||||
**Task**: AZ-482_test_secrets_and_banned_libs
|
||||
**Name**: OWM key not in source/bundle + no ML/JOSE/SW/dropped libs + AC-N1 anti-criterion
|
||||
**Description**: Implement the 6 static blackbox tests pinning the source-/bundle-hygiene contracts: no OpenWeatherMap key in source (NFT-SEC-09), no in-browser ML libs (NFT-SEC-10), no JOSE / response-signature libs (NFT-SEC-11), no service worker (NFT-SEC-12), no dropped legacy features (NFT-SEC-13), and the AC-N1 anti-criterion (no concurrent-edit reconciliation surfaces in source) (NFT-SEC-14).
|
||||
**Complexity**: 3 points
|
||||
**Dependencies**: AZ-456_test_infrastructure
|
||||
**Component**: 00_foundation + cross-cutting (Blackbox Tests)
|
||||
**Tracker**: AZ-482
|
||||
**Epic**: AZ-455
|
||||
|
||||
## Problem
|
||||
|
||||
Six contract pins that are all static-checkable and all about what MUST NOT exist in the codebase / built bundle. Each comes from a hard restriction in `_docs/00_problem/restrictions.md` (R*) or `_docs/00_problem/security_approach.md`. Without static enforcement, they regress on the day someone "just adds" a banned library.
|
||||
|
||||
## Outcome
|
||||
|
||||
- 6 scenarios pass (as static checks).
|
||||
|
||||
## Scope
|
||||
|
||||
### Included
|
||||
|
||||
| Scenario | Profile | Source file | results_report row |
|
||||
|----------|---------|-------------|--------------------|
|
||||
| NFT-SEC-09 — OpenWeatherMap API key is not shipped in source or bundle | static | security-tests.md | 63 |
|
||||
| NFT-SEC-10 — No in-browser ML libs | static | security-tests.md | per NFT-SEC-10 |
|
||||
| NFT-SEC-11 — No response-signature / JOSE libs on the request path | static | security-tests.md | per NFT-SEC-11 |
|
||||
| NFT-SEC-12 — No service worker — offline mode is explicitly absent | static + e2e | security-tests.md | per NFT-SEC-12 |
|
||||
| NFT-SEC-13 — Dropped legacy features are not present in source | static | security-tests.md | per NFT-SEC-13 |
|
||||
| NFT-SEC-14 — Anti-criterion AC-N1 — no concurrent-edit reconciliation surfaces | static | security-tests.md | per NFT-SEC-14 |
|
||||
|
||||
### Excluded
|
||||
|
||||
- Auth-storage assertions (covered in 02_test_auth_token_handling).
|
||||
- Destructive `alert()` ban (covered in 11_test_destructive_ux).
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
**AC-1: OWM key absence**
|
||||
NFT-SEC-09 — `grep -rn '335799082893fad97fa36118b131f919' src/` returns 0 hits (per the autodev Step 4 C01 refactor); `grep -rn` against `dist/` after `bun run build` also returns 0 hits. The OWM key is provided only via `VITE_OWM_API_KEY` at build time.
|
||||
|
||||
**AC-2: No ML libs**
|
||||
NFT-SEC-10 — `package.json` does not declare `tensorflow`, `tfjs`, `onnxruntime`, `@tensorflow/*`, `@huggingface/*`; the static check enumerates against an explicit deny-list.
|
||||
|
||||
**AC-3: No JOSE / signature libs**
|
||||
NFT-SEC-11 — `package.json` does not declare `jose`, `jsonwebtoken`, `node-forge`, `tweetnacl`; same deny-list pattern.
|
||||
|
||||
**AC-4: No service worker**
|
||||
NFT-SEC-12 — `grep -rn 'serviceWorker' src/` returns no `register(` call; `grep -rn 'sw\.js\|workbox' src/ public/` returns 0; e2e: `navigator.serviceWorker.getRegistrations()` returns `[]`.
|
||||
|
||||
**AC-5: Dropped features absent**
|
||||
NFT-SEC-13 — `grep -rn 'WhatsApp\|TelegramBot\|D-Bus\|libsignal' src/` returns 0 hits (the dropped legacy integrations).
|
||||
|
||||
**AC-6: AC-N1 anti-criterion**
|
||||
NFT-SEC-14 — no source string matches `concurrent.edit|operational.transform|crdt|y-?websocket` — the SPA has no concurrent-edit reconciliation surface.
|
||||
|
||||
## System Under Test Boundary
|
||||
|
||||
- System under test: the source tree at HEAD, the built `dist/` after `bun run build`, and `package.json` / `bun.lock`.
|
||||
- Allowed stubs: none — these are pure ripgrep / grep / build-output checks.
|
||||
- Disallowed: bypassing the build to test against a hand-edited bundle.
|
||||
|
||||
## Constraints
|
||||
|
||||
- All checks runnable from `scripts/run-tests.sh --static-only`; total runtime ≤ 30 s.
|
||||
- AC-2 / AC-3 deny-list lives in `tests/security/banned-deps.json` so additions are visible in code review.
|
||||
Reference in New Issue
Block a user