high priority medium complexity testing pending testing specialist Tier 7

Acceptance Criteria

Integration test file covers all 7 lifecycle scenarios: initial load, Realtime INSERT ingestion, mark-read optimistic update, mark-all-read, rollback on failure, filter application, and clear-all
Initial load test verifies NotificationBloc transitions from NotificationInitial → NotificationLoading → NotificationLoaded with correct notification list and unread count
Realtime INSERT test uses a fake StreamController to push a new notification and verifies it is appended to state after role filter passes
Realtime INSERT test verifies notifications that fail role filter are NOT added to state
Mark-read test verifies optimistic state is emitted before the mock repository resolves
Mark-read test verifies unread count decrements correctly on success
Mark-all-read test verifies all notifications in state have is_read=true and unread count is 0 after success
Rollback test verifies state reverts to pre-mutation snapshot when repository throws
Rollback test verifies an error state or error side-effect is emitted after rollback
Filter application test verifies visibleNotifications changes to match predicate without triggering a repository fetch
Clear-all test verifies visibleNotifications is empty and unread=0 but allNotifications cache is preserved
All emitted state objects are verified as distinct instances (immutability check via != reference equality or Equatable)
All tests pass with a fake StreamController and mocked repository — no real Supabase calls

Technical Requirements

frameworks
Flutter
flutter_test
bloc_test
mocktail
apis
Supabase Realtime (faked via StreamController<Map<String, dynamic>>)
data models
NotificationBloc
NotificationEvent (all variants)
NotificationState (all variants)
NotificationRepository (mocked)
Role-Aware Notification Filter Service (mocked or real)
Notification Read State Service (mocked)
performance requirements
All bloc_test cases must complete within 10 seconds total
Fake StreamController must be properly closed in tearDown to prevent async leaks
security requirements
Tests must confirm role-based filtering is applied to Realtime INSERT events — unauthorized role notifications must be dropped

Execution Context

Execution Tier
Tier 7

Tier 7 - 84 tasks

Can start after Tier 6 completes

Implementation Notes

The key challenge is testing the optimistic update sequence. Use a Completer inside the mock repository's markRead() method: the bloc emits optimistic state before await completer.future resolves. Capture the state list in the blocTest expect and assert the optimistic state appears at index N before the confirmed state at index N+1. For Realtime INSERT simulation: expose the StreamController from a fake repository and push payload maps in the act callback.

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() helper. Each scenario is a separate blocTest call with explicit act, expect, and verify sections. Use StreamController> to simulate Supabase Realtime INSERT payloads. Use mocktail for NotificationRepository and Notification Read State Service.

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%.

Component
Notification BLoC
service high
Epic Risks (3)
medium impact medium prob technical

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.

medium impact medium prob technical

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.

medium impact low prob scope

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.