Write Admin RLS Guard unit tests
epic-admin-portal-foundation-task-011 — Implement unit tests for AdminRlsGuard covering: successful org_id claim injection, missing session handling, claim injection failure recovery, and correct guarded client exposure. Use flutter_test with mocked Supabase client. Verify no unguarded client requests can escape the middleware.
Acceptance Criteria
Technical Requirements
Execution Context
Tier 4 - 323 tasks
Can start after Tier 3 completes
Implementation Notes
Use mocktail (preferred over mockito for null-safe Dart) to create a MockSupabaseClient. The guard should be designed as a thin wrapper — test the wrapper's decision logic, not Supabase internals. Structure tests in groups: 'AdminRlsGuard - claim injection', 'AdminRlsGuard - error paths', 'AdminRlsGuard - client exposure'. Use setUp/tearDown to reset mock state.
Avoid real async timers — use fake_async if any debounce or retry logic exists in the guard. The critical invariant to test: after guard.initialise() throws, calling guard.client must itself throw, not return a stale unguarded reference. Document each mock expectation with a comment explaining which RLS scenario it represents.
Testing Requirements
Unit tests only (no integration or e2e). Use flutter_test with mocktail to mock SupabaseClient and GoTrueClient. Cover the following scenarios: (1) happy path — valid session, claim injected, guarded client returned; (2) null session — guard returns error before any DB call; (3) set_claim RPC throws PostgrestException — guard catches, re-throws typed error, unguarded client never exposed; (4) concurrent calls — two simultaneous guard initialisations with different org_ids do not cross-contaminate; (5) claim already present — guard is idempotent and does not double-inject. Run with `flutter test --coverage` and enforce 90%+ branch coverage on the guard file.
Missing RLS policies on one or more tables (e.g., a newly added join table or a Supabase view) could expose cross-org data to org_admin queries, creating a GDPR-reportable data breach.
Mitigation & Contingency
Mitigation: Enumerate all tables and views accessed by admin queries before writing any policy. Create an automated test that attempts a cross-org query for each table from an org_admin JWT and asserts an empty result set.
Contingency: If a gap is discovered post-deployment, immediately disable the affected query surface and deploy a hotfix policy before re-enabling. Log the incident and notify DPO if any cross-org data was returned.
The recursive CTE for NHF's deeply nested org tree (up to 5 levels, 1,400 local chapters) may exceed the 2-second dashboard load target when resolving large subtrees on every request.
Mitigation & Contingency
Mitigation: Benchmark the recursive CTE against a synthetic NHF-scale dataset during development. Introduce a short-TTL server-side cache for subtree resolution results. Index the parent_id column on the organisations table.
Contingency: If CTE performance remains insufficient, materialise the org subtree as a precomputed closure table updated on org structure changes, and switch the RLS guard to query the closure table instead.
Incorrect JWT claim injection in AdminRlsGuard (e.g., wrong claim key name or missing refresh on org switch) could silently apply the wrong org scope, causing org_admin to see a different organisation's data without an explicit error.
Mitigation & Contingency
Mitigation: Write unit tests for the guard that verify the injected claim value against the authenticated user's org_id for every admin route. Add a server-side assertion that the claim matches the user's database record before executing any query.
Contingency: Roll back the guard to a deny-all fallback, invalidate active admin sessions, and re-issue corrected JWTs. Audit query logs to identify any sessions that received incorrect scope.