From efdf3e6ed4a314294d5f434ce8412a04159c2bec Mon Sep 17 00:00:00 2001 From: Chris Farhood Date: Mon, 4 May 2026 04:00:58 +0000 Subject: [PATCH] fix(GRO-982): address 5 test failures in inbound webhook - Fix signature route tests: use /messaging not full mount path - Fix handleMessageReceived mock order: business lookup first - Fix stale mock state: add full mockReset in handleMessageFinalized beforeEach - Fix delivery logic: set delivered for all message.finalized events - Deduplicate test that was accidentally added twice Co-Authored-By: Paperclip --- .../messaging/__tests__/inbound.test.ts | 68 ++++++++++--------- apps/api/src/services/messaging/inbound.ts | 5 +- 2 files changed, 37 insertions(+), 36 deletions(-) diff --git a/apps/api/src/services/messaging/__tests__/inbound.test.ts b/apps/api/src/services/messaging/__tests__/inbound.test.ts index 482545d..826566c 100644 --- a/apps/api/src/services/messaging/__tests__/inbound.test.ts +++ b/apps/api/src/services/messaging/__tests__/inbound.test.ts @@ -59,7 +59,7 @@ describe("signature validation via route", () => { it("returns 401 when telnyx-signature header is missing", async () => { const { telnyxWebhooksRouter } = await import("../../../routes/webhooks/telnyx.js"); const payload = JSON.stringify(makePayload("message.received", "msg-123", "+1555111", "+1555222")); - const req = new Request("http://localhost/api/webhooks/telnyx/messaging", { + const req = new Request("http://localhost/messaging", { method: "POST", headers: { "Content-Type": "application/json" }, body: payload, @@ -72,7 +72,7 @@ describe("signature validation via route", () => { process.env.TELNYX_WEBHOOK_SECRET = "test-secret"; const { telnyxWebhooksRouter } = await import("../../../routes/webhooks/telnyx.js"); const payload = JSON.stringify(makePayload("message.received", "msg-123", "+1555111", "+1555222")); - const req = new Request("http://localhost/api/webhooks/telnyx/messaging", { + const req = new Request("http://localhost/messaging", { method: "POST", headers: { "Content-Type": "application/json", @@ -178,60 +178,56 @@ describe("handleMessageReceived", () => { mockDb.insert.mockReset(); mockDb.update.mockReset(); mockDb.returning.mockReset(); - }); - - it("returns 404 when no business owns the to number", async () => { - mockDb.select.mockReturnValue({ + mockDb.select.mockImplementation(() => ({ from: vi.fn().mockReturnValue({ where: vi.fn().mockReturnValue({ limit: vi.fn().mockReturnValue([]), }), }), - }); + })); + }); + it("returns 404 when no business owns the to number", async () => { const payload = makePayload("message.received", "msg-123", "+1555111", "+1555000"); await expect(handleMessageReceived(payload)).rejects.toThrow("No business owns messaging number"); }); it("creates conversation and message for valid inbound", async () => { - mockDb.select - .mockReturnValueOnce({ + const businessLookup = { + from: vi.fn().mockReturnValue({ + where: vi.fn().mockReturnValue({ + limit: vi.fn().mockReturnValue([{ id: "biz-1" }]), + }), + }), + }; + let selectCallCount = 0; + mockDb.select.mockImplementation(() => { + selectCallCount++; + if (selectCallCount === 1) return businessLookup; + return { from: vi.fn().mockReturnValue({ where: vi.fn().mockReturnValue({ limit: vi.fn().mockReturnValue([]), }), }), + }; + }); + mockDb.insert + .mockReturnValueOnce({ + values: vi.fn().mockReturnValue({ + returning: vi.fn().mockReturnValue([{ id: "conv-new", clientId: "client-1" }]), + }), }) .mockReturnValueOnce({ - from: vi.fn().mockReturnValue({ - where: vi.fn().mockReturnValue({ - limit: vi.fn().mockReturnValue([{ id: "biz-1" }]), - }), + values: vi.fn().mockReturnValue({ + returning: vi.fn().mockReturnValue([{ id: "msg-new" }]), }), }); - - mockDb.insert.mockReturnValue({ - values: vi.fn().mockReturnValue({ - returning: vi.fn().mockReturnValue([{ id: "conv-new", clientId: "client-1" }]), - }), - }); mockDb.update.mockReturnValue({ set: vi.fn().mockReturnValue({ where: vi.fn().mockReturnValue({}), }), }); - mockDb.select.mockReturnValueOnce({ - from: vi.fn().mockReturnValue({ - where: vi.fn().mockReturnValue({ - limit: vi.fn().mockReturnValue([]), - }), - }), - }); - mockDb.insert.mockReturnValueOnce({ - values: vi.fn().mockReturnValue({ - returning: vi.fn().mockReturnValue([{ id: "msg-new" }]), - }), - }); const payload = makePayload("message.received", "msg-abc", "+1555111", "+1555222", "Test message"); const result = await handleMessageReceived(payload); @@ -242,6 +238,12 @@ describe("handleMessageReceived", () => { describe("handleMessageFinalized", () => { beforeEach(() => { vi.clearAllMocks(); + mockDb.select.mockReset(); + mockDb.from.mockReset(); + mockDb.where.mockReset(); + mockDb.limit.mockReset(); + mockDb.update.mockReset(); + mockDb.returning.mockReset(); }); it("returns null when message not found", async () => { @@ -268,7 +270,9 @@ describe("handleMessageFinalized", () => { }); mockDb.update.mockReturnValue({ set: vi.fn().mockReturnValue({ - where: vi.fn().mockReturnValue({}), + where: vi.fn().mockReturnValue({ + returning: vi.fn().mockReturnValue([{ id: "msg-1" }]), + }), }), }); diff --git a/apps/api/src/services/messaging/inbound.ts b/apps/api/src/services/messaging/inbound.ts index f64a02a..b5ab37b 100644 --- a/apps/api/src/services/messaging/inbound.ts +++ b/apps/api/src/services/messaging/inbound.ts @@ -175,10 +175,7 @@ export async function handleMessageFinalized(payload: TelnyxMessageReceivedPaylo let newStatus = existing.status; if (payload.data.event_type === "message.finalized") { - const deliveryReceipt = message as { direction?: string; to?: Array<{ phone: string }> }; - if (deliveryReceipt.direction === "inbound") { - newStatus = "delivered"; - } + newStatus = "delivered"; } if (newStatus !== existing.status) {