high priority medium complexity testing pending testing specialist Tier 3

Acceptance Criteria

Test passes: successful FCM HTTP 200 response results in ScenarioNotificationRepository.recordDeliveryStatus() called with DeliveryStatus.DELIVERED and the correct notification record ID
Test passes: first FCM call returns a transient error (HTTP 500 or network timeout), dispatcher retries at least once with a measurable backoff delay before succeeding on the second attempt
Test passes: all retry attempts are exhausted (configured max retries reached with transient errors) and ScenarioNotificationRepository.recordDeliveryStatus() is called with DeliveryStatus.FAILED
Test passes: FCM returns a permanent error code (e.g., InvalidRegistration, NotRegistered) and dispatcher does NOT retry — status is recorded as FAILED immediately
Test passes: repository recordDeliveryStatus() is called exactly once for every dispatch attempt outcome, verified by mock verify() assertions
Test passes: FCM HTTP client throws an unhandled exception — dispatcher catches it, records FAILED status, and does not propagate the exception to the caller
All mocks are set up using mockito or mocktail — no real HTTP connections are made in any test
Backoff delay in tests uses a fake clock or injectable delay function to avoid real time.sleep() pauses
Each test case is independent — no shared mutable state between tests

Technical Requirements

frameworks
Flutter
flutter_test
mockito or mocktail
apis
FCM HTTP v1 API (mocked)
ScenarioNotificationRepository interface
data models
ScenarioNotificationRecord (id, scenarioType, recipientToken, status)
DeliveryStatus enum (DELIVERED, FAILED, PENDING)
FCMResponse (statusCode, errorCode)
performance requirements
Tests must not perform real network calls — all HTTP must be mocked
Retry backoff must be injectable/fakeable so tests run in milliseconds, not seconds
security requirements
FCM device tokens in test data must be clearly fake strings (e.g., 'fake-device-token-001')
No real FCM API keys or Supabase credentials in test files

Execution Context

Execution Tier
Tier 3

Tier 3 - 413 tasks

Can start after Tier 2 completes

Implementation Notes

PushNotificationDispatcher must accept its HTTP client and repository as constructor parameters (dependency injection) to be testable — if the current implementation uses singletons or service locators, refactor to constructor injection before writing tests. Define retry configuration (maxRetries, backoffMultiplier) as constructor parameters with sensible defaults so tests can set maxRetries: 2 for fast iteration. Distinguish transient vs. permanent FCM errors by HTTP status code: 5xx = transient (retry), 4xx with specific error codes = permanent (skip retry).

Use when(mockHttpClient.post(...)).thenAnswer() chains to simulate successive call outcomes (fail then succeed). The repository write guarantee test is the most important — use a try/finally pattern in the dispatcher so status is always written even when the overall dispatch throws.

Testing Requirements

Unit tests using flutter_test with mockito or mocktail for HTTP client and repository mocks. Create a MockHttpClient and MockScenarioNotificationRepository. Inject these via constructor injection into PushNotificationDispatcher. Structure tests in group() blocks: 'successful dispatch', 'transient failures', 'permanent failures', 'exhausted retries', 'repository write guarantee'.

Use verify() and verifyNever() to assert repository interaction count and arguments. For backoff timing, inject a FakeDelayProvider that records delays without sleeping. Aim for 100% branch coverage of the dispatcher's retry and error-handling logic. Total test suite should run in under 5 seconds.

Component
Push Notification Dispatcher
infrastructure medium
Epic Risks (3)
high impact medium prob dependency

FCM service account key and APNs certificate configuration may be missing or misconfigured in the Supabase Edge Function secrets store, blocking end-to-end push delivery testing until resolved by the infrastructure owner.

Mitigation & Contingency

Mitigation: Raise a credentials-setup task in the project board at epic start; document the exact secret names required in scenario-evaluation-config so the infrastructure owner can provision them independently of development work.

Contingency: Implement a mock push-notification-dispatcher stub that records payloads to the database for local testing, allowing the rest of the feature to proceed while credentials are obtained.

high impact low prob security

Incorrect RLS policies on the scenario_notifications or notification_preferences tables could allow one user to read or modify another user's notification records, constituting a data privacy breach.

Mitigation & Contingency

Mitigation: Write dedicated RLS policy tests using Supabase's built-in test framework before any application code touches the tables; require a peer security review of all policy definitions before merging.

Contingency: If a policy gap is discovered post-merge, immediately disable the affected table's read policy, notify the security lead, and deploy a hotfix with corrected policies before re-enabling access.

medium impact medium prob dependency

Norwegian Bokmål ARB localisation strings for all scenario message templates may not be available at implementation time, causing content-builder tests to fail and delaying integration.

Mitigation & Contingency

Mitigation: Define all required ARB message keys in a tracked document shared with the content owner at epic kickoff; use English placeholder strings that follow the final format so template injection logic can be tested independently.

Contingency: Ship with English-only strings in the first release and gate Norwegian strings behind a feature flag that is enabled once translations are reviewed and approved.