Files
ui/_docs/03_implementation/batch_02_report.md
T
Oleksandr Bezdieniezhnykh ab22223580 [AZ-457] [AZ-459] [AZ-465] [AZ-481] Batch 2 - auth/enum/i18n/CI tests
Implements 22 blackbox test scenarios across the four batch-2 tasks:

AZ-457 - Auth & token handling (11 scenarios, fast + e2e):
- src/api/client.test.ts: FT-P-02, NFT-SEC-04, NFT-PERF-02, NFT-RES-01,
  NFT-RES-08 (apiClient surface)
- src/auth/AuthContext.test.tsx: FT-P-01 (it.fails - Step 4 drift),
  FT-P-03, NFT-SEC-01, NFT-SEC-02
- src/auth/ProtectedRoute.test.tsx: FT-N-04, NFT-RES-08 (router half)
- e2e/tests/auth.e2e.ts: FT-P-02 e2e, NFT-SEC-01/02/03 (cookie attrs
  via Playwright context.cookies(), gated by suite stack)

AZ-459 - Wire-contract enums (4 scenarios):
- tests/wire_contract.test.ts: FT-P-04 (AnnotationStatus, it.fails),
  FT-P-05 (MediaStatus + Affiliation it.fails; CombatReadiness skip
  per verification_pending), FT-P-06 (AnnotationSource control +
  spec value-set membership), FT-N-15 (typed-enum shape + skip for
  value-set verification)
- e2e/tests/wire_contract.e2e.ts: FT-P-06 against real annotations/
  service, drift-gated via AZAION_RUN_DRIFT_E2E
- scripts/run-tests.sh STC-FN15: ripgrep static for MediaType
  magic-literal hygiene

AZ-465 - i18n (4 scenarios, all static + quarantined fast):
- scripts/check-i18n-coverage.mjs: FT-P-22 (en vs ua key parity) +
  FT-P-23 (no raw user strings outside t() in src/**/*.tsx); refined
  JSX text-node regex with negative lookbehind to drop TS generics
  + arrow-function false positives
- tests/i18n-allowlist.json: snapshot of current pre-existing raw
  strings (CI gates growth per AZ-465 Constraints)
- tests/i18n.test.tsx: FT-P-24 + FT-P-25 it.skip (QUARANTINE - i18n
  detector + persistence not wired today; control tests assert the
  gap so the skip flips to a real test once Step 4 lands)

AZ-481 - CI image labels (3 scenarios, static against
  .woodpecker/build-arm.yml):
- scripts/check-ci-image-labels.mjs: NFT-RES-LIM-11 (tag scheme
  ${CI_COMMIT_BRANCH}-arm), NFT-RES-LIM-12 (revision/created/source
  PASS, image.title reported as DRIFT - foundation/CI-CD owns the
  fix), NFT-RES-LIM-13 (revision = $CI_COMMIT_SHA)

Cross-cutting:
- scripts/run-tests.sh: src_grep now excludes *.test.{ts,tsx} +
  *.spec.{ts,tsx} so production-source static checks (STC-SEC4,
  STC-FN15, etc.) don't false-positive on test prose
- tsconfig.json: exclude src/**/*.{test,spec}.{ts,tsx} so production
  tsc -b doesn't see jest-dom matchers
- _docs/03_implementation/batch_02_report.md: full per-task AC
  coverage matrix + drift inventory + verification run
- _docs/_autodev_state.md: 22 tasks remain after batch 2

Verification (host):
  fast    : 7 files, 38 passed | 4 skipped (quarantined)
  static  : 19/19 checks PASS (was 13 in batch 1; +6 from batch 2)
  e2e     : not run on host (Risk 4 - requires suite docker stack)

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

13 KiB
Raw Blame History

Batch Report

Batch: 02 Tasks: AZ-457 (auth & token handling), AZ-459 (wire-contract enums), AZ-465 (i18n), AZ-481 (CI image labels) Date: 2026-05-11 Cycle: Phase A baseline, Step 6 — Implement Tests Total complexity: 12 pts (5 + 2 + 3 + 2)

Task Results

Task Status Files Modified Tests AC Coverage Issues
AZ-457_test_auth_token_handling Done 4 created (3 fast + 1 e2e) 16 fast PASS, 4 e2e gated by suite stack 4 / 4 ACs covered 1 documented drift (FT-P-01 it.fails() until Step 4 fix)
AZ-459_test_wire_contract_enums Done 2 created (1 fast + 1 e2e) 11 fast (2 skipped per verification_pending), 1 e2e (drift-gated) 4 / 4 ACs covered 3 documented drifts (AnnotationStatus, MediaStatus, Affiliation — Step 4 .NET inspection pending)
AZ-465_test_i18n Done 4 created (1 fast + 2 static helpers + 1 allowlist) 4 fast (2 quarantined), 2 static checks PASS 4 / 4 ACs covered 2 quarantined (FT-P-24/25 — detector + persistence not yet implemented in production)
AZ-481_test_ci_image_labels Done 1 created (static check), wired into runner 6 static findings (5 PASS, 1 DRIFT for image.title) 3 / 3 ACs covered 1 documented drift (org.opencontainers.image.title missing — foundation/CI-CD follow-up)

AC Test Coverage: All covered

AZ-457 — Auth & token handling (11 scenarios, 4 ACs)

Scenario Where Profile Status (this run)
FT-P-01 (row 02) bootstrap refresh credentials:'include' src/auth/AuthContext.test.tsx fast PASS via it.fails() (drift documented; flips when Step 4 fix lands)
FT-P-02 (rows 03, 12) 401→refresh→retry src/api/client.test.ts (fast) + e2e/tests/auth.e2e.ts (e2e) fast + e2e fast PASS; e2e gated by suite stack
FT-P-03 (row 11) refresh transparency src/auth/AuthContext.test.tsx fast PASS
FT-N-04 (row 09) unauthenticated /admin/login src/auth/ProtectedRoute.test.tsx fast PASS
NFT-SEC-01 bearer never in browser storage src/auth/AuthContext.test.tsx (fast) + e2e companion fast + e2e fast PASS; e2e gated
NFT-SEC-02 refresh cookie not in document.cookie src/auth/AuthContext.test.tsx (fast) + e2e companion fast + e2e fast PASS; e2e gated
NFT-SEC-03 refresh cookie Secure; HttpOnly; SameSite=Strict e2e/tests/auth.e2e.ts e2e only gated
NFT-SEC-04 credentials:'include' on every authed fetch src/api/client.test.ts fast one assertion PASS, broader claim it.fails() (Step 4 quarantine)
NFT-PERF-02 exactly one refresh per cycle src/api/client.test.ts fast PASS
NFT-RES-01 transparent recovery src/api/client.test.ts fast PASS
NFT-RES-08 expired refresh → /login src/api/client.test.ts + src/auth/ProtectedRoute.test.tsx fast PASS

AZ-459 — Wire-contract enums (4 scenarios, 4 ACs)

Scenario Where Profile Status
FT-P-04 (row 14) AnnotationStatus tests/wire_contract.test.ts fast PASS via it.fails() (drift documented)
FT-P-05 (rows 15-17) MediaStatus / Affiliation / CombatReadiness tests/wire_contract.test.ts fast MediaStatus + Affiliation it.fails() (drift); CombatReadiness it.skip (verification_pending)
FT-P-06 (rows 18, 19) detection wire payload tests/wire_contract.test.ts (fast control) + e2e/tests/wire_contract.e2e.ts (@drift) fast + e2e fast PASS; e2e gated by AZAION_RUN_DRIFT_E2E=1
FT-N-15 MediaType magic-literal hygiene scripts/run-tests.sh (STC-FN15, static) + tests/wire_contract.test.ts (fast) static + fast static PASS; fast PASS for typed-shape, it.skip for value-set (verification_pending)

AZ-465 — i18n (4 scenarios, 4 ACs)

Scenario Where Profile Status
FT-P-22 (row 45) en↔ua key parity scripts/check-i18n-coverage.mjs via STC-FP22 static PASS
FT-P-23 (row 46) no raw user strings outside t() scripts/check-i18n-coverage.mjs --coverage-only via STC-FP23 + tests/i18n-allowlist.json static PASS (allow-list seeded with current pre-existing raw strings; CI gates growth)
FT-P-24 (row 47) detector path on first boot tests/i18n.test.tsx fast + e2e it.skip (QUARANTINE: detector not wired in src/i18n/i18n.ts today) + control test asserts the gap
FT-P-25 (row 48) persistence across reload tests/i18n.test.tsx fast + e2e it.skip (QUARANTINE: no persistence adapter today) + control test asserts the gap

AZ-481 — CI image labels (3 scenarios, 3 ACs)

Scenario Where Profile Status
NFT-RES-LIM-11 tag scheme ${branch}-arm scripts/check-ci-image-labels.mjs via STC-CI11 (parses .woodpecker/build-arm.yml) static PASS
NFT-RES-LIM-12 OCI labels present same static revision/created/source PASS; org.opencontainers.image.title reported DRIFT (lifting the drift is a follow-up CI hygiene task — not in scope here)
NFT-RES-LIM-13 revision label = $CI_COMMIT_SHA same static PASS
(e2e portion against pushed image) not run on host requires-ci gated; requires-ci per _dependencies_table.md

Code Review Verdict: PASS_WITH_WARNINGS

Self-review (4-task batch, sequential per .cursor/rules/no-subagents.mdc). Phases 17 of code-review/SKILL.md walked inline:

  • Phase 1 (Context): each task spec re-read; _docs/02_document/module-layout.md Blackbox Tests envelope respected; _docs/00_problem/input_data/enum_spec_snapshot.json is the contract pin for AZ-459; .woodpecker/build-arm.yml is the SUT for AZ-481.
  • Phase 2 (Spec compliance): every AC across the four task specs has at least one test (running, it.fails(), or it.skip with quarantine reason). Drift handling (AZ-459 §AC-2 "Drift surfaces, not silently passes" + verification_pending markers) implemented uniformly via it.fails() for documented UI drift and it.skip for verification_pending enums; AZ-481's static analogue uses a DRIFT finding category to surface the missing image.title label without gating CI.
  • Phase 3 (Code quality): helper functions compareEnum, describeDrift, probeLabel, instrumentStorage each carry one responsibility; no bare catch; meaningful messages; arrange/act/assert respected; tests do not import <AuthContext> internals or <ProtectedRoute> internals (only setToken / getToken / setNavigateToLogin accessors per AZ-457 AC-2 and the autodev Step 4 testability accessors).
  • Phase 4 (Security): no new secrets in test fixtures (re-uses AZ-456's placeholder argon2 hashes via the seed helpers); no use of eval / shell=True; static checks tightened to exclude *.test.{ts,tsx,spec.ts,spec.tsx} from production grep so test prose can mention forbidden tokens (e.g. document.cookie) without false positives.
  • Phase 5 (Performance): fast suite ~3s wall-clock for 38 + 4-skipped tests (well under 5-min budget); static profile ~13s including vite build and tsc --noEmit (test).
  • Phase 6 (Cross-task consistency): the four tasks touch disjoint subsystems (auth vs enums vs i18n vs CI config); no contract collisions, no duplicate symbols. The shared tests/helpers/{auth,navigate,render}.ts (landed in batch 1) is the only cross-task surface and is consumed read-only.
  • Phase 7 (Architecture compliance):
    • Test files only import the public accessors of 01_api-transport (api, setToken, getToken) and 02_auth (ProtectedRoute default export — the public boundary), plus 00_foundation/i18n for AZ-465's reflective control test. No imports of <AuthContext> internals, no imports of _internal/ or *.internal.* files. Per the batch-1 finding (Low / Architecture / Interpretation), this is the same "test setup helper imports public accessors" pattern, now extended to the test bodies via the tests/helpers/ indirection.
    • No new cyclic module dependencies introduced (test files are leaves in the import graph).

Findings

  1. Low / Maintainabilitytests/i18n-allowlist.json was seeded with the current set of raw strings in src/ so that FT-P-23 (no raw user strings outside t()) passes today. This is per AZ-465 §Constraints ("Allow-list file lives at tests/i18n-allowlist.json; CI enforces it must not grow without a code-review reason") — the allow-list is a snapshot, not a permanent exemption. The static check enforces "must not grow without code review" because the JSON file is committed and any growth would be visible in PR diffs. Recommendation: a follow-up i18n-cleanup task (out of scope here) should drain the allow-list as keys are migrated to t(...).

  2. Low / Style / Driftscripts/check-ci-image-labels.mjs introduces a DRIFT finding category (parallels AZ-459's it.fails()) for the missing org.opencontainers.image.title OCI label in .woodpecker/build-arm.yml. The script reports DRIFT to stdout but does not exit non-zero. Rationale: lifting the drift requires editing CI config (foundation/CI-CD ownership envelope), which is out of scope for a test-only batch. Recommendation: file a follow-up CI hygiene task to add the --label org.opencontainers.image.title=azaion-ui clause; once landed, the DRIFT flips to PASS with no test change required.

  3. Low / Architecture / Interpretation (carried over from batch 1) — same issue as batch 1: tests rely on tests/helpers/{render,auth,navigate}.ts which import production accessors. Reaffirmed here that "Black-box discipline applies to test bodies, not to test setup helpers / composition-root wrappers". The batch-1 recommendation (clarify the layout rule) still stands.

Auto-Fix Attempts: 0

Stuck Agents: None

Files Changed (12)

Created — src/ (3)

src/api/client.test.ts                # AZ-457 fast — 9 tests
src/auth/AuthContext.test.tsx         # AZ-457 fast — 4 tests
src/auth/ProtectedRoute.test.tsx      # AZ-457 fast — 3 tests

Created — tests/ (3)

tests/wire_contract.test.ts           # AZ-459 fast — 11 tests (2 skipped)
tests/i18n.test.tsx                   # AZ-465 fast — 4 tests (2 skipped)
tests/i18n-allowlist.json             # AZ-465 raw-string allow-list (seed)

Created — e2e/tests/ (2)

e2e/tests/auth.e2e.ts                 # AZ-457 e2e — 4 scenarios (gated by suite stack)
e2e/tests/wire_contract.e2e.ts        # AZ-459 e2e — drift-gated annotation save body

Created — scripts/ (2)

scripts/check-i18n-coverage.mjs       # AZ-465 STC-FP22 + STC-FP23
scripts/check-ci-image-labels.mjs     # AZ-481 STC-CI11

Modified (3)

scripts/run-tests.sh                  # +6 static checks (STC-FN15, STC-SEC4 refinement, STC-FP22, STC-FP23, STC-CI11) + src_grep test-file exclusion
tsconfig.json                         # +exclude src/**/*.{test,spec}.{ts,tsx} from production tsc -b
_docs/_autodev_state.md               # batch 2 sub_step pointer

Verification Run (host)

$ bun run test:fast
 ✓ mission-planner/src/test/jsonImport.test.ts (6 tests) 7ms
 ✓ tests/wire_contract.test.ts (11 tests | 2 skipped) 9ms
 ✓ tests/infrastructure.test.ts (5 tests) 35ms
 ✓ tests/i18n.test.tsx (4 tests | 2 skipped) 3ms
 ✓ src/api/client.test.ts (9 tests) 58ms
 ✓ src/auth/ProtectedRoute.test.tsx (3 tests) 76ms
 ✓ src/auth/AuthContext.test.tsx (4 tests) 241ms
 Test Files  7 passed (7)
      Tests  38 passed | 4 skipped (42)

$ ./scripts/run-tests.sh --static-only
[run-tests] static profile PASSED — 19/19 checks (was 13 in batch 1; +6 from batch 2)

$ ./scripts/run-tests.sh
[run-tests] static profile : ran (PASS)
[run-tests] fast profile   : ran (PASS)
[run-tests] e2e profile    : skipped (host)
[run-tests] exit code      : 0

E2E profile not exercised in this batch — same Risk 4 as batch 1 (requires docker compose -f e2e/docker-compose.suite-e2e.yml up -d plus parent-suite :test images). Per AZ-457 e2e companion, NFT-SEC-03 specifically requires Playwright's context.cookies() against the real admin/ service.

Next Batch

Remaining: 22 test-implementation tasks in _docs/02_tasks/todo/ (AZ-458, AZ-460..AZ-464, AZ-466..AZ-480, AZ-482). All carry Component: Blackbox Tests and Dependencies: AZ-456 (✓ done) — soft cross-deps:

  • AZ-473 depends on AZ-472 (DetectionClasses fixtures)
  • AZ-458 (SSE bearer rotation) consumes the auth helpers landed by AZ-457 (✓ now done)
  • AZ-467 (ProtectedRoute spinner + RBAC) consumes the same helpers (✓)
  • AZ-468 (Header dropdown — uses authed page) consumes the same helpers (✓)

Suggested next batch (4 tasks, ~10 pts): AZ-458 (SSE lifecycle, 5pts), AZ-467 (ProtectedRoute spinner + RBAC, 4pts), AZ-468 (Header flight dropdown, 2pts), and one small parallel — for example AZ-482 (secrets/banned-libs, 3pts) so the four tasks remain dependency-disjoint at the file level.

Recommendation: continue in a new conversation. Batch 2 added 12 new files and 6 new static checks; the next batch will load distinct task specs and SSE / RBAC subsystems.