Implement Expiry Window Query Logic
epic-certification-management-automation-task-002 — Write the database query logic inside the cron function to identify certifications expiring in 30, 14, and 7 days from the current date, as well as certifications that expired exactly on the current date. Use parameterised date arithmetic against the certifications table with proper index usage. Return structured result sets grouped by expiry window.
Acceptance Criteria
Technical Requirements
Execution Context
Tier 1 - 540 tasks
Can start after Tier 0 completes
Implementation Notes
Implement in supabase/functions/certification-expiry-cron/db.ts. Use the supabase-js query builder for clarity and automatic parameterisation rather than raw SQL strings. Example pattern: supabase.from('certifications').select('id, peer_mentor_id, organization_id, cert_type, expires_at').eq('status', 'active').filter('expires_at::date', 'eq', targetDateStr). If the PostgREST filter syntax does not support cast expressions, use a Supabase RPC call to a SQL function that accepts the target_date as a parameter — this is safer and more performant.
Define a CertificationRow TypeScript interface at the top of db.ts to type the return values. Use Promise.allSettled to run all four queries in parallel, reducing total latency. Log the count of results per window at INFO level for observability (e.g. console.log('[expiry-cron] 30d window: 12 certs found')).
Testing Requirements
Write Deno unit tests in supabase/functions/certification-expiry-cron/db_test.ts. Create a MockSupabaseClient that captures the query parameters passed to the .from('certifications').select().eq().filter() chain. Assert that for a given today = new Date('2026-03-26'), the 30-day query targets 2026-04-25, the 14-day query targets 2026-04-09, the 7-day query targets 2026-04-02, and the expired-today query targets 2026-03-26. Assert that status filter excludes 'paused' and 'revoked'.
Write an integration test against a local Supabase instance seeded with certifications at known expiry dates and verify the correct records appear in each result window. Run with deno test --allow-env --allow-net.
Supabase Edge Functions can have cold-start latency that causes the nightly cron to time out when processing large cohorts of expiring certifications, resulting in partial reminder dispatches.
Mitigation & Contingency
Mitigation: Batch the cron processing in chunks of 50 mentors per iteration. Use pagination with a cursor to resume processing if the function is re-invoked. Keep total invocation time well under the Edge Function timeout limit.
Contingency: If timeouts occur in production, split the cron into two separate functions: one for reminders and one for auto-pauses, each with its own schedule offset to reduce peak load.
Certification BLoC covers three distinct workflows (view, renew, enrol) which may lead to an overly complex state machine that is hard to test and maintain, particularly when error states from multiple concurrent operations need to be differentiated in the UI.
Mitigation & Contingency
Mitigation: Use separate sealed state classes per workflow (CertificationViewState, RenewalState, EnrolmentState) composed into a single BLoC state wrapper. Follow the existing BLoC patterns established in the codebase for consistency.
Contingency: If the BLoC grows too complex, split into two BLoCs: CertificationBLoC (view/load) and CertificationActionBLoC (mutations), connected via a shared stream.