Unit test filter service and read state service
epic-in-app-notification-centre-services-task-010 — Write unit tests for the Role-Aware Notification Filter Service and the Notification Read State Service using flutter_test and mocktail. Cover: predicate output for all three role scopes, in-memory filter inclusion and exclusion rules, optimistic update emission sequence, rollback trigger on repository failure, read_at timestamp correctness, and mark-all-read bulk path. Target 90%+ line coverage on both service classes.
Acceptance Criteria
Technical Requirements
Execution Context
Tier 2 - 518 tasks
Can start after Tier 1 completes
Implementation Notes
For testing the optimistic update emission sequence, use a StreamController
Use const constructors for test notification fixtures to keep tests readable. Group tests with descriptive group() labels: 'role scope filtering', 'type predicate', 'read-state predicate', 'optimistic update', 'rollback', 'mark-all-read'. Use setUp() to reset mocks between tests.
Testing Requirements
Pure unit tests using flutter_test and mocktail. No widget or integration tests in this task. Test structure: one test file per service class, grouped with group() blocks per scenario category. For Role-Aware Notification Filter Service: test each role scope independently, test type filters (assignment_reminder, certification_expiry, pause_status, scenario_prompt), test read-state filters (all/unread/read), test combined type+read-state predicates, test empty input list.
For Notification Read State Service: test optimistic stream emission ordering using StreamController fakes, test rollback on mock repository .throws(), test read_at extraction from mock response, test mark-all-read with list of 0, 1, and N items. Use lcov or flutter test --coverage to verify 90%+ line coverage.
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.