critical priority medium complexity testing pending testing specialist Tier 4

Acceptance Criteria

Test suite covers all 6 mandatory scenarios: peer mentor push payload, coordinator escalation push payload, in-app persistence, idempotency block within window, idempotency allow after window expiry, PushNotificationService failure handling
All Supabase, PushNotificationService, InAppNotificationRepository, and Logger dependencies are replaced with test doubles (mocks or fakes)
Each test follows the Arrange-Act-Assert structure with no shared mutable state between tests
Idempotency within-window test asserts PushNotificationService.send was never called and DispatchResult.skipped was returned
Idempotency after-expiry test asserts PushNotificationService.send was called exactly once
Push payload tests assert required fields: recipient FCM token, notification title, body, and assignment_id in data payload
PushNotificationService failure test asserts DispatchResult.failed is returned and error is logged without rethrowing
Test file is co-located with the service file under test/services/ and named reminder_dispatch_service_test.dart
All tests pass with flutter test and produce no warnings

Technical Requirements

frameworks
Flutter
Dart
flutter_test
mockito or mocktail
data models
Assignment
ReminderContactTracking
PushPayload
InAppNotificationRecord
performance requirements
Full test suite must complete in under 10 seconds
No real network calls — all external dependencies mocked
security requirements
Test fixtures must not contain real FCM tokens, real user IDs, or real assignment data

Execution Context

Execution Tier
Tier 4

Tier 4 - 323 tasks

Can start after Tier 3 completes

Implementation Notes

Structure the test file with clearly labeled describe/group blocks: 'peer mentor dispatch', 'coordinator escalation dispatch', 'in-app persistence', 'idempotency guard', 'error handling'. Use a factory helper buildTestAssignment({...}) to create Assignment fixtures with sensible defaults, overriding only the fields relevant to each test — this keeps tests readable. For the Clock mock, use a FixedClock that returns a predetermined DateTime so window boundary assertions are deterministic. Avoid using real Supabase clients in any test in this file.

If mocktail is not yet in pubspec.yaml, add it as a dev_dependency. Verify mock call counts with verifyNever and verify(...).called(1) rather than boolean flags.

Testing Requirements

Pure unit tests using flutter_test. No widget tests or integration tests in this suite. Use mocktail (preferred for Dart null-safety) or mockito with build_runner for generating mocks. Required mock classes: MockPushNotificationService, MockInAppNotificationRepository, MockReminderContactTrackingRepository, MockReminderLogger, MockClock.

Each test group should be named after the scenario being tested. Use setUp() to reset mock state between tests. Parametrize payload composition tests with both PeerMentor and Coordinator notification type variants to avoid duplication.

Component
Reminder Dispatch Service
service medium
Epic Risks (3)
medium impact high prob scope

The idempotency window (how long after a reminder is sent before another can be sent for the same assignment) is not explicitly specified. An incorrect window — too short, duplicate reminders appear; too long, a resolved and re-opened situation is not re-notified. This ambiguity could result in user-visible bugs post-launch.

Mitigation & Contingency

Mitigation: Before implementation, define the idempotency window explicitly with stakeholders: a reminder is suppressed if a same-type notification record exists with sent_at within the last (reminder_days - 1) days. Document this rule as a named constant in the service with a comment referencing the decision.

Contingency: If the window is wrong in production, it is a single constant change with a hotfix deployment. The notification_log table allows re-processing without data migration.

high impact medium prob technical

For organisations with thousands of open assignments (e.g., NHF with 1,400 chapters), the daily scheduler query over all open assignments could time out or consume excessive Supabase compute units, especially if the contact tracking query lacks proper indexing.

Mitigation & Contingency

Mitigation: Add a composite index on assignments(status, last_contact_date) before running performance tests. Use cursor-based pagination in the scheduler (query 500 rows at a time). Run a load test with 10,000 synthetic assignments as described in the feature documentation before merging.

Contingency: If the query is too slow for synchronous execution, move the evaluation to the Edge Function (cron trigger epic) and use Supabase's built-in parallelism. The service interface does not change, only the execution context.

medium impact medium prob integration

If the push notification service fails (FCM outage, invalid device token) during dispatch, the in-app notification may already be persisted but the push is silently lost. Inconsistent state makes it impossible to report accurate delivery status.

Mitigation & Contingency

Mitigation: Implement push dispatch and in-app persistence as separate operations with independent error handling. Record delivery_status as 'pending', 'delivered', or 'failed' on the notification_log row. Retry failed push deliveries up to 3 times with exponential backoff.

Contingency: If FCM is consistently unavailable, the in-app notification is still visible to the user, providing a degraded but functional fallback. Alert on consecutive push failures via the cron trigger's error logging.