diff --git a/src/lib/server/order-order-duplicate-link-service.test.ts b/src/lib/server/order-order-duplicate-link-service.test.ts index 185dc4a..95b2dcf 100644 --- a/src/lib/server/order-order-duplicate-link-service.test.ts +++ b/src/lib/server/order-order-duplicate-link-service.test.ts @@ -17,7 +17,11 @@ const { store, pbMock, clearStore } = vi.hoisted(() => { const clauses = filter.split("&&").map((c) => c.trim()); return items.filter((item) => clauses.every((clause) => { - const m = clause.match(/^(\w+)\s*([!=<>]+)\s*"?([^"]+)"?$/); + // Mock-only filter parser: accepts the four operators the + // duplicate-matcher uses (=, !=, >=, plus the lex variants + // <, <=, >). Anything else silently passes through so a + // typo in a clause doesn't 500 the test. + const m = clause.match(/^(\w+)\s*([!=]+|>=|<=|>|<)\s*"?([^"]+)"?$/); if (!m) return true; const [, field, op, raw] = m; const value = raw.replace(/^"|"$/g, ""); @@ -204,6 +208,14 @@ describe("suggestOrderDuplicate", () => { const result = await suggestOrderDuplicate("order-mine"); expect(result.duplicateOrderId).toBeNull(); + // Symmetric contract: no-match branch still stamps the + // _matching_attempted flag so a UI / external scan can distinguish + // "we tried and found nothing" from "we never looked". + const updated = await pbMock.collection("orders").getOne("order-mine"); + expect(updated.duplicate_order_matching_attempted).toBe(true); + expect(updated.duplicate_order_match_method).toBe("no_match"); + expect(updated.duplicate_order_match_confidence).toBe(0); + expect(updated.potential_duplicate_order).toBeFalsy(); }); it("does not flag when the matching order is older than 60 days", async () => { @@ -221,6 +233,11 @@ describe("suggestOrderDuplicate", () => { const result = await suggestOrderDuplicate("order-mine"); expect(result.duplicateOrderId).toBeNull(); + const updated = await pbMock.collection("orders").getOne("order-mine"); + expect(updated.duplicate_order_matching_attempted).toBe(true); + expect(updated.duplicate_order_match_method).toBe("no_match"); + expect(updated.duplicate_order_match_confidence).toBe(0); + expect(updated.potential_duplicate_order).toBeFalsy(); }); it("does not flag when item quantities differ", async () => { @@ -238,6 +255,11 @@ describe("suggestOrderDuplicate", () => { const result = await suggestOrderDuplicate("order-b"); expect(result.duplicateOrderId).toBeNull(); + const updated = await pbMock.collection("orders").getOne("order-b"); + expect(updated.duplicate_order_matching_attempted).toBe(true); + expect(updated.duplicate_order_match_method).toBe("no_match"); + expect(updated.duplicate_order_match_confidence).toBe(0); + expect(updated.potential_duplicate_order).toBeFalsy(); }); it("never matches an order against itself", async () => { @@ -250,5 +272,61 @@ describe("suggestOrderDuplicate", () => { const result = await suggestOrderDuplicate("order-solo"); expect(result.duplicateOrderId).toBeNull(); + const updated = await pbMock.collection("orders").getOne("order-solo"); + expect(updated.duplicate_order_matching_attempted).toBe(true); + expect(updated.duplicate_order_match_method).toBe("no_match"); + expect(updated.duplicate_order_match_confidence).toBe(0); + expect(updated.potential_duplicate_order).toBeFalsy(); + }); + + it("does not flag when the matching order is just past the 60-day window", async () => { + // 2026-08-30T12:00:00Z - 61d = 2026-06-30T12:00:00Z, just one day + // outside the inclusive `created >= cutoff` window. This pins + // the boundary actually being enforced — without it, a bug that + // drops the `>=` clause would still pass the 90-day-old test. + await seedOrderWithItems( + "order-edge", + { order_number: "ORD-A", company: "comp-1", created: "2026-06-30 12:00:00.000Z" }, + [{ description: "M16x50 Bolt", quantity: 300 }], + ); + await seedOrderWithItems( + "order-mine", + { order_number: "ORD-B", company: "comp-1", created: "2026-08-30 00:00:00.000Z" }, + [{ description: "M16x50 Bolt", quantity: 300 }], + ); + + const result = await suggestOrderDuplicate("order-mine"); + + expect(result.duplicateOrderId).toBeNull(); + const updated = await pbMock.collection("orders").getOne("order-mine"); + expect(updated.duplicate_order_matching_attempted).toBe(true); + expect(updated.duplicate_order_match_method).toBe("no_match"); + expect(updated.potential_duplicate_order).toBeFalsy(); + }); + + it("flags when the matching order is within the 60-day window (boundary positive control)", async () => { + // 2026-08-30T12:00:00Z - 60d = 2026-07-01T12:00:00Z. The candidate + // lands exactly on the inclusive boundary of `created >= cutoff`, + // so it must be matched. Pairs with the just-past-the-window + // test above to pin the boundary from both sides. + await seedOrderWithItems( + "order-edge-in", + { order_number: "ORD-A", company: "comp-1", created: "2026-07-01 12:00:00.000Z" }, + [{ description: "M16x50 Bolt", quantity: 300 }], + ); + await seedOrderWithItems( + "order-mine", + { order_number: "ORD-B", company: "comp-1", created: "2026-08-30 00:00:00.000Z" }, + [{ description: "M16x50 Bolt", quantity: 300 }], + ); + + const result = await suggestOrderDuplicate("order-mine"); + + expect(result.duplicateOrderId).toBe("order-edge-in"); + const updated = await pbMock.collection("orders").getOne("order-mine"); + expect(updated.duplicate_order_matching_attempted).toBe(true); + expect(updated.duplicate_order_match_method).toBe("exact_items_same_company"); + expect(updated.duplicate_order_match_confidence).toBe(1); + expect(updated.potential_duplicate_order).toBe("order-edge-in"); }); });