critical priority high complexity testing pending testing specialist Tier 4

Acceptance Criteria

A test SQL file (or pgTAP test suite) covers at minimum 12 distinct role/scenario combinations across all 5 protected tables
Test scenario: coordinator of chapter-A cannot SELECT any row from activities, contacts, or assignments where organization_unit_id belongs to chapter-B
Test scenario: coordinator of chapter-A can SELECT all rows within their own chapter subtree
Test scenario: peer mentor cannot SELECT another peer mentor's activities, assignments, or contacts
Test scenario: peer mentor can SELECT their own activities, assignments, and directly assigned contacts
Test scenario: global admin JWT (role = 'global_admin') can SELECT all rows across all organisations and all tables
Test scenario: unauthenticated request (no JWT) returns zero rows from all RLS-protected tables
Test scenario: NHF peer mentor assigned to 3 chapters can SELECT their own records across all 3 chapters
Test scenario: coordinator cannot INSERT an activity with a unit_id outside their chapter (WITH CHECK enforcement)
Test scenario: peer mentor cannot INSERT into user_roles table
Test scenario: simulated JWT with a manually crafted unit_ids claim (client-side tampering attempt) does not grant access beyond the server-injected claims
All tests are automated and executable via supabase db test or equivalent CI command
Tests produce a structured pass/fail report; any failure blocks the deployment pipeline
Test fixtures include seed data: 2 organisations, 3 chapters per org, 2 coordinators, 5 peer mentors, 20 activities distributed across chapters

Technical Requirements

frameworks
Supabase PostgreSQL 15
pgTAP (PostgreSQL unit testing framework)
Supabase CLI
apis
Supabase PostgreSQL 15 (set_config for JWT simulation, pgTAP assertions)
Supabase Auth (auth.jwt() function behaviour under simulated claims)
data models
activity
assignment
contact
contact_chapter
performance requirements
Full test suite must complete within 60 seconds on local Supabase instance (supabase start)
Each test scenario must execute and assert within 2 seconds
Test seed data setup and teardown must use transactions (BEGIN / ROLLBACK) to avoid state pollution between tests
security requirements
Test scenarios must explicitly test that client-side JWT claim manipulation (injecting extra unit_ids) does not bypass RLS — use set_config with a crafted JWT and confirm the database ignores or rejects the tampered payload
Test the global admin bypass path to confirm it only activates when role = 'global_admin' in app_metadata, not user_metadata
Tests must confirm that the service role (postgres user) bypasses RLS — document this as expected behaviour, not a vulnerability
Cross-organisation test: user from org-1 with a valid JWT cannot access any data from org-2, even if they guess a valid row UUID

Execution Context

Execution Tier
Tier 4

Tier 4 - 323 tasks

Can start after Tier 3 completes

Implementation Notes

Supabase's set_config approach for JWT simulation in SQL tests: use SELECT set_config('request.jwt.claims', '{"sub":"user-uuid","app_metadata":{"unit_ids":["unit-a-uuid"],"role":"coordinator"}}', true); followed by SET ROLE authenticated; before each test query. Reset with RESET ROLE; and SELECT set_config('request.jwt.claims', '', true); after. pgTAP provides ok(), is(), isnt(), results_eq() for structured assertions. Example: SELECT ok((SELECT count(*) FROM activities WHERE organization_unit_id = 'chapter-b-unit-id') = 0, 'Coordinator A cannot read Chapter B activities'); Organise test scenarios into groups using pgTAP's subtest() blocks for readable output.

For the tampered JWT test, construct a JWT with a unit_ids array containing chapter-B's unit_id for a chapter-A coordinator, apply via set_config, and assert chapter-B data is still inaccessible. This verifies that the policy relies on server-injected claims from the hook (task-012) rather than raw JWT content that a client could forge. Use consistent UUID fixtures (hardcoded in seed.sql) for deterministic assertions.

Testing Requirements

This task IS the testing task. Deliverables: (1) A pgTAP test file (tests/rls/rls_policy_tests.sql) with plan(), ok(), is(), and finish() assertions covering all 12+ scenarios. (2) A seed data SQL file (tests/rls/seed.sql) creating 2 orgs, 3 chapters each, 2 coordinators, 5 peer mentors, 20 activities. (3) A teardown SQL file (tests/rls/teardown.sql).

(4) Integration into CI via supabase db test command. (5) A written test report template that CI populates with pass/fail counts per scenario category (read isolation, write prevention, admin bypass, unauthenticated). The test suite itself must achieve 100% scenario coverage of the RLS policy matrix defined in task-013's policy audit document.

Component
RLS Policy Manager
infrastructure high
Epic Risks (4)
high impact medium prob security

Injecting all unit assignment IDs into JWT claims for users assigned to many units (up to 5 for NHF peer mentors, many more for national coordinators) may exceed JWT size limits, causing authentication failures.

Mitigation & Contingency

Mitigation: Store unit IDs in a Supabase session variable or a dedicated Postgres function rather than embedding them directly in the JWT payload. Use set_config('app.unit_ids', ...) within RLS helper functions querying the assignments table at policy evaluation time.

Contingency: Fall back to querying the unit_assignments table directly within RLS policies using the authenticated user ID, accepting a small per-query overhead in exchange for removing the JWT size constraint.

medium impact medium prob technical

Rendering 1,400+ nodes in a recursive Flutter tree widget may cause jank or memory pressure on lower-end devices used by field peer mentors, degrading the admin experience.

Mitigation & Contingency

Mitigation: Implement lazy tree expansion — only the root level is rendered on initial load. Child nodes are rendered on demand when the parent is expanded. Use const constructors and ListView.builder for all node lists to minimize rebuild scope.

Contingency: Add a search/filter bar that scopes the visible tree to matching nodes, reducing the visible node count. Provide a 'flat list' fallback view for administrators who prefer searching over browsing the tree.

medium impact medium prob scope

Requirements for what constitutes a valid hierarchy structure may expand during NHF sign-off (e.g., mandatory coordinator assignments per chapter, minimum member counts per region), requiring repeated validator redesign.

Mitigation & Contingency

Mitigation: Design the validator as a pluggable rule engine where each check is a discrete, independently testable function. New rules can be added without changing the core validation orchestration. Surface all rules in a configuration table per organization.

Contingency: Defer non-blocking validation rules to warning-level feedback rather than hard blocks, allowing structural changes to proceed while flagging potential issues for admin review.

high impact low prob integration

Deploying RLS policy migrations to a shared Supabase project used by multiple organizations simultaneously could lock tables or interrupt active sessions, causing downtime during production migration.

Mitigation & Contingency

Mitigation: Write all RLS policies as CREATE POLICY IF NOT EXISTS statements. Schedule migrations during off-peak hours. Use Supabase's migration preview environment to validate policies against production data shapes before applying.

Contingency: Prepare rollback migration scripts for every RLS policy. If a migration causes issues, execute the rollback immediately and re-test the policy logic in staging before reattempting.