From b494d58c37c323f6ae1e85ef49a04acc064fc460 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 28 Oct 2026 06:10:41 +0000 Subject: [PATCH 1/2] test(campaign-enrollment): give every world()-seeding case an explicit budget sized from measured runs Each enrolment run enrols every eligible person the marketer can see, so the file's runs grow from 7 to 51 members and the last #2241 leads case ran past vitest's 5 s default on main CI twice. Measured 0.5-2.5 s alone and 0.8-4.9 s in a full local test:coverage run. Co-Authored-By: Claude Claude-Session: https://claude.ai/code/session_01J8VVAjZErQs1eFzRrtWuDe --- .../2357-campaign-enrollment-test-budget.md | 4 ++ test/flow-campaign-enrollment.test.ts | 48 ++++++++++++------- 2 files changed, 35 insertions(+), 17 deletions(-) create mode 100644 .changeset/2357-campaign-enrollment-test-budget.md diff --git a/.changeset/2357-campaign-enrollment-test-budget.md b/.changeset/2357-campaign-enrollment-test-budget.md new file mode 100644 index 000000000..c6456d4b9 --- /dev/null +++ b/.changeset/2357-campaign-enrollment-test-budget.md @@ -0,0 +1,4 @@ +--- +--- + +Tests only, nothing ships: every case in `test/flow-campaign-enrollment.test.ts` that seeds a campaign world now carries an explicit 30 s budget instead of vitest's 5 s default. Each enrolment run enrols every eligible person the marketer can see, so the file's runs grow from 7 to 51 members, and the last leads case ran past 5 s on main CI twice (#2357). diff --git a/test/flow-campaign-enrollment.test.ts b/test/flow-campaign-enrollment.test.ts index b9636a0ad..be364802c 100644 --- a/test/flow-campaign-enrollment.test.ts +++ b/test/flow-campaign-enrollment.test.ts @@ -43,6 +43,20 @@ beforeAll(async () => { }); }, 120_000); +/** + * The budget of every case that seeds a `world()`, against vitest's 5 s + * default. A run enrols every eligible person the marketer can see, not only + * its own world's, and each `world()` adds two eligible leads and two eligible + * contacts: so the Nth enrolling run inserts a member per person through a + * per-person subflow, and the file's runs grow from 7 members to 51 (the #2241 + * leads case, the last one). Measured: the enrolling cases take 0.5 s to 2.5 s + * alone and 0.8 s to 4.9 s in a full local `test:coverage` run (as CI runs the + * suite), and that #2241 leads case ran past 5 s on main CI twice (#2357). + * Refused runs enrol nobody and stay under 0.3 s. Same size as `ENROLL_BUDGET_MS` in + * test/campaign-enrollment-doors.test.ts, for the same growth. + */ +const WORLD_BUDGET_MS = 30_000; + /** * One campaign and the people who could be enrolled in it — the eligible ones * and one of every ineligible shape — written as the system, owned by the @@ -168,7 +182,7 @@ describe('campaign_enrollment — screen action', () => { expect(m.status).toBe('sent'); expect(m.added_date, 'added_date should be stamped').toBeTruthy(); } - }); + }, WORLD_BUDGET_MS); it('never enrols an opted-out, converted or off-status lead, and an email-less one cannot be stored', async () => { const w = await world(); @@ -177,7 +191,7 @@ describe('campaign_enrollment — screen action', () => { for (const key of ['l_optout', 'l_converted', 'l_other']) { expect(enrolled, `${key} must not be enrolled`).not.toContain(key); } - }); + }, WORLD_BUDGET_MS); it('tops up rather than double-enrolling on a re-run', async () => { // Duplicate member rows inflated num_sent and the response rate. @@ -187,7 +201,7 @@ describe('campaign_enrollment — screen action', () => { expect(members.filter((m) => m.crm_lead === w.leads.l_new!.id), 'l_new was enrolled twice').toHaveLength(1); // …and the not-yet-enrolled lead still gets added. expect(enrolledKeys(w, members, 'crm_lead')).toContain('l_new2'); - }); + }, WORLD_BUDGET_MS); it('does not treat an enrolment in ANOTHER campaign as a duplicate', async () => { const w = await world(); @@ -198,12 +212,12 @@ describe('campaign_enrollment — screen action', () => { members.filter((m) => m.crm_lead === w.leads.l_new!.id), 'a member row for a different campaign blocked enrolment', ).toHaveLength(1); - }); + }, WORLD_BUDGET_MS); it('enrols into a campaign still in planning', async () => { const w = await world({ status: 'planning' }); expect(enrolledKeys(w, await enrol(w), 'crm_lead').length).toBeGreaterThan(0); - }); + }, WORLD_BUDGET_MS); }); describe('campaign_enrollment — contacts (#597)', () => { @@ -219,7 +233,7 @@ describe('campaign_enrollment — contacts (#597)', () => { expect(m.status).toBe('sent'); expect(m.added_date, 'added_date should be stamped').toBeTruthy(); } - }); + }, WORLD_BUDGET_MS); it('never enrols an opted-out or off-segment contact, and an email-less one cannot be stored', async () => { const w = await world(); @@ -228,7 +242,7 @@ describe('campaign_enrollment — contacts (#597)', () => { for (const key of ['c_optout', 'c_other']) { expect(enrolled, `${key} must not be enrolled`).not.toContain(key); } - }); + }, WORLD_BUDGET_MS); it('tops up rather than double-enrolling a contact on a re-run', async () => { const w = await world(); @@ -239,7 +253,7 @@ describe('campaign_enrollment — contacts (#597)', () => { 'c_eng was enrolled twice', ).toHaveLength(1); expect(enrolledKeys(w, members, 'crm_contact')).toContain('c_eng2'); - }); + }, WORLD_BUDGET_MS); it('does not treat a LEAD enrolment as a duplicate of a contact enrolment', async () => { // Two different records of two different relationships; the seed datasets @@ -255,17 +269,17 @@ describe('campaign_enrollment — contacts (#597)', () => { await existingMember(w.campaign, { crm_lead: twin!.id }); const members = await enrol(w, asContacts()); expect(members.filter((m) => m.crm_contact === w.contacts.c_eng!.id)).toHaveLength(1); - }); + }, WORLD_BUDGET_MS); it('the two branches are exclusive — picking contacts enrols no leads', async () => { const w = await world(); expect((await enrol(w, asContacts())).filter((m) => m.crm_lead)).toHaveLength(0); - }); + }, WORLD_BUDGET_MS); it('…and picking leads enrols no contacts', async () => { const w = await world(); expect((await enrol(w)).filter((m) => m.crm_contact)).toHaveLength(0); - }); + }, WORLD_BUDGET_MS); }); /** @@ -322,7 +336,7 @@ describe('campaign_enrollment — a campaign that is not open, or no longer exis const w = await world({ status }); expect(await ending(w.campaign.id, { ...SCREEN, memberSource })) .toEqual({ ...REFUSED, refusalMessage: refusal }); - }); + }, WORLD_BUDGET_MS); it.each(['leads', 'contacts'] as const)('a campaign id that names no campaign (%s) is refused, and nobody is enrolled', async (memberSource) => { const missing = `no-such-campaign-${++k}`; @@ -342,7 +356,7 @@ describe('campaign_enrollment — a campaign that is not open, or no longer exis success: true, status: undefined, refusalMessage: undefined, successMessage: CampaignEnrollmentFlow.successMessage, ran, members: true, }); - }); + }, WORLD_BUDGET_MS); }); /** @@ -412,21 +426,21 @@ describe('campaign_enrollment — only the chosen source\'s criterion shows, and const screen = ((run as Rec).screen ?? (run as Rec).output?.screen) as Rec | undefined; expect(screen?.fields, 'the paused run carries no screen descriptor').toBeTruthy(); expect(visibleFields(screen!, { memberSource })).toEqual(shown); - }); + }, WORLD_BUDGET_MS); it('a contacts run with no lead status submits and enrols the chosen department', async () => { const w = await world(); const members = await enrol(w, { memberSource: 'contacts', contactDepartment: 'engineering' }); expect(enrolledKeys(w, members, 'crm_contact')).toEqual(['c_eng', 'c_eng2']); expect(members.filter((m) => m.crm_lead), 'a contacts run enrolled a lead').toHaveLength(0); - }); + }, WORLD_BUDGET_MS); it('a leads run with no department submits and enrols the chosen status', async () => { const w = await world(); const members = await enrol(w, { memberSource: 'leads', leadStatus: 'new' }); expect(enrolledKeys(w, members, 'crm_lead')).toEqual(['l_new', 'l_new2']); expect(members.filter((m) => m.crm_contact), 'a leads run enrolled a contact').toHaveLength(0); - }); + }, WORLD_BUDGET_MS); it.each([ ['contacts', 'contactDepartment', { memberSource: 'contacts', leadStatus: 'new' }], @@ -444,5 +458,5 @@ describe('campaign_enrollment — only the chosen source\'s criterion shows, and expect(JSON.stringify({ code: refusal?.code, error: refusal?.error, message: refusal?.message })) .toContain(`Screen field \\"${missing}\\" is required`); expect(await verify.rows('crm_campaign_member', { crm_campaign: w.campaign.id })).toHaveLength(0); - }); + }, WORLD_BUDGET_MS); }); From cb2c9c3c4c120c2f70fc2056953415be6952a619 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 28 Oct 2026 06:11:40 +0000 Subject: [PATCH 2/2] =?UTF-8?q?chore(changeset):=20carry=20the=20claim's?= =?UTF-8?q?=20Clause-=E2=91=A1=20and=20Metadata-protocol=20lines?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Claude-Session: https://claude.ai/code/session_01J8VVAjZErQs1eFzRrtWuDe --- .changeset/2357-campaign-enrollment-test-budget.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.changeset/2357-campaign-enrollment-test-budget.md b/.changeset/2357-campaign-enrollment-test-budget.md index c6456d4b9..04455d9e1 100644 --- a/.changeset/2357-campaign-enrollment-test-budget.md +++ b/.changeset/2357-campaign-enrollment-test-budget.md @@ -1,4 +1,7 @@ --- --- +Clause-②: no +Metadata-protocol: none + Tests only, nothing ships: every case in `test/flow-campaign-enrollment.test.ts` that seeds a campaign world now carries an explicit 30 s budget instead of vitest's 5 s default. Each enrolment run enrols every eligible person the marketer can see, so the file's runs grow from 7 to 51 members, and the last leads case ran past 5 s on main CI twice (#2357).