high priority medium complexity testing pending testing specialist Tier 6

Acceptance Criteria

All happy-path flows pass: coordinator login emits RoleState.coordinator, peerMentor login emits RoleState.peerMentor, globalAdmin login emits RoleState.blocked
PermissionCheckerService.canAccess() returns true for coordinator accessing coordinator-gated routes and false for peerMentor attempting the same
Multi-role user (coordinator + peerMentor in same org) resolves to coordinator as primary role by default
User holding roles in two different NHF chapters resolves role scoped to the active chapter only — no cross-chapter data leaks
Role revocation mid-session: after Supabase role row is deleted, next permission check detects stale state and triggers re-fetch within 30 seconds or on next app foreground event
Logout resets RoleStateManager to RoleState.unauthenticated and clears all cached permission results
No cross-org data exposure: assertions confirm that org_id filter is applied to all Supabase queries within the integration test scope
All tests are hermetic: no real Supabase network calls; all data returned via mock/fake implementations
Test suite completes in under 60 seconds on CI

Technical Requirements

frameworks
Flutter
flutter_test
BLoC
Riverpod
apis
Supabase Auth (mocked)
Supabase PostgREST (mocked)
data models
UserRole
RoleState
Organization
PermissionResult
performance requirements
Full integration test suite completes under 60 seconds
Role resolution from mock data completes under 50ms per test case
security requirements
Tests must assert org_id scoping on every Supabase query — no unscoped queries permitted
Verify that globalAdmin canAccess returns false for all mobile app routes
Assert that cached permission results are cleared on logout and cannot be read after session end

Execution Context

Execution Tier
Tier 6

Tier 6 - 158 tasks

Can start after Tier 5 completes

Implementation Notes

Use a FakeSupabaseClient that implements the same interface as the real client, returning pre-configured fixture data. Do NOT mock RoleResolutionService itself — the point of integration tests is to exercise real service logic against fake infrastructure. For revocation detection, advance a FakeClock or trigger a manual refresh rather than sleeping. For cross-org isolation tests, prepare two org fixture datasets in the same fake client and assert that queries always include an eq('org_id', activeOrgId) filter — inspect captured query parameters on the fake client.

The multi-chapter NHF scenario (user in up to 5 lokallag) is particularly important: role resolution must scope permissions to the currently active chapter context, not a union of all chapters. Pay attention to BLoC stream ordering: use emitsInOrder and emitsDone rather than individual expect calls on stream values.

Testing Requirements

Integration tests only — no unit tests and no e2e tests in this task. Use flutter_test with fake/mock Supabase client (implement SupabaseClientFake or use mocktail). Structure tests in three groups: (1) Happy-path role resolution (coordinator, peerMentor, orgAdmin, globalAdmin), (2) Multi-role and multi-chapter edge cases (NHF-specific chapter scoping, role priority ordering), (3) Session lifecycle edge cases (revocation detection, logout reset). Each test must arrange mock data via a test fixture builder, act through the real RoleResolutionService and RoleStateManager (not mocks), and assert on emitted states using expectLater + emitsInOrder.

Aim for 100% branch coverage of RoleResolutionService and PermissionCheckerService.

Component
Role Resolution Service
service medium
Epic Risks (3)
high impact low prob security

A coordinator's permissions could be revoked by an admin while they are actively using the app. If the permission checker relies solely on the cached role state from login, the coordinator could continue performing actions they are no longer authorized for until the next login.

Mitigation & Contingency

Mitigation: The Permission Checker Service must re-validate against the Role Repository (not just in-memory state) before high-impact actions. Implement a configurable staleness window (e.g., 15 minutes) after which role data is refreshed from Supabase in the background.

Contingency: If a revoked permission is detected during a pre-action check, immediately clear the cached role state, force a re-resolution from Supabase, and display an inline error explaining the permission change rather than crashing or silently failing.

medium impact medium prob technical

Using both BLoC and Riverpod in the same state management layer for roles risks state synchronization bugs where one system updates before the other, causing widgets to render with stale role data during the switch transition.

Mitigation & Contingency

Mitigation: Choose a single primary state management approach (Riverpod StateNotifier is recommended) for role state and wrap the BLoC pattern within it if legacy code requires BLoC interfaces. Establish a single source-of-truth provider that all consumers read from.

Contingency: If synchronization bugs appear during integration testing, introduce a RoleStateReady gate widget that delays rendering of role-dependent UI until the state notifier emits a confirmed resolved state, preventing partial renders.

medium impact high prob scope

Hardcoded permission constants per role can become a maintenance burden as new features are added across 61 total features, leading to permission definitions that are scattered, stale, or inconsistent.

Mitigation & Contingency

Mitigation: Centralize all role-permission mappings in a single RolePermissions constants file with named action keys. Enforce that no widget or service directly checks role type strings; all checks must go through the Permission Checker Service.

Contingency: If permission definitions drift out of sync, introduce a validation test suite that cross-references all registered permission constants against their usage sites and fails the CI build if an undefined permission key is referenced.