Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

14 — QA Checklist (Audit Module)

Module QA specific to framework + shared 00-shared/10. Every check derives from a specific backend behavior so failure analysis is direct.


1. Functional

  • List loads GET /audit-logs with defaults page=1&limit=50 (audit.controller.ts:20-21).
  • Response parsed per actual shape envelope.data.data[] + envelope.data.total (no meta — OQ-2; response-envelope.interceptor.ts:25-32).
  • Entries sorted occurredAt desc (never asc/unsorted) (audit-log.repository.ts:23).
  • Filters action/entityType/actorId sent as query params only when set; AND combination works (audit.controller.ts:26-29).
  • Filter change resets to page 1; Load more appends; hasMore = items.length < total.
  • entityType filter: when data has no such entries, empty state shows helper note (OQ-1), never a raw error.
  • Entry detail renders from in-memory entry (no extra request — no :id endpoint, OQ-4).
  • Diff view: changed/added/removed rows computed correctly; before absent → "Snapshot after action" label (audit.handler.ts:42-43); both absent → empty state.
  • Immutability checks: sending PATCH/DELETE to /api/v1/audit-logs* returns 404 (no routes — audit.controller.ts:17); no edit/delete UI exists; PLAN 19.3 (PLAN.md:191).
  • Masking: response after for a UserCreated-style event never contains passwordHash/password/totpSecret/refreshToken/accessToken/token — masked '***' (audit.service.ts:5-12, 20).
  • Realtime: while list open, another session performs an action → entry appears (≤ ~1 s), dedupe by _id; banner count correct; WS payloads never rendered verbatim (OQ-8).
  • WS reconnect → resubscribe + silent refetch (merge without duplicates).
  • Non-numeric page/limit handled: client never sends them; if server 400 arrives → reset page 1 (http-exception.filter.ts:47-55).
  • correlationId present on detail and copyable (traceability, audit-log.schema.ts:51-52).

2. Visual / layout

  • Desktop table ≥ 1200 px: sticky header, hover, arrow-key nav, horizontal scroll ≤ 1199 px (min-width 720).
  • Phone cards + filter bottom sheet; tablet master-detail ≥ 840 px.
  • Diff colors (added secondary / removed error / changed tertiary) pass contrast in light and dark; never color-only (icon + text) (00-shared/09 §9).
  • Long correlationId/entityId mono strings truncate with tooltip, wrap at 200% text scale (no clipping).
  • Dark parity: sticky header surfaceContainerHigh, JSON on surfaceContainerLow.

3. Accessibility (00-shared/09)

  • TalkBack: full list→filter→detail flow operable without sight; table column headers announced; diff rows labeled "field X changed".
  • VoiceOver separately; keyboard-only desktop ( / focus, arrows, Enter, Esc ).
  • Realtime append + result count announced via live regions.
  • Reduced motion: no stagger/flash (appends fade only).
  • Contrast body 4.5:1, icons 3:1.

4. Performance

  • List first frame ≤ 300 ms cached / ≤ 2 s network (00-shared/10 §1); skeleton ≤ 200 ms.
  • ListView.builder/virtualized table — no unbounded widget build on 10 000+ rows.
  • Deep pages (page 50+) load without jank; no duplicate parallel requests for same page (load-more guard).
  • JSON tree: long arrays/objects collapsed by default; expand is lazy.

5. Offline & network (10 §2)

  • Offline → cached list + banner; Load more disabled; filter changes blocked (no stale queries) with guidance.
  • Reconnect → WS resubscribe + refetch, merged without duplicates.
  • Slow network × Load more: bottom spinner, no ANR; retry after failure keeps list.

6. Tablet / desktop

  • Master-detail at ≥ 840 px (list selection persists in detail pane).
  • Right-click context menu = long-press menu; hover tooltips (UTC time, truncated values, full action).
  • Enter submits filter; Tab order filter → table → footer; focus ring visible.

7. Localization

  • All strings via i18n keys; date/time via Intl locale; UTC in tooltip; no hardcoded date formats.
  • RTL readiness: table/diff/key order logical; no hardcoded alignment.
  • Long localized action strings (never translated — raw eventType) don't break row height (ellipsis + tooltip).

8. Permissions

  • Client hides route without audit.read (permissions.constants.ts:54); direct link → 403 screen.
  • Known server gap (OQ-5): today any authenticated user can call GET /audit-logs (JWT-only guard, audit.controller.ts:9) — verify with security owner whether student/parent tokens must be blocked server-side before Phase-5 (docs/IMPLEMENTATION_PLAN.md:241).
  • Cross-tenant: no UI path can read another tenant's entries (server scopes by requireTenantId(), audit.controller.ts:30).

9. Dark mode

  • Full visual pass on list/table/detail/diff/JSON; token-only colors.

10. Animations

  • m-* tokens only; reduced-motion respected; no flash on realtime append.

11. Security

  • Masked '***' values displayed as-is — UI never hints secrets are recoverable.
  • WS broadcast payloads not logged/cached in display state (OQ-8).
  • Copy JSON on detail never includes raw secrets (server-masked only).
  • Retention conflict flagged (OQ-6): migrate.ts:23 TTL 90 days on audit_logs.occurredAt vs 7-year policy (DATA_RETENTION.md:29) — product/security decision required; QA should re-verify retention after fix.

12. Server-backed edge mirrors

  • total shown matches count when filters applied (server count call, audit.service.ts:48).
  • Entry with metadata renders nested JSON tree (audit-log.schema.ts:54-55).
  • actorType other than user (future system/api_key writes) renders badge correctly (audit-log.schema.ts:18-19).

QA scripts

  • Golden: AuditTable, AuditEntryRow, AuditDiffView (changed/added/removed/empty/ nested), JsonTreeView, dark mode, 3 sizes.
  • E2E (P0): login as org_admin → open audit → filter by action → open entry → verify diff → realtime append from second session → immutability probe (PATCH/DELETE → 404).
  • Security smoke: student-role token calls GET /audit-logs → record current behavior (allowed today, OQ-5) and confirm Phase-5 enforcement when landed.