From afd7af70c82c506a81d6c77169229ef332cbe463 Mon Sep 17 00:00:00 2001 From: lorentz Date: Wed, 15 Jul 2026 22:24:17 -0400 Subject: [PATCH] test(18-04): add failing regression test for campaign report_count double-increment - Sibling report already in campaign-1 re-matches via Tier 3; asserts zero UPDATE campaigns / INSERT campaigns / UPDATE reports calls (CAMP-02/CR-01) --- .../campaign-grouping-service.test.ts | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/lib/services/campaign-grouping-service.test.ts b/lib/services/campaign-grouping-service.test.ts index e0e5d21..b507662 100644 --- a/lib/services/campaign-grouping-service.test.ts +++ b/lib/services/campaign-grouping-service.test.ts @@ -333,4 +333,31 @@ describe('groupReportIntoCampaign', () => { // a second time for this report re-run. expect(callsContaining('UPDATE campaigns')).toHaveLength(0); }); + + it('re-analyzing a report already linked to a campaign that a sibling report re-matches does not increment report_count a second time', async () => { + // Own report is already linked to campaign-1. A SIBLING report (not this + // report's own excluded row) also belongs to campaign-1 and matches via + // Tier 3 (sender + subject + client). This is the CAMP-02/CR-01 gap: + // tier queries only exclude the report's OWN row, not siblings already + // in the same campaign, so a naive re-match would double-increment. + stage({ + ownReport: [{ ...REPORT_ROW, campaign_id: 'campaign-1' }], + ownMessage: [], + tier3: [{ campaign_id: 'campaign-1', title: 'Invoice Alert' }], + }); + + const result = await groupReportIntoCampaign('report-self'); + + expect(result).toEqual({ + campaignId: 'campaign-1', + groupMethod: 'sender_subject_client', + created: false, + }); + + // Critical assertions: no double-increment, no phantom new campaign, no + // redundant re-link. + expect(callsContaining('UPDATE campaigns')).toHaveLength(0); + expect(callsContaining('INSERT INTO campaigns')).toHaveLength(0); + expect(callsContaining('UPDATE reports SET campaign_id')).toHaveLength(0); + }); });