high priority medium complexity testing pending testing specialist Tier 4

Acceptance Criteria

All tests are written using Deno's built-in `Deno.test` and `std/testing/asserts` — no external test framework required
ThresholdCriteriaEvaluator: test cases include count exactly at threshold (pass), count one below threshold (fail), count of zero (fail), count far above threshold (pass), Blindeforbundet 3rd honorar threshold boundary, Blindeforbundet 15th honorar threshold boundary
StreakCriteriaEvaluator: test cases include current streak exactly meets required days (pass), current streak one day short (fail), activities on same calendar day count as 1 streak day, activities in non-chronological order still produce correct streak, zero activities produces streak of 0 (fail), longest-streak variant tested if supported
TrainingCompletionCriteriaEvaluator: test cases include completed certification matching required course ID (pass), no certifications at all (fail), certification exists but status is 'expired' (fail), certification exists but for a different course ID (fail), multiple certifications where required one is present (pass)
All tests are pure unit tests — no Supabase connection required, stats objects constructed inline
Test file is co-located with the evaluator source files in the edge function directory
All tests pass with `deno test` in the edge function root
Test names are descriptive enough to serve as living documentation of evaluator behaviour
Total test suite execution time is under 2 seconds (pure in-memory logic)
Code coverage for all three evaluator classes is 100% branch coverage

Technical Requirements

frameworks
Deno runtime
Deno std/testing/asserts
Deno std/testing/mock (for any mock needs)
data models
PeerMentorStats (inline test fixtures)
BadgeDefinition (inline test fixtures)
performance requirements
Full test suite must complete in under 2 seconds — all tests are pure functions with no I/O
security requirements
Test fixtures must not contain real peer mentor IDs or real certification data — use synthetic UUIDs and course codes

Execution Context

Execution Tier
Tier 4

Tier 4 - 323 tasks

Can start after Tier 3 completes

Implementation Notes

Structure test file as three `describe`-equivalent grouped blocks (use `Deno.test` with descriptive names like `'ThresholdEvaluator: exactly at threshold returns true'`). Build a `buildStats(overrides: Partial)` helper factory at the top of the test file to reduce boilerplate. Build a `buildDefinition(overrides: Partial)` helper similarly. For Blindeforbundet's 3rd and 15th honorar thresholds, write named test cases that explicitly call out the business context in the test name (e.g., `'ThresholdEvaluator: Blindeforbundet RK office honorar — awards at 3rd honorar'`).

This makes the tests valuable as business rule documentation. Since evaluators are pure functions, no mocking is needed — just construct input structs and assert return values.

Testing Requirements

This task IS the testing deliverable. Acceptance is based on: all test assertions pass, all three evaluator classes reach 100% branch coverage, test descriptions serve as readable specification of evaluator contracts. Run with: `deno test supabase/functions/badge-evaluation-service/evaluators_test.ts`. Optionally generate a coverage report with `deno test --coverage=coverage_dir` and `deno coverage coverage_dir` to confirm branch coverage.

Component
Badge Evaluation Service
service high
Epic Risks (3)
medium impact medium prob technical

Supabase Edge Functions may experience cold start latency of 500ms–2s when they have not been invoked recently. If evaluation latency consistently exceeds the 2-second UI expectation, the celebration overlay timing SLA cannot be met without the optimistic UI fallback from the UI epic.

Mitigation & Contingency

Mitigation: Keep the edge function warm by scheduling a lightweight health-check invocation every 5 minutes in production. Optimise the function size to minimise Deno module load time. Implement the optimistic UI path in badge-bloc (from the UI epic) as the primary UX path so cold start only affects server-side reconciliation, not perceived responsiveness.

Contingency: If cold starts remain problematic, migrate badge evaluation to a Supabase database function (pl/pgsql) triggered directly by a database trigger on activity insert, eliminating the Edge Function overhead entirely for the evaluation logic while keeping Edge Function only for FCM notification dispatch.

high impact low prob integration

Supabase database webhooks can fail silently if the edge function returns a non-2xx response or times out. A missed webhook means a peer mentor does not receive a badge they earned, which is both a functional defect and a trust issue for organisations relying on milestone tracking.

Mitigation & Contingency

Mitigation: Implement idempotent webhook processing: the edge function reads the activity ID from the webhook payload and checks whether evaluation for this activity has already run (via an audit log query) before proceeding. Add Supabase webhook retry configuration (3 retries with exponential backoff). Monitor webhook failure rates via Supabase logs alert.

Contingency: Implement a nightly reconciliation job (Supabase scheduled function) that scans all activities from the past 24 hours, re-evaluates badge criteria for any peer mentor with no corresponding evaluation log entry, and awards any missing badges. Alert operations if reconciliation awards more than 5% of badges, indicating systematic webhook failure.

high impact low prob security

The evaluation service loads badge definitions per organisation, but a misconfigured RLS policy or incorrect organisation scoping in the edge function could cause one organisation's badge criteria to be evaluated against another organisation's peer mentor activity data, leading to incorrect or cross-contaminated badge awards.

Mitigation & Contingency

Mitigation: The edge function must extract organisation_id from the webhook payload activity record and pass it explicitly to every database query. Write a security test that seeds two organisations with distinct badge definitions and verifies that evaluating a peer mentor in org A never reads or awards org B definitions. Use Supabase service role key only within the edge function, never the anon key.

Contingency: If cross-org contamination is detected in audit logs, immediately disable the edge function webhook, run a targeted SQL query to identify and revoke incorrectly awarded badges, notify affected organisations, and perform a full security review of all RLS policies on badge-related tables before re-enabling.