From 887b56ea264db413d2736ea3d07c3580e336cc9a Mon Sep 17 00:00:00 2001 From: Dave Date: Fri, 6 Nov 2026 08:02:43 +0200 Subject: [PATCH] test(giellie): pin the negative-quantity safety cases MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ledger asked the final review to close this gap, and it was still open: the negative-quantity test asserted the ledger row (the delta and the note) but never the persisted `stock_items` patch, and nothing pinned a negative `targetQty` under `allowNegativeQty` — where the code applies the negative allowance only to `currentQty`. - The negative-quantity test now also asserts the persisted patch carries `current_qty: -3`. - A new test asserts a negative `targetQty` is still rejected while `allowNegativeQty` is set, and that nothing is written. --- .../server/stock-quantities-service.test.ts | 29 +++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/src/lib/server/stock-quantities-service.test.ts b/src/lib/server/stock-quantities-service.test.ts index d24890c..98ace2d 100644 --- a/src/lib/server/stock-quantities-service.test.ts +++ b/src/lib/server/stock-quantities-service.test.ts @@ -15,6 +15,7 @@ function stubPb(args: { updated?: Record; }) { const ledgerCreates: Array> = []; + const stockUpdates: Array> = []; const updated = args.updated ?? { ...args.before }; return { pb: { @@ -22,7 +23,12 @@ function stubPb(args: { if (name === "stock_items") { return { getOne: vi.fn().mockResolvedValue(args.before), - update: vi.fn().mockResolvedValue(updated), + update: vi.fn().mockImplementation( + async (_id: string, patch: Record) => { + stockUpdates.push(patch); + return updated; + }, + ), create: vi.fn(), }; } @@ -38,6 +44,7 @@ function stubPb(args: { }, }, ledgerCreates, + stockUpdates, } as const; } @@ -454,7 +461,7 @@ describe("updateStockItemsQuantities — negative quantities", () => { }); it("accepts a negative currentQty when allowNegativeQty is set", async () => { - const { pb, ledgerCreates } = stubPb({ + const { pb, ledgerCreates, stockUpdates } = stubPb({ before: { id: "abcdefghijklmno", current_qty: 5 }, }); const res = await updateStockItemsQuantities({ @@ -466,6 +473,24 @@ describe("updateStockItemsQuantities — negative quantities", () => { expect(res.summary.succeeded).toBe(1); expect(ledgerCreates[0].change_qty).toBe(-8); expect(ledgerCreates[0].notes).toBe("Dave edited qty 5 → -3 via Giellie"); + // The negative quantity is persisted, not just recorded. + expect(stockUpdates[0]).toMatchObject({ current_qty: -3 }); + }); + + it("rejects a negative targetQty even when allowNegativeQty is set", async () => { + const { pb, stockUpdates } = stubPb({ + before: { id: "abcdefghijklmno", current_qty: 5 }, + }); + const res = await updateStockItemsQuantities({ + pb: pb as never, + updates: [{ id: "abcdefghijklmno", targetQty: -1 }], + actor: { id: "u1", name: "Dave" }, + allowNegativeQty: true, + } as UpdateStockItemsQuantitiesInput); + const row = res.results[0]; + expect(row.ok).toBe(false); + expect(row.ok === false && row.reason).toBe("validation_error"); + expect(stockUpdates).toHaveLength(0); }); it("keeps minQty floored at zero even with allowNegativeQty", async () => {