test(giellie): pin the negative-quantity safety cases

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.
This commit is contained in:
Dave 2026-11-06 08:02:43 +02:00
parent 7031499bf0
commit 887b56ea26

View file

@ -15,6 +15,7 @@ function stubPb(args: {
updated?: Record<string, unknown>;
}) {
const ledgerCreates: Array<Record<string, unknown>> = [];
const stockUpdates: Array<Record<string, unknown>> = [];
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<string, unknown>) => {
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 () => {