Write integration tests for trigger engine scenario evaluators
epic-scenario-push-engagement-core-engine-task-014 — Write integration tests covering all three scenario evaluators (inactivity, milestone, certification expiry) in the trigger engine. Tests must verify: correct trigger firing at threshold boundaries, suppression when user has opted out, suppression during active cooldown periods, and accurate notification record creation. Use a test Supabase project or local emulator with seeded data.
Acceptance Criteria
Technical Requirements
Execution Context
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.
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.
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.
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.