From 14347c4b4b81558e62d0febf90536c53cc17dfd5 Mon Sep 17 00:00:00 2001 From: Dave Date: Sun, 30 Aug 2026 22:30:52 +0200 Subject: [PATCH] test(orders): cover auth + matcher-isolation for backfill endpoint, document response shape --- .../backfill-duplicate-checks/route.test.ts | 45 +++++++++++++++++++ .../orders/backfill-duplicate-checks/route.ts | 11 +++++ 2 files changed, 56 insertions(+) diff --git a/src/app/api/v1/orders/backfill-duplicate-checks/route.test.ts b/src/app/api/v1/orders/backfill-duplicate-checks/route.test.ts index 6eefa01..543700c 100644 --- a/src/app/api/v1/orders/backfill-duplicate-checks/route.test.ts +++ b/src/app/api/v1/orders/backfill-duplicate-checks/route.test.ts @@ -1,6 +1,9 @@ import { describe, it, expect, vi, beforeEach } from "vitest"; import { NextRequest } from "next/server"; import { POST } from "./route"; +import { suggestOrderNumberDuplicate } from "@/lib/server/order-number-duplicate-link-service"; +import { suggestOrderDuplicate } from "@/lib/server/order-order-duplicate-link-service"; +import { suggestDeliveryNoteForOrder } from "@/lib/server/order-delivery-note-link-service"; vi.mock("@/lib/server/order-number-duplicate-link-service", () => ({ suggestOrderNumberDuplicate: vi.fn().mockResolvedValue({ duplicateOrderId: null }), @@ -45,5 +48,47 @@ describe("POST /api/v1/orders/backfill-duplicate-checks", () => { expect(res.status).toBe(200); const body = await res.json(); expect(body.ordersProcessed).toBe(2); + + expect(suggestOrderNumberDuplicate).toHaveBeenCalledTimes(2); + expect(suggestOrderDuplicate).toHaveBeenCalledTimes(2); + expect(suggestDeliveryNoteForOrder).toHaveBeenCalledTimes(2); + expect(suggestOrderNumberDuplicate).toHaveBeenCalledWith("order-1"); + expect(suggestOrderNumberDuplicate).toHaveBeenCalledWith("order-2"); + }); + + it("returns 401 when AGENT_API_KEY is missing", async () => { + delete process.env.AGENT_API_KEY; + const res = await POST(backfillRequest()); + expect(res.status).toBe(401); + const body = await res.json(); + expect(body.error).toBeTruthy(); + }); + + it("returns 401 when AGENT_API_KEY is wrong", async () => { + process.env.AGENT_API_KEY = AGENT_API_KEY; + const req = new NextRequest( + "http://localhost/api/v1/orders/backfill-duplicate-checks", + { + method: "POST", + headers: { authorization: "Bearer wrong-key" }, + }, + ); + const res = await POST(req); + expect(res.status).toBe(401); + }); + + it("continues processing when one matcher throws for one order", async () => { + vi.mocked(suggestOrderNumberDuplicate) + .mockRejectedValueOnce(new Error("boom")) + .mockResolvedValueOnce({ duplicateOrderId: null }); + + const res = await POST(backfillRequest()); + expect(res.status).toBe(200); + const body = await res.json(); + expect(body.ordersProcessed).toBe(2); + expect(body.failures.length).toBe(1); + expect(body.failures[0].orderId).toBe("order-1"); + expect(body.failures[0].error).toContain("order_number:"); + expect(body.failures[0].error).toContain("boom"); }); }); diff --git a/src/app/api/v1/orders/backfill-duplicate-checks/route.ts b/src/app/api/v1/orders/backfill-duplicate-checks/route.ts index 089b494..ec32e9d 100644 --- a/src/app/api/v1/orders/backfill-duplicate-checks/route.ts +++ b/src/app/api/v1/orders/backfill-duplicate-checks/route.ts @@ -11,6 +11,17 @@ * over the full orders table, and PocketBase's admin client doesn't * love thousands of concurrent requests. This is a background/admin * operation, not a user-facing latency path. + * + * Returns: `{ ordersProcessed, failures }` where `failures` is + * `Array<{ orderId, error }>`. The `error` string is prefixed with + * the matcher name (`order_number:`, `item_duplicate:`, or + * `delivery_note:`) followed by the underlying error message, so + * callers can tell which matcher tripped without re-running. + * + * Errors: + * 401 — missing/wrong `AGENT_API_KEY`. + * (per-order matcher failures are reported in `failures`, not + * surfaced as HTTP errors) */ export const runtime = "nodejs"; export const dynamic = "force-dynamic";