import { after, describe, it } from 'node:test'; import assert from 'node:assert/strict'; import { CampaignRecipe, MailboxRole } from '../../lib/prisma'; import { assignSatellite, createCampaign, startCampaign } from '../../services/campaign.service'; import { prisma } from '../../lib/prisma'; import { cleanupCampaign, cleanupInbox, createTestInbox, } from '../helpers/test-db'; describe('integration: recipes', () => { const campaignIds: string[] = []; const inboxIds: string[] = []; after(async () => { for (const id of campaignIds) await cleanupCampaign(id); for (const id of inboxIds) await cleanupInbox(id); }); async function setupCampaign(recipe: CampaignRecipe, extra?: Record) { const primary = await createTestInbox({ role: MailboxRole.primary }); const sat1 = await createTestInbox(); const sat2 = await createTestInbox(); inboxIds.push(primary.id, sat1.id, sat2.id); const campaign = await createCampaign({ primaryMailboxId: primary.id, name: `Test ${recipe}`, recipe, durationDays: 14, minEmailsPerDay: 1, maxEmailsPerDay: 40, ...extra, }); campaignIds.push(campaign.id); await assignSatellite(campaign.id, sat1.id); await assignSatellite(campaign.id, sat2.id); return startCampaign(campaign.id); } it('flat recipe persists constant plannedSends', async () => { const campaign = await setupCampaign(CampaignRecipe.flat); const schedules = await prisma.dailySchedule.findMany({ where: { campaignId: campaign.id }, orderBy: { dayIndex: 'asc' }, }); assert.equal(schedules.length, 14); for (const row of schedules) { assert.equal(row.plannedSends, 40); } }); it('custom recipe persists hold-last schedule', async () => { const primary = await createTestInbox({ role: MailboxRole.primary }); const sat1 = await createTestInbox(); const sat2 = await createTestInbox(); inboxIds.push(primary.id, sat1.id, sat2.id); const campaign = await createCampaign({ primaryMailboxId: primary.id, name: 'Test custom', recipe: CampaignRecipe.custom, durationDays: 14, customSchedule: [ { day: 1, sends: 2 }, { day: 2, sends: 5 }, ], }); campaignIds.push(campaign.id); await assignSatellite(campaign.id, sat1.id); await assignSatellite(campaign.id, sat2.id); const started = await startCampaign(campaign.id); const schedules = await prisma.dailySchedule.findMany({ where: { campaignId: started.id }, orderBy: { dayIndex: 'asc' }, }); assert.equal(schedules[0]!.plannedSends, 2); assert.equal(schedules[1]!.plannedSends, 5); assert.equal(schedules[13]!.plannedSends, 5); }); it('randomized recipe is deterministic per campaign id seed', async () => { const a = await setupCampaign(CampaignRecipe.randomized); const bPrimary = await createTestInbox({ role: MailboxRole.primary }); inboxIds.push(bPrimary.id); const b = await createCampaign({ primaryMailboxId: bPrimary.id, name: 'Random B', recipe: CampaignRecipe.randomized, durationDays: 14, }); campaignIds.push(b.id); const satB1 = await createTestInbox(); const satB2 = await createTestInbox(); inboxIds.push(satB1.id, satB2.id); await assignSatellite(b.id, satB1.id); await assignSatellite(b.id, satB2.id); const scheduleA = await prisma.dailySchedule.findMany({ where: { campaignId: a.id }, orderBy: { dayIndex: 'asc' }, }); const restarted = await startCampaign(b.id); const scheduleB = await prisma.dailySchedule.findMany({ where: { campaignId: restarted.id }, orderBy: { dayIndex: 'asc' }, }); assert.notDeepEqual( scheduleA.map((r) => r.plannedSends), scheduleB.map((r) => r.plannedSends), ); }); });