import { randomUUID } from 'node:crypto'; import { afterEach, describe, expect, it } from 'vitest'; import { db } from '../src/db/knex.js'; import { runReminders, selectDrafts, selectSurveys } from '../src/services/reminders.js'; import { truncateTables } from './truncate.js'; const day = 24 * 60 * 60 * 1000; /** * @param {number} days * @param {Date} [from] */ function daysAgo(days, from = new Date()) { return new Date(from.getTime() - days * day); } describe('reminder selection', () => { const now = new Date('2026-09-30T10:00:00.000Z'); it('selects a draft untouched for 3 days and skips recent, maxed, and rejected rows', () => { const applications = [ { id: 1, status: 'DRAFT', updated_at: daysAgo(3, now) }, { id: 2, status: 'DRAFT', updated_at: daysAgo(4, now) }, { id: 3, status: 'DRAFT', updated_at: daysAgo(10, now) }, { id: 4, status: 'REJECTED', updated_at: daysAgo(10, now) }, { id: 5, status: 'SUBMITTED', updated_at: daysAgo(10, now) }, ]; const reminders = new Map([ [2, [{ status: 'logged', created_at: daysAgo(1, now) }]], [ 3, [ { status: 'logged', created_at: daysAgo(9, now) }, { status: 'logged', created_at: daysAgo(6, now) }, { status: 'logged', created_at: daysAgo(3, now) }, ], ], ]); const selected = selectDrafts(applications, reminders, now, 3).map((row) => row.id); expect(selected).toEqual([1]); }); it('selects an unfinished approved survey after 5 days and skips a completed one', () => { const applications = [ { id: 1, status: 'APPROVED', decided_at: daysAgo(5, now) }, { id: 2, status: 'APPROVED', decided_at: daysAgo(8, now) }, ]; const selected = selectSurveys(applications, new Set([2]), new Map(), now, 5).map( (row) => row.id, ); expect(selected).toEqual([1]); }); }); describe('reminder delivery', () => { afterEach(async () => { await truncateTables(); }); it('records one reminder and does not send it again the same day', async () => { const [id] = await db('applications').insert({ public_id: randomUUID(), email: 'draft@example.com', status: 'DRAFT', full_name: 'Asha Rao', version: 1, }); await db('applications') .where({ id }) .update({ updated_at: daysAgo(4) }); await runReminders(); await runReminders(); const emails = await db('email_log').where({ application_id: id, template: 'reminder_draft' }); expect(emails).toHaveLength(1); expect(emails[0].reminder_count).toBe(1); expect(emails[0].status).not.toBe('failed'); }); });