BLoC integration test: full notification lifecycle
epic-in-app-notification-centre-services-task-011 — Write bloc_test integration tests for the NotificationBloc covering the full notification lifecycle: initial load, Realtime INSERT event ingestion with role filtering, mark-read with optimistic update, mark-all-read, rollback on failure, filter application, and clear-all. Use a fake Supabase Realtime stream controller and mocked repository to simulate INSERT events. Verify correct unread count transitions and state immutability throughout all scenarios.
Acceptance Criteria
Technical Requirements
Execution Context
Tier 7 - 84 tasks
Can start after Tier 6 completes
Implementation Notes
The key challenge is testing the optimistic update sequence. Use a Completer
For rollback: use when(repo.markRead()).thenThrow(Exception('network error')) and verify both the reverted state and the error emission appear in the expect sequence. Use Equatable on all state and event classes to make expect() comparisons work correctly. Use addTearDown(bloc.close) and controller.close() to prevent test pollution. Structure tests in a single file with nested group() blocks per lifecycle phase.
Testing Requirements
BLoC integration tests using bloc_test's blocTest
Use either the real Role-Aware Notification Filter Service (preferred for integration coverage) or a mock. Verify state sequence precisely using expect: [...] lists. For the optimistic update test, use a Completer in the mock repository to control resolution timing and capture intermediate states. Run with flutter test --coverage and confirm NotificationBloc event handler coverage >= 85%.
A Realtime INSERT event arriving during an in-flight mark-all-read operation can cause the new notification to be incorrectly marked read in the optimistic state update, silently hiding it from the user.
Mitigation & Contingency
Mitigation: Process Realtime events sequentially in the BLoC event queue using bloc_concurrency's sequential transformer. The mark-all-read event should only affect notifications whose IDs were fetched before the operation started.
Contingency: Add a reconciliation step after mark-all-read that re-fetches the unread count from the repository and corrects the BLoC state if it diverges from the server value.
A coordinator assigned to many peer mentors may trigger a query returning hundreds or thousands of notifications. Without pagination and query optimisation, the initial load will be slow and memory-heavy.
Mitigation & Contingency
Mitigation: Enforce server-side pagination (50 items per page) in the Role-Aware Filter's query predicates. Add a composite index on (org_id, user_id, created_at DESC) and profile query plans before shipping.
Contingency: If query performance is insufficient for large coordinator scopes, introduce a server-side RPC function that pre-aggregates visible notification IDs and returns only the first page, deferring full scope resolution to lazy-load.
If the read-state optimistic update rolls back frequently due to intermittent connectivity, users will observe notifications toggling between read and unread, creating confusion and distrust of the feature.
Mitigation & Contingency
Mitigation: Queue failed mutations in a local retry store and re-attempt on next connectivity event using a connectivity-aware retry service. Show a non-intrusive banner if offline rather than applying optimistic updates.
Contingency: Disable optimistic updates for mark-as-read in low-connectivity scenarios detected by the connectivity provider, instead showing a loading indicator until server confirmation.