high priority medium complexity testing pending testing specialist Tier 5

Acceptance Criteria

All five components (NotificationPreferencesRepository, FCMTokenManager, NotificationRepository, NotificationPermissionManager, and the integration flow) have individual test files
Code coverage ≥ 80% on each component as measured by flutter test --coverage
NotificationPreferencesRepository tests cover: create, read, update, delete, idempotent default init, and RLS violation scenario
FCMTokenManager tests cover: successful registration, token rotation via onTokenRefresh, revocation on logout, and deletion of stale tokens for the same device
NotificationRepository tests cover: paginated fetch (first page, next page, empty page), mark-as-read mutation, mark-all-read, and unread count stream emitting correct values
NotificationPermissionManager tests cover: iOS authorized/denied/notDetermined states, Android API <33 and ≥33 flows, rationale dialog triggered only when shouldShowRationale returns true
Integration test simulates full flow: authenticate → request permission (granted) → FCM token registered → default preferences initialized — all assertions pass
All tests pass in CI with flutter test and produce no flaky failures across 3 consecutive runs

Technical Requirements

frameworks
Flutter
flutter_test
Mockito (mock_gen)
Riverpod
apis
Supabase Dart SDK (mocked)
Firebase Cloud Messaging (mocked)
data models
device_token
accessibility_preferences
performance requirements
Full test suite must complete in under 60 seconds
No real network calls in unit tests — all Supabase and FCM interactions mocked
security requirements
Test fixtures must not contain real user IDs, JWTs, or FCM tokens — use UUIDs generated via Uuid package
RLS edge case tests must assert that cross-user data access returns empty/forbidden, not real data

Execution Context

Execution Tier
Tier 5

Tier 5 - 253 tasks

Can start after Tier 4 completes

Implementation Notes

Use @GenerateMocks([SupabaseClient, SupabaseQueryBuilder, FirebaseMessaging]) with build_runner to generate type-safe mocks. For the unread count stream test, use StreamController in the mock and emit values manually to assert BLoC/Riverpod state transitions. The RLS edge case test should configure the mock to throw a PostgrestException with code '42501' (RLS violation) and assert the repository returns an appropriate Failure type rather than crashing. For the integration test, use a real Supabase test instance and wrap the test in setUpAll/tearDownAll that creates a test user and deletes all test rows after the run.

Tag integration tests with @Tags(['integration']) so they can be excluded from fast local runs.

Testing Requirements

Unit tests using flutter_test with Mockito-generated mocks for SupabaseClient and FirebaseMessaging. Integration tests use a dedicated Supabase test project (separate from staging) with test credentials stored in CI secrets. Structure test files as: notification_preferences_repository_test.dart, fcm_token_manager_test.dart, notification_repository_test.dart, notification_permission_manager_test.dart, notification_infrastructure_integration_test.dart. Run coverage with 'flutter test --coverage && genhtml coverage/lcov.info' and fail CI if any component is below 80%.

Component
Notification Repository
data medium
Epic Risks (3)
high impact medium prob scope

iOS only allows one system permission prompt per app install. If the rationale dialog timing or content is wrong the user may permanently deny permissions during onboarding, permanently blocking push delivery for that device with no recovery path short of manual system settings navigation.

Mitigation & Contingency

Mitigation: Design and user-test the rationale dialog content and trigger point (after onboarding value-demonstration step, not at first launch). Implement the settings-deep-link fallback in NotificationPermissionManager so the permission state screen always offers a path to system settings if denied.

Contingency: If denial rates are high in TestFlight testing, revise the rationale copy and trigger timing before production release. Ensure the in-app notification centre provides full value without push so denied users are not blocked from the feature.

medium impact medium prob technical

FCM token rotation callbacks can fire at any time, including during app termination or network outage. If the token rotation is not persisted reliably the backend trigger service will dispatch to a stale token, resulting in silent notification failures that are hard to diagnose.

Mitigation & Contingency

Mitigation: Persist token rotation updates with a local queue that retries on next app foreground if network is unavailable. Use Supabase upsert by (user_id, device_id) to prevent duplicate token rows and ensure the latest token always wins.

Contingency: If token staleness is observed in production, add a token validity check on each app foreground and force a re-registration if the stored token does not match the FCM-reported current token.

high impact low prob security

Incorrect RLS policies on notification_preferences or fcm_tokens could expose one user's preferences or device tokens to another user, or could block the backend Edge Function service role from reading token lists needed for dispatch, silently dropping all notifications.

Mitigation & Contingency

Mitigation: Write explicit RLS policy tests using the Supabase test harness covering user-scoped read/write, service-role read for dispatch, and cross-user access denial. Review policies during code review with a security checklist.

Contingency: Maintain a rollback migration that reverts the RLS changes, and add an integration test in CI that asserts the service role can query all tokens and that a normal user JWT cannot access another user's token rows.