critical priority high complexity testing pending testing specialist Tier 7

Acceptance Criteria

Inactivity evaluator fires a trigger exactly at the configured inactivity threshold (e.g., 14 days) and does NOT fire one day before threshold
Milestone evaluator fires when a peer mentor reaches the configured milestone count and does NOT fire for counts below threshold
Certification expiry evaluator fires when expiry date is within the configured warning window and does NOT fire outside that window
All three evaluators suppress notification dispatch when the user has opted_in = false in notification_preferences
All three evaluators suppress notification dispatch when a cooldown record exists and last_triggered_at is within the cooldown window
Each successful evaluator run inserts exactly one record into scenario_notification_records with correct user_id, scenario_type, sent_at, and payload_json
No duplicate notification records are created when the same evaluator runs twice within the cooldown period
Tests run against a seeded test Supabase project or local emulator — no production data
All test cases pass consistently across three consecutive runs (no flakiness)
Test suite execution completes within 60 seconds

Technical Requirements

frameworks
flutter_test
Dart test package
Supabase Dart client
apis
Supabase REST API
Supabase Auth (service role key for test setup)
data models
notification_preferences (user_id, scenario_type, opted_in)
scenario_notification_records (user_id, scenario_type, sent_at, delivery_status, payload_json)
scenario_notification_cooldowns (user_id, scenario_type, last_triggered_at)
peer_mentors (activity timestamps, milestone counts, certification_expiry_date)
performance requirements
Full integration test suite completes in under 60 seconds
Each individual test case completes in under 5 seconds
Test teardown must clean up all seeded rows to prevent cross-test contamination
security requirements
Test environment must use a dedicated Supabase project or local emulator — never the production project
Service role key used for test setup/teardown must not be committed to source control — use environment variables
Test data must not include real user PII — use generated UUIDs and synthetic data

Execution Context

Execution Tier
Tier 7

Tier 7 - 84 tasks

Can start after Tier 6 completes

Implementation Notes

Use a shared TestSupabaseFixture helper that initializes the Supabase client with the test project URL and service role key from environment variables. Seed data using direct Supabase inserts in setUp — do not call application-layer repository methods as those may have side effects. For boundary testing, use a clock injection pattern: pass a DateTime parameter (or a clock abstraction) to each evaluator so tests can control 'now' without sleeping. The cooldown suppression test must first seed a cooldown record with last_triggered_at = DateTime.now().subtract(Duration(hours: 1)) (within window), then assert no new notification record is created.

Ensure each test calls supabase.from('scenario_notification_records').delete().eq('user_id', testUserId) in tearDown to prevent record bleed between tests. Tag all tests with @Tags(['integration']) so they can be excluded from fast unit test runs in CI.

Testing Requirements

Integration tests only — no unit mocks for database layer; all assertions must run against a real (test) Supabase instance. Organize tests in three groups: InactivityEvaluatorTest, MilestoneEvaluatorTest, CertificationExpiryEvaluatorTest. Each group must contain: (1) fires-at-threshold boundary test, (2) does-not-fire-before-threshold test, (3) suppressed-when-opted-out test, (4) suppressed-during-cooldown test, (5) record-created-on-fire test. Use setUp/tearDown hooks to seed and wipe test data per test.

Assert database state directly via Supabase client after evaluator invocation, not just return values. Total minimum: 15 test cases across three evaluators.

Component
Scenario Trigger Engine
service high
Epic Risks (3)
high impact medium prob technical

The scenario-edge-function-scheduler must evaluate all active peer mentors within the 30-second Supabase Edge Function timeout. For large organisations, a sequential evaluation loop may exceed this limit, causing partial runs and missed notifications.

Mitigation & Contingency

Mitigation: Design the trigger engine to batch mentor evaluations using database-side SQL queries (bulk inactivity check via a single query rather than per-mentor calls), and add a performance test against 500 mentors during development. Document the evaluated mentor count per scenario type in scenario-evaluation-config to allow selective scenario execution per run.

Contingency: If single-run execution is insufficient, split evaluation into per-scenario-type scheduled functions (inactivity check, milestone check, expiry check) on separate cron schedules, dividing the computational load across multiple invocations.

high impact low prob technical

A race condition between concurrent scheduler invocations or retried cron triggers could cause the same scenario notification to be dispatched multiple times to a mentor, severely degrading trust in the feature.

Mitigation & Contingency

Mitigation: Implement cooldown enforcement using a database-level upsert with a unique constraint on (user_id, scenario_type, cooldown_window_start) so that a second invocation within the same window is rejected at the persistence layer rather than the application layer.

Contingency: Add an idempotency key derived from (user_id, scenario_type, evaluation_date) to the notification record insert; if a duplicate key violation is caught, log it as a warning and skip dispatch without error.

medium impact medium prob integration

The trigger engine queries peer mentor activity history across potentially multiple organisations and chapters. RLS policies configured for app-user roles may block the Edge Function's service-role queries, or query performance may degrade on large activity tables.

Mitigation & Contingency

Mitigation: Confirm the Edge Function runs with the Supabase service role key (bypassing RLS) and add composite indexes on (user_id, activity_date) to the activity tables before implementing the inactivity detection query.

Contingency: If service-role access is restricted by organisational policy, implement a dedicated database function (SECURITY DEFINER) that performs the inactivity aggregation and is callable by the Edge Function with limited scope.