high priority high complexity testing pending testing specialist Tier 5

Acceptance Criteria

Test suite seeds 4 test mentors with certifications expiring exactly 30, 14, 7, and 0 days from the mocked 'current date'
After running CertificationExpiryChecker: mentor at 30d receives a 30-day reminder notification; no status change
After running CertificationExpiryChecker: mentor at 14d receives a 14-day reminder notification; no status change
After running CertificationExpiryChecker: mentor at 7d receives a 7-day reminder notification; no status change
After running CertificationExpiryChecker: mentor at 0d (today) has status transitioned to 'expired_cert'; receives an expiry notification
A 5th mentor seeded with certification already expired 1 day ago and status already 'expired_cert' is NOT re-notified or re-transitioned (idempotency check)
Running the checker a second time on the same dataset produces no duplicate notifications and no duplicate status transitions (full idempotency)
Audit log entries (from task-009 equivalent for expiry checker) are written for each notification dispatched and each status transition performed
Test harness uses a dedicated Supabase test project with a clean schema; test data is seeded and torn down per test run
A mentor in 'paused' status with an expiring certification does NOT receive expiry reminders (paused mentors are handled separately)
All tests pass in CI without real FCM or BankID credentials — FCM dispatch is mocked at the Edge Function boundary

Technical Requirements

frameworks
flutter_test
Supabase test instance (dedicated project)
Dart test fixtures/helpers for seeding certification data
apis
Supabase PostgreSQL (test instance)
Supabase Edge Functions (CertificationExpiryChecker function)
FCM mock (HTTP mock server or stub)
data models
certification
assignment
device_token
performance requirements
Full integration test suite must complete within 3 minutes
Each individual test case must complete within 30 seconds including database round-trips
security requirements
Test database must be a completely isolated Supabase test project — never run tests against production or staging
No real personnummer, names, or contact details in test seed data — use clearly synthetic values (e.g., 'Test Mentor 30d')
Test project credentials stored in CI secrets, not in source code

Execution Context

Execution Tier
Tier 5

Tier 5 - 253 tasks

Can start after Tier 4 completes

Implementation Notes

The most critical design decision for these tests is clock injection — CertificationExpiryChecker must accept a configurable 'reference date' (via an env var or function input parameter) so tests can simulate arbitrary dates without waiting or manipulating system time. This should be designed in during implementation of the checker itself (task-005). For seeding, write a Dart helper class TestCertificationSeeder with typed seed methods (seedMentorExpiringInDays(int days)) to keep test bodies readable. Run tests against a dedicated Supabase test project provisioned in CI — use Supabase CLI (supabase start) or a pre-provisioned test project.

Mock FCM by intercepting the Edge Function's outbound HTTP call with a WireMock container or by injecting a stub notification service. Assert notification dispatch by querying a notifications_log table that the checker writes to, rather than verifying actual FCM delivery.

Testing Requirements

Integration tests using flutter_test and direct Supabase client calls. Structure: setUp() seeds all test mentors and certifications with clock-mocked expiry dates; test() invokes CertificationExpiryChecker Edge Function via HTTP; assertions query Supabase database for resulting status values, notification_log rows, and audit_log rows. Use a date injection mechanism (environment variable or function parameter) to control 'today' in the checker without system clock manipulation. Teardown drops all seeded rows after each test.

Include negative tests: certification expiring in 31 days (no notification expected), mentor with no certification (no error expected).

Component
Certification Expiry Checker Service
service high
Epic Risks (4)
high impact medium prob technical

The nightly expiry checker may run multiple times due to scheduler retries or infrastructure issues, causing duplicate auto-transitions and duplicate coordinator notifications that erode trust in the notification system.

Mitigation & Contingency

Mitigation: Implement idempotency via a unique constraint on (mentor_id, threshold_day, certification_expiry_date) in the cert_expiry_reminders table. Auto-transitions should be wrapped in a Postgres RPC that checks current status before applying, making repeated invocations safe.

Contingency: Add a compensation query in the reconciliation log that detects duplicate log entries for the same certification period and alerts the operations team for manual review within 24 hours.

high impact medium prob integration

The HLF Dynamics portal API may have eventual-consistency behaviour or rate limits that cause website listing updates to lag behind status changes, leaving expired mentors visible on the public website for an unacceptable window.

Mitigation & Contingency

Mitigation: Design the sync service to be triggered immediately on status transitions (event-driven via database webhook) in addition to the nightly batch run. Implement a reconciliation job that verifies sync state against app state and re-triggers any divergent records.

Contingency: If real-time sync cannot be guaranteed, implement a manual 'force sync' action in the coordinator dashboard so coordinators can trigger an immediate re-sync for urgent cases. Document the expected sync lag in coordinator onboarding materials.

medium impact medium prob scope

Stakeholder requests to extend the expiry checker to handle additional certification types, grace periods, or organisation-specific threshold configurations may significantly increase scope beyond what is designed here, delaying delivery.

Mitigation & Contingency

Mitigation: Parameterise threshold day values (30, 14, 7) via configuration repository rather than hard-coding them, enabling per-organisation customisation without code changes. Document that grace period logic and additional cert types are out of scope for this epic and require a dedicated follow-up.

Contingency: Deliver the feature with hard-coded HLF-standard thresholds first and introduce the configuration repository as a follow-up task in the next sprint, using a feature flag to enable per-org threshold overrides.

high impact low prob security

Dynamics portal API credentials stored as environment secrets in Supabase Edge Function configuration may be rotated or invalidated by HLF IT without notice, causing silent sync failures that go undetected for multiple days.

Mitigation & Contingency

Mitigation: Implement credential health-check calls on each scheduler run and emit an immediate alert on auth failure rather than only alerting after N consecutive failures. Document the credential rotation procedure with HLF IT and establish a rotation notification protocol.

Contingency: Maintain a break-glass manual sync script accessible to HLF administrators that can re-execute the Dynamics sync with newly provided credentials while the automated system is restored.