critical priority high complexity testing pending testing specialist Tier 5

Acceptance Criteria

FCM token registration test: token is written to fcm_tokens table in Supabase test project and can be retrieved
Dispatch test (foreground): triggering a new_assignment event via NotificationTriggerService results in an FCM message received by the foreground Flutter client within 10 seconds
Dispatch test (background): the same trigger delivers the notification when the app is backgrounded and a notification banner appears
Deep link test: tapping the foreground/background notification navigates to the correct assignment detail screen as resolved by NotificationDeepLinkHandler
Persistence test: after notification receipt in all three states, NotificationRepository contains exactly one record per notification_id (idempotency verified)
Preference filtering test: user with opted-out new_assignment preference does not receive FCM dispatch; dispatch count matches number of opted-in users only
Role-aware payload test: peer mentor receives payload with assignment_id; coordinator receives payload with chapter_id and batch summary fields
Cold-start test: app terminated, notification tapped, app launches and navigates directly to the correct screen
All tests run against an isolated Supabase test project (not production) and use dedicated test FCM device tokens
Test suite completes in under 15 minutes on CI

Technical Requirements

frameworks
flutter_test
integration_test package
flutter_driver (if needed)
Supabase Dart client
apis
Supabase REST API (all pipeline tables)
Firebase Cloud Messaging HTTP v1 API (trigger dispatch)
FirebaseMessaging.instance (client receipt)
data models
FCMToken
NotificationRecord
NotificationPreference
Assignment
UserRole
performance requirements
FCM delivery from trigger to client receipt must occur within 10 seconds in test environment
Full test suite must complete within 15 minutes to be viable in CI pipeline
Supabase test project must be seeded with test data in under 30 seconds before tests run
security requirements
Test FCM tokens must be dedicated test device tokens — never production user tokens
Supabase test project must use separate credentials from production — stored in CI environment variables only
Test data must be cleaned up after each test run to prevent cross-test contamination
ui components
AssignmentDetailScreen (navigation target)
NotificationCenterScreen (persistence verification)

Execution Context

Execution Tier
Tier 5

Tier 5 - 253 tasks

Can start after Tier 4 completes

Implementation Notes

Structure the test suite with a shared TestEnvironment helper class that initializes Supabase with test credentials, creates required test users with assigned roles, seeds FCM tokens, and tears down after completion. Use a test_helpers/notification_test_data.dart file for all seeded data to keep test files clean. For the cold-start test, use flutter_driver to terminate the app, then launch it via a notification click simulation — document that this requires manual steps if full automation is not feasible on CI. Use a dedicated FCM test sender utility (a simple Dart script calling the FCM HTTP v1 API with a service account) to trigger dispatches from within the test suite rather than relying on Supabase triggers during testing.

Annotate flaky async timing tests with a configurable timeout and retry mechanism.

Testing Requirements

Integration test suite (flutter integration_test package) organized into test groups: (1) Token Registration, (2) Foreground Dispatch & Receipt, (3) Background Dispatch & Receipt, (4) Cold-Start Deep Link, (5) Notification Persistence & Idempotency, (6) Preference Filtering. Each group has setup/teardown that seeds and cleans Supabase test data. Use flutter drive or a CI-compatible test runner. Physical device or emulator with Google Play Services required — pure Dart tests cannot test FCM delivery.

Document required test device setup in the test file header. TestFlight device (iOS) must be used for the cold-start scenario.

Component
Push Notification Service
infrastructure medium
Dependencies (5)
Extend NotificationTriggerService to query notification_preferences before each FCM dispatch. If a user has opted out of a specific category (e.g., deadline_approaching), skip the FCM send for that user entirely. Implement batch filtering for coordinator broadcasts to only include users with active preferences. Log skipped dispatches for observability without failing the overall trigger invocation. epic-push-notification-delivery-engine-task-013 Extend PushNotificationService to persist each received notification (foreground, background, and terminated) to the NotificationRepository so the Notification Center can display history. Write notification_id, category, title, body, payload, and received_at for all three app states. Implement idempotency check to prevent duplicates when the same message is processed by multiple handlers. epic-push-notification-delivery-engine-task-012 Create the NotificationTriggerService Supabase Edge Function that listens to database change events (INSERT on assignments, UPDATE on assignment_status, deadline proximity via scheduled cron) using Supabase realtime or pg_cron. Evaluates per-user per-category preferences from notification_preferences table before dispatching. Constructs role-aware FCM payloads with data fields for deep link route resolution and sends via FCM HTTP v1 API using a service account credential. epic-push-notification-delivery-engine-task-009 Extend PushNotificationService to display local notifications when the app is in the foreground using flutter_local_notifications with configured Android and iOS channel settings. Map incoming FCM RemoteMessage to a LocalNotification with title, body, and payload. Ensure accessibility labels from NotificationAccessibilityConfiguration are applied and that the displayed notification triggers the deep link handler on tap. epic-push-notification-delivery-engine-task-008 Extend NotificationDeepLinkHandler to handle cold-start navigation where the app was terminated when the user tapped the notification. Use FirebaseMessaging.instance.getInitialMessage() during app initialization to capture the launch payload, validate the target resource exists, then push the resolved route after the router finishes initial navigation. Ensure no race condition with auth state loading. epic-push-notification-delivery-engine-task-011
Epic Risks (4)
high impact high prob technical

Flutter's background message handler for FCM must run in a separate Dart isolate. Incorrect dependency initialization in the isolate (e.g., attempting to access Riverpod providers or Supabase before initialization) will cause silent crashes on Android when the app is terminated, resulting in missed notifications that are invisible in crash reporting.

Mitigation & Contingency

Mitigation: Use a minimal top-level background handler function annotated with @pragma('vm:entry-point') that only stores the raw RemoteMessage payload to a platform channel or shared preferences. Process the payload in the main isolate on next app launch. Write an explicit test for terminated-state message handling on Android.

Contingency: If isolate crashes are observed, implement a native Android FirebaseMessagingService subclass that handles background messages without Flutter isolate complexity, falling back to a database-insert-only approach for terminated-state notifications.

medium impact medium prob technical

Supabase Edge Functions can experience cold-start latency of 1–3 seconds after periods of inactivity. For high-frequency events like assignment creation, cumulative cold starts could cause dispatch delays exceeding the 30-second SLA, reducing the perceived reliability of the notification system.

Mitigation & Contingency

Mitigation: Configure the Edge Function with a keep-warm ping mechanism or use Supabase database webhooks that invoke the function directly on row insert to minimize cold-start frequency. Batch preference lookups within the function to reduce per-invocation Supabase round-trips.

Contingency: If latency SLA is consistently breached, move to a polling or Realtime-subscription architecture within the Edge Function, or pre-compute dispatch targets at preference-save time to eliminate per-dispatch preference queries.

high impact low prob security

If the deep link handler does not perform server-side role validation before rendering the target screen, a peer mentor who receives a mis-configured notification payload containing a coordinator-only route could access restricted data, violating the role-based access control invariants.

Mitigation & Contingency

Mitigation: The deep link handler must check the user's current role from the RoleStateManager before constructing the navigation route. Coordinator-only routes must be listed in a deny-list checked against the current role. The go_router route guard is a second line of defence.

Contingency: If a role bypass is discovered in testing, immediately add the affected route to the deep link handler deny-list and add a regression test. Audit all notification payload types for route targets that could expose cross-role data.

medium impact low prob dependency

FCM v1 HTTP API enforces per-project send quotas. For large organisations with many active peer mentors receiving simultaneous assignment notifications, batch dispatch events (e.g., bulk coordinator assignments) could approach quota limits and result in dropped notifications with 429 errors logged silently.

Mitigation & Contingency

Mitigation: Implement exponential backoff retry logic in the Edge Function for 429 responses. Design bulk assignment flows to dispatch notifications in batches with a configurable delay between batches. Monitor FCM console quotas during load testing.

Contingency: If quota limits are hit, implement a notification queue table in Supabase and a separate Edge Function that processes the queue with rate limiting, ensuring eventual delivery without exceeding FCM quotas.