high priority low complexity testing pending testing specialist Tier 3

Acceptance Criteria

Test file covers all public methods on RoleAccessValidator with at least one test per branch
Coordinator accessing their own org's stats → assertCanViewStats returns normally (no exception)
Coordinator accessing another coordinator's stats in the same org → AccessDeniedException thrown with message identifying 'cross-coordinator access'
Coordinator accessing stats for a different org_id → AccessDeniedException thrown with message identifying 'cross-org access'
Org-admin accessing any coordinator's stats within their org → returns normally
Org-admin accessing stats for a different org_id → AccessDeniedException thrown
Unauthenticated actor (null JWT) → UnauthenticatedException thrown, not AccessDeniedException
All exception messages are non-empty strings that identify the violation type without leaking internal IDs
100% branch coverage verified by flutter test --coverage output on the RoleAccessValidator source file
All tests are deterministic and pass without network access

Technical Requirements

frameworks
Flutter
Dart
flutter_test
performance requirements
All tests complete in <500 ms total — no async delays or real network calls
security requirements
Test fixtures must not use real personnummer or real UUIDs from production — use clearly fake UUIDs (00000000-0000-0000-0000-000000000001 pattern)
Exception messages tested must not leak org structure details that could aid enumeration attacks

Execution Context

Execution Tier
Tier 3

Tier 3 - 413 tasks

Can start after Tier 2 completes

Implementation Notes

Create a test helper buildFakeAuthState({required String userId, required String orgId, required String role}) that returns a fake Supabase User/Session object with the correct app_metadata structure. This avoids duplicating JWT fixture setup across every test. Structure the test file as: roleAccessValidatorTest.dart in test/unit/validators/. Import RoleAccessValidator directly — no integration wiring needed.

If RoleAccessValidator depends on a Supabase client to resolve role claims, inject a mock via the constructor. Prefer testing the public contract (exception type + message prefix) over implementation details (which private method was called). After 100% branch coverage is confirmed, add a test for the boundary case where coordinator_id is passed as org_id (transposed parameters) — this is a common integration bug.

Testing Requirements

Use flutter_test group() blocks to organise by scenario: 'coordinator access', 'org-admin access', 'unauthenticated access', 'cross-org access'. Each group has positive (allowed) and negative (denied) test cases. Mock the Supabase auth context using a fake AuthState object with preset user.id and app_metadata claims. Use expect(..., throwsA(isA())) matchers.

After running tests, verify coverage with: flutter test --coverage && genhtml coverage/lcov.info -o coverage/html. The RoleAccessValidator source file must show 100% branch coverage in the HTML report.

Component
Role Access Validator
service low
Epic Risks (3)
high impact medium prob technical

Pre-aggregated Supabase views may still be slow for orgs with very large activity datasets (NHF with 1,400 chapters). If the view query plan performs sequential scans, dashboard load times could exceed acceptable thresholds and degrade the perceived value of the feature.

Mitigation & Contingency

Mitigation: Design views with composite indexes on (org_id, coordinator_id, month) from the start. Run EXPLAIN ANALYZE during development against a seeded dataset of realistic scale. Add materialized view refresh strategy if needed.

Contingency: If live view performance is insufficient, convert to materialized views refreshed on a schedule or on activity-write triggers. Expose the refresh delay transparently in the UI with a 'last updated' timestamp.

high impact low prob security

Supabase RLS policies for the stats views may not be configured correctly during initial migration, potentially allowing cross-coordinator data leakage before the RoleAccessValidator layer is reached. This is a security and compliance risk.

Mitigation & Contingency

Mitigation: Write RLS integration tests as part of this epic that explicitly verify a coordinator JWT cannot read another coordinator's stats rows. Apply RLS policies in the migration script itself, not as a manual step.

Contingency: If an RLS gap is discovered post-deployment, immediately disable the stats screen via a feature flag, apply the corrected RLS migration, and re-enable after verification. Log and audit all queries that ran during the gap window.

medium impact medium prob integration

Cache invalidation logic may not be triggered correctly when a new activity is registered by a peer mentor or when an expense approval is granted. Stale data could cause coordinators to make decisions based on outdated KPIs, undermining trust in the dashboard.

Mitigation & Contingency

Mitigation: Define explicit invalidation event contracts with the activity registration and expense approval pipelines. Implement an event bus subscription within StatsCacheManager. Document the invalidation contract in code.

Contingency: If event-driven invalidation proves unreliable, add a manual 'Refresh' pull-to-refresh gesture on the dashboard and reduce TTL to 5 minutes as a fallback degradation strategy.