diff --git a/apps/web/src/server/api/routers/campaign-security.trpc.test.ts b/apps/web/src/server/api/routers/campaign-security.trpc.test.ts index e320ce1..1d60a3f 100644 --- a/apps/web/src/server/api/routers/campaign-security.trpc.test.ts +++ b/apps/web/src/server/api/routers/campaign-security.trpc.test.ts @@ -8,6 +8,7 @@ const { mockDb, mockValidateDomainFromEmail } = vi.hoisted(() => ({ campaign: { findUnique: vi.fn(), update: vi.fn(), + create: vi.fn(), }, contactBook: { findUnique: vi.fn(), @@ -56,6 +57,7 @@ describe("campaignRouter.updateCampaign authorization", () => { mockDb.teamUser.findFirst.mockReset(); mockDb.campaign.findUnique.mockReset(); mockDb.campaign.update.mockReset(); + mockDb.campaign.create.mockReset(); mockDb.contactBook.findUnique.mockReset(); mockDb.teamUser.findFirst.mockResolvedValue({ @@ -77,6 +79,11 @@ describe("campaignRouter.updateCampaign authorization", () => { domainId: 2, contactBookId: "cb_other_team", }); + + mockDb.campaign.create.mockResolvedValue({ + id: "camp_copy", + teamId: 10, + }); }); it("rejects assigning a contact book from another team", async () => { @@ -102,3 +109,64 @@ describe("campaignRouter.updateCampaign authorization", () => { }); }); }); + +describe("campaignRouter.duplicateCampaign", () => { + beforeEach(() => { + mockDb.teamUser.findFirst.mockReset(); + mockDb.campaign.findUnique.mockReset(); + mockDb.campaign.create.mockReset(); + + mockDb.teamUser.findFirst.mockResolvedValue({ + teamId: 10, + userId: 1, + role: "ADMIN", + team: { id: 10, name: "Acme" }, + }); + + mockDb.campaign.findUnique.mockResolvedValue({ + id: "camp_1", + teamId: 10, + name: "Weekly update", + from: "Team ", + replyTo: ["support@example.com"], + cc: ["ops@example.com"], + bcc: ["audit@example.com"], + subject: "This week", + previewText: "Quick overview", + content: '{"root":{}}', + html: "

This week

", + domainId: 2, + contactBookId: "cb_1", + }); + + mockDb.campaign.create.mockResolvedValue({ + id: "camp_copy", + teamId: 10, + }); + }); + + it("duplicates reply-to and other email headers", async () => { + const caller = createCaller(getContext()); + + await caller.duplicateCampaign({ + campaignId: "camp_1", + }); + + expect(mockDb.campaign.create).toHaveBeenCalledWith({ + data: { + name: "Weekly update (Copy)", + from: "Team ", + replyTo: ["support@example.com"], + cc: ["ops@example.com"], + bcc: ["audit@example.com"], + subject: "This week", + previewText: "Quick overview", + content: '{"root":{}}', + html: "

This week

", + teamId: 10, + domainId: 2, + contactBookId: "cb_1", + }, + }); + }); +}); diff --git a/apps/web/src/server/api/routers/campaign.ts b/apps/web/src/server/api/routers/campaign.ts index 5d2e27e..6e385c4 100644 --- a/apps/web/src/server/api/routers/campaign.ts +++ b/apps/web/src/server/api/routers/campaign.ts @@ -243,7 +243,11 @@ export const campaignRouter = createTRPCRouter({ data: { name: `${campaign.name} (Copy)`, from: campaign.from, + replyTo: campaign.replyTo, + cc: campaign.cc, + bcc: campaign.bcc, subject: campaign.subject, + previewText: campaign.previewText, content: campaign.content, html: campaign.html, teamId: team.id,