critical priority medium complexity testing pending testing specialist Tier 4

Acceptance Criteria

All test cases pass with zero failures in flutter_test runner
Test for successful org_id claim injection: guard wraps client with correct org_id header for an authenticated admin session
Test for missing session: guard throws AuthException or returns unauthenticated error without invoking Supabase client
Test for claim injection failure: simulated Supabase set_claim RPC failure results in guard surfacing a typed error, never an unguarded client
Test that guarded client instance is the only one exposed — mocked unguarded client must never be called after guard initialisation
Test multi-org scenario: switching org_id mid-session injects updated claim correctly
Minimum 90% line coverage on AdminRlsGuard class measured by flutter test --coverage
All tests are deterministic and do not depend on network or real Supabase instance
Mock setup documents which Supabase client methods are stubbed so future maintainers understand the contract

Technical Requirements

frameworks
Flutter
flutter_test
mockito or mocktail for Dart mocking
apis
Supabase set_claim RPC (mocked)
Supabase Auth session API (mocked)
data models
AdminSession
OrgClaim
GuardedSupabaseClient
performance requirements
Full test suite completes in under 10 seconds
No async leaks — every Future and Stream is properly awaited or closed in tearDown
security requirements
Tests must assert that no raw/unguarded SupabaseClient reference leaks out of the guard under any failure path
Verify claim injection uses server-side set_claim, never client-side JWT manipulation

Execution Context

Execution Tier
Tier 4

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.

Component
Admin Row-Level Security Guard
service high
Epic Risks (3)
high impact medium prob security

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.

high impact medium prob technical

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.

high impact low prob security

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.