Unit tests for PushNotificationService
epic-assignment-follow-up-reminders-foundation-task-011 — Write unit tests for the PushNotificationService wrapper covering: sendToUser happy path, sendToTopic happy path, FCM token not found handling, platform channel error handling, and mock stub verification. Verify the service correctly delegates to FCM token manager and does not expose raw FCM internals to callers.
Acceptance Criteria
Technical Requirements
Execution Context
Tier 1 - 540 tasks
Can start after Tier 0 completes
Implementation Notes
The abstraction boundary is the most architecturally significant aspect of these tests. If FirebasePushNotificationService is implemented correctly, its unit tests should not require firebase_messaging to be initialized (which requires a real app context). All Firebase calls should be behind a thin FcmDispatcher interface that is mockable. If the implementation does not have this abstraction, the tests will fail to run in a pure Dart unit test environment — this is a useful design pressure.
For the MockPushNotificationService self-test (last acceptance criterion), ensure the mock's capturedCalls list is reset in setUp() to prevent test ordering dependencies.
Testing Requirements
Unit tests only. The key test here is the interface boundary test — confirm no FCM-specific types leak through the PushNotificationService API. This is best verified by ensuring the test file itself has zero imports of firebase_messaging or firebase_core; if those imports are needed to set up mocks, the abstraction is leaking. Use a MockFcmTokenManager stub to control token resolution behavior.
For the PlatformException wrapping test, throw a PlatformException from the mock dispatch method and assert the caught exception is PushNotificationException with cause set.
Adding last_contact_date to the assignments table may conflict with existing RLS policies or trigger-based logic that monitors the assignments table. If the migration is not carefully reviewed, existing assignment management features could break in production.
Mitigation & Contingency
Mitigation: Review all existing triggers, policies, and foreign key constraints on the assignments table before writing the migration. Run the migration against a staging Supabase instance with production-like data and execute the full existing test suite before merging.
Contingency: Roll back the migration using Supabase's versioned migration history. Apply the schema change as an additive-only migration (nullable column with default) to ensure zero downtime and reversibility.
The PushNotificationService wraps an existing FCM integration whose internal API contract may have changed or may not expose the payload formatting required for deep-link CTAs. Misalignment discovered late delays the dispatch service epic.
Mitigation & Contingency
Mitigation: Before implementing the wrapper, read the existing push notification integration code and confirm the method signatures, payload structure, and token management model. Agree on a stable interface contract in a shared Dart abstract class.
Contingency: If the existing service is incompatible, implement a thin adapter layer that translates reminder payloads to the existing service's format, isolating the reminder feature from upstream changes.
Incorrect RLS policies on notification_log could allow coordinators to read reminder records belonging to peer mentors in other chapters, exposing sensitive assignment information across organisational boundaries.
Mitigation & Contingency
Mitigation: Write explicit RLS policies with integration tests that assert cross-chapter queries return zero rows. Use Supabase's built-in auth.uid() and join through the org membership tables to scope all queries.
Contingency: If a policy gap is discovered post-merge, immediately disable the affected table's SELECT policy, deploy a corrected policy, and audit recent queries in Supabase logs for any cross-boundary reads.