high priority medium complexity testing pending testing specialist Tier 4

Acceptance Criteria

All tests pass with flutter test with zero failures and zero skips
Test suite covers the following scenarios as individual named test cases: successful_push_and_inapp, push_success_inapp_failure, push_failure_inapp_success, both_channels_fail_dead_letter, invalid_token_rejected_no_http_call, retry_exhaustion_returns_dead_letter, retry_succeeds_on_second_attempt, duplicate_event_skips_insert
MockFcmHttpClient is implemented as a fake (not a generated mock) that supports per-call response sequencing (e.g. fail twice then succeed)
MockSupabaseAdapter captures inserted NotificationRecord arguments for assertion
No test uses real HTTP connections, real Supabase connections, or real FCM tokens
Tests are deterministic: no real timers; FakeAsync controls all delay-dependent paths
Line coverage of FcmNotificationDispatcher class is ≥ 90% as reported by flutter test --coverage
Branch coverage of DispatchResult handling is 100% (all sealed class subclasses exercised)
Test file is colocated with the implementation file in test/ mirroring the lib/ structure

Technical Requirements

frameworks
flutter_test
Flutter
Dart
async (FakeAsync)
data models
FcmDispatchPayload
DispatchResult
NotificationRecord
RetryConfig
performance requirements
Full test suite must complete in under 10 seconds (no real network or sleep calls)
security requirements
No real FCM tokens or Supabase credentials in test code or test fixtures
Test fixtures use clearly fake token strings (e.g. 'fake-fcm-token-for-testing')

Execution Context

Execution Tier
Tier 4

Tier 4 - 323 tasks

Can start after Tier 3 completes

Implementation Notes

Build MockFcmHttpClient as a class with a Queue of pre-programmed responses — dequeue one per call. This is more reliable than generated mocks for sequencing multi-attempt retry scenarios. Build MockSupabaseAdapter similarly with a List insertedRecords for assertion. Use FakeAsync.run() wrapping async tests that involve retry delays — this is the only way to test backoff without sleeping.

Use expect() with typed matchers: expect(result, isA()); expect((result as DispatchResult.deadLetter).attemptCount, equals(3)). Group arrange/act/assert clearly in each test. Add a test helper buildDispatcher({required MockFcmHttpClient fcmClient, required MockSupabaseAdapter supabase, RetryConfig? retryConfig}) to reduce boilerplate across tests.

Testing Requirements

This task IS the testing task. All tests are unit tests using flutter_test. Use FakeAsync for retry delay simulation. Structure tests in group() blocks by scenario category: 'FCM dispatch channel', 'In-app channel', 'Retry logic', 'Integration (coordinator)'.

Each test uses setUp() to construct a fresh dispatcher instance with injected mocks. Assert on both the returned DispatchResult and side effects (mock call counts, captured arguments). Generate coverage report with flutter test --coverage and verify thresholds before marking task complete.

Component
FCM Notification Dispatcher
infrastructure medium
Epic Risks (3)
high impact medium prob integration

The org membership table structure used to resolve coordinator relationships may differ from what the repository assumes, causing incorrect coordinator lookup or missing rows for mentors in multi-chapter scenarios.

Mitigation & Contingency

Mitigation: Review the existing org membership table schema and RLS policies before writing repository queries. Align query logic with the patterns already used by peer-mentor-status-repository and multi-chapter-membership-service.

Contingency: If schema differs, add an adapter layer in the repository that normalises the membership resolution and document the discrepancy for the data team. Fall back to coordinator lookup via the feature's own stored coordinator_id field if org membership join fails.

high impact medium prob technical

Device tokens stored in the database may be stale or unregistered, causing FCM dispatch failures that silently drop coordinator notifications — the primary coordination safeguard of this feature.

Mitigation & Contingency

Mitigation: Implement token validation on every dispatch call and handle FCM's NOT_REGISTERED error by flagging the token as invalid in the database. Reuse the token refresh pattern already established by fcm-token-manager.

Contingency: If push delivery fails after retry, ensure the in-app notification record is always written regardless of push outcome so coordinators can still see the event in the notification centre.

medium impact low prob technical

The optional reason field may contain special characters, emoji, or non-Latin scripts that exceed the 200-character byte limit when FCM encodes the payload, causing delivery failures.

Mitigation & Contingency

Mitigation: Enforce the 200-character limit on Unicode code point count, not byte count, in the payload builder. Add a unit test with multi-byte input strings.

Contingency: If an oversized payload is detected at dispatch time, strip the reason field from the push notification body and note 'See in-app notification for full reason' to preserve delivery.