high priority medium complexity testing pending testing specialist Tier 5

Acceptance Criteria

RoleStateManager initial state test: freshly constructed manager has no active role and empty availableRoles
setActiveRole test: emits RoleState with correct activeRole after call
switchRole valid test: emits new RoleState with updated activeRole
switchRole invalid test: state unchanged and SwitchRoleResult.unauthorized returned
resetOnLogout test: all state fields reset to initial values and stream emits cleared state
Multi-role scenario: user with coordinator + peerMentor roles โ€” coordinator selected as primary, both present in availableRoles
PermissionCheckerService canAccess: test all route/role combinations from the permission matrix โ€” each combination has an explicit test case
canAccess deny-by-default: unknown route string returns false for all roles
globalAdmin blocked: canAccess returns false for ALL routes when role is globalAdmin
checkPermissionBeforeSubmit revoked: mock Supabase to return downgraded role, assert PermissionCheckResult.revoked
checkPermissionBeforeSubmit sessionExpired: mock Supabase timeout, assert PermissionCheckResult.sessionExpired
Test suite achieves 90%+ line coverage on RoleStateManager and PermissionCheckerService as verified by flutter test --coverage

Technical Requirements

frameworks
Flutter
flutter_test
mocktail
Riverpod
Dart
apis
Supabase Auth (mocked via mocktail)
data models
assignment
performance requirements
Full test suite runs in under 30 seconds
No real network calls โ€” all Supabase interactions mocked
security requirements
Tests must cover the globalAdmin-blocked path explicitly โ€” this is a security requirement not just a functional one
Tests must verify deny-by-default for unknown routes
Tests must cover permission revocation mid-session scenario

Execution Context

Execution Tier
Tier 5

Tier 5 - 253 tasks

Can start after Tier 4 completes

Implementation Notes

Use ProviderContainer directly in tests (not WidgetTester) since these are pure service tests with no UI. Pattern: final container = ProviderContainer(overrides: [roleRepositoryProvider.overrideWithValue(mockRepo)]); addTearDown(container.dispose). For stream-based tests use expectLater(stream, emitsInOrder([...])) to assert ordered state emissions. The permission matrix test is best written as a data-driven test using a table of (route, role, expectedResult) tuples iterated in a single test case โ€” this makes the matrix exhaustive and readable.

Include a comment in the test file explaining which workshop requirement each permission rule implements (e.g., bulkRegister restricted to coordinator โ€” NHF/HLF workshop ยง2.4).

Testing Requirements

Use flutter_test for all tests. Use mocktail to create MockRoleRepository, MockSupabaseClient, and MockGoTrueClient. Organise tests into three test files: role_state_manager_test.dart, permission_checker_service_test.dart, pre_action_validation_test.dart. Each file should have a group() per method under test.

Use setUp() to construct a fresh ProviderContainer with overrideWithValue() for all mocked dependencies. After all tests, run flutter test --coverage and assert coverage report shows 90%+ for the two service files. Add a CI step that fails the build if coverage drops below threshold.

Component
Permission Checker Service
service medium
Dependencies (3)
Extend PermissionCheckerService with the sensitive pre-action validation flow: checkPermissionBeforeSubmit(action) performs an async re-validation against the current session state before bulk registration submission, expense attestation, and proxy registration actions. This handles the case where permissions may have been revoked mid-session, preventing partial operations. Return a PermissionCheckResult with allowed/denied/revoked states. epic-role-based-access-control-state-and-services-task-006 Extend RoleStateManager with switchRole(newRole) that validates the requested role is in the user's available roles list before switching, emits the new RoleState, and triggers reactive updates downstream. Expose an availableRolesStream so UI components (role switch widget) can display only roles the user actually holds. Ensure org/chapter scoping is preserved when switching between coordinator and peerMentor roles within NHF's multi-chapter structure. epic-role-based-access-control-state-and-services-task-008 Build the PermissionCheckerService with synchronous canAccess(route, role) and asynchronous checkPermission(action, context) methods. Define a permission matrix mapping each UserRole to allowed routes, actions (registerActivity, viewContacts, bulkRegister, exportBufdir, attestExpense), and data operations. The service reads the active role from RoleStateManager and evaluates permissions without additional network calls for the synchronous path. epic-role-based-access-control-state-and-services-task-005
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.