high priority low complexity testing pending testing specialist Tier 7

Acceptance Criteria

Test file `no_access_route_guard_test.dart` exists under `test/` mirroring the source path
Test case: blocked role + destination `/home` → guard returns redirect to `/no-access`
Test case: blocked role + destination `/logout` → guard returns `null` (allow through)
Test case: blocked role + destination `/auth/login` → guard returns `null` (allow through)
Test case: unblocked (`peerMentor`) role + destination `/home` → guard returns `null`
Test case: unauthenticated user (no role) + destination `/home` → guard returns `null` (not the no-access guard's responsibility)
All tests pass with `flutter test` with zero failures
Guard logic is tested in isolation without spinning up a full GoRouter widget tree
Mocked `AccessDenialService` is used — no real Supabase calls

Technical Requirements

frameworks
Flutter
flutter_test
go_router
mocktail or mockito
data models
UserRole
AccessDenialState
NoAccessConfig
performance requirements
All guard unit tests complete in under 1 second total
security requirements
Verify that no protected route is reachable when role is blocked
Ensure allowed-through paths (/logout, /auth/login) are explicitly whitelisted and not inferred

Execution Context

Execution Tier
Tier 7

Tier 7 - 84 tasks

Can start after Tier 6 completes

Implementation Notes

GoRouter guards are typically implemented as a `redirect` callback on `GoRouter` or as a class implementing a `redirect(context, state)` method. Extract the pure logic into a testable function that accepts `isBlocked` (bool) and `location` (String) and returns `String?` — this makes the guard trivially unit-testable without GoRouter internals. The whitelist check should use an explicit `const Set` of allowed paths, not string prefix matching, to prevent accidental bypasses. Ensure the guard does not redirect unauthenticated users — that responsibility belongs to the auth guard, not this guard.

Testing Requirements

Unit tests using `flutter_test`. The GoRouter `redirect` callback is a plain function — test it by calling the guard's `redirect` method directly with a mocked `GoRouterState` (or a simple data class holding the `location` string) rather than pumping a full widget. Use `mocktail` to stub `AccessDenialService.isBlocked`. Cover all whitelist paths (`/logout`, `/auth/login`) as individual test cases.

Group by scenario: `group('blocked role', ...)` and `group('unblocked role', ...)`. Aim for 100% branch coverage on the guard's redirect logic.

Component
No-Access Route Guard
service low
Epic Risks (2)
high impact medium prob technical

If the GoRouter redirect callback evaluates the no-access route itself as a blocked destination, it will trigger an infinite redirect loop, crashing the navigator.

Mitigation & Contingency

Mitigation: Add an explicit guard condition in the redirect callback: return null (no redirect) when the current location is already the no-access route or the logout route. Write a dedicated unit test covering this exact scenario.

Contingency: If the redirect loop is detected in production, deploy a hotfix that adds the null-return guard; the feature can be toggled off via the existing feature-flag infrastructure while the fix is prepared.

medium impact low prob integration

The access-denial-service may read role state before authentication completes (e.g. during app resume), causing a temporary false-positive block that redirects valid peer-mentor users to the no-access screen.

Mitigation & Contingency

Mitigation: Subscribe to the role-state-manager's loading/ready lifecycle and only evaluate role-based access once the RBAC state is confirmed as loaded. Return a 'pending' state that causes the guard to defer rather than redirect.

Contingency: Add a retry mechanism: if a user lands on the no-access screen but their role subsequently resolves as non-blocked, automatically navigate them to the role-based home screen.