test(stock): cover validation + not_found + per-row independence
This commit is contained in:
parent
86eba97d33
commit
ab639eea6d
1 changed files with 92 additions and 0 deletions
|
|
@ -186,3 +186,95 @@ describe("updateStockItemsPricing — stock_ledger audit on cost change", () =>
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("updateStockItemsPricing — input validation", () => {
|
||||||
|
it("rejects negative numbers", async () => {
|
||||||
|
const result = await updateStockItemsPricing({
|
||||||
|
pb: { collection: () => ({ getOne: vi.fn(), update: vi.fn(), create: vi.fn() }) } as never,
|
||||||
|
updates: [{ id: "abcdefghijklmno", costPrice: -1 }],
|
||||||
|
actor: { id: "useropid1234567", name: "Gary" },
|
||||||
|
});
|
||||||
|
expect(result.results[0]).toMatchObject({
|
||||||
|
ok: false,
|
||||||
|
reason: "validation_error",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("rejects forbidden fields", async () => {
|
||||||
|
const result = await updateStockItemsPricing({
|
||||||
|
pb: { collection: () => ({ getOne: vi.fn(), update: vi.fn(), create: vi.fn() }) } as never,
|
||||||
|
updates: [
|
||||||
|
{ id: "abcdefghijklmno", costPrice: 1, maliciousFlag: true } as never,
|
||||||
|
],
|
||||||
|
actor: { id: "useropid1234567", name: "Gary" },
|
||||||
|
});
|
||||||
|
expect(result.results[0]).toMatchObject({
|
||||||
|
ok: false,
|
||||||
|
reason: "forbidden_field",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("updateStockItemsPricing — not_found and per-row independence", () => {
|
||||||
|
it("returns not_found when the row is gone", async () => {
|
||||||
|
const pb = {
|
||||||
|
collection: (name: string) => {
|
||||||
|
if (name === "stock_items") {
|
||||||
|
return {
|
||||||
|
getOne: vi.fn().mockRejectedValue(new Error("404 not found")),
|
||||||
|
update: vi.fn(),
|
||||||
|
create: vi.fn(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (name === "stock_ledger") {
|
||||||
|
return { create: vi.fn() };
|
||||||
|
}
|
||||||
|
throw new Error(`unexpected ${name}`);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const result = await updateStockItemsPricing({
|
||||||
|
pb: pb as never,
|
||||||
|
updates: [{ id: "missing00000000x", costPrice: 1 }],
|
||||||
|
actor: { id: "useropid1234567", name: "Gary" },
|
||||||
|
});
|
||||||
|
expect(result.results[0]).toMatchObject({
|
||||||
|
ok: false,
|
||||||
|
reason: "not_found",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("does not abort sibling rows when one is not_found", async () => {
|
||||||
|
const updated = { id: "abcdefghijklmno", cost_price: 5, selling_price: 0, markup: 0 };
|
||||||
|
let nextIsMissing = true;
|
||||||
|
const pb = {
|
||||||
|
collection: (name: string) => {
|
||||||
|
if (name === "stock_items") {
|
||||||
|
return {
|
||||||
|
getOne: vi.fn().mockImplementation(async () => {
|
||||||
|
if (nextIsMissing) {
|
||||||
|
nextIsMissing = false;
|
||||||
|
throw new Error("404 not found");
|
||||||
|
}
|
||||||
|
return updated;
|
||||||
|
}),
|
||||||
|
update: vi.fn().mockResolvedValue(updated),
|
||||||
|
create: vi.fn(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (name === "stock_ledger") return { create: vi.fn().mockResolvedValue({ id: "l1" }) };
|
||||||
|
throw new Error(`unexpected ${name}`);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const result = await updateStockItemsPricing({
|
||||||
|
pb: pb as never,
|
||||||
|
updates: [
|
||||||
|
{ id: "missing00000000x", costPrice: 1 },
|
||||||
|
{ id: "abcdefghijklmno", costPrice: 5 },
|
||||||
|
],
|
||||||
|
actor: { id: "useropid1234567", name: "Gary" },
|
||||||
|
});
|
||||||
|
expect(result.summary).toEqual({ total: 2, succeeded: 1, failed: 1 });
|
||||||
|
expect(result.results[0]).toMatchObject({ ok: false, reason: "not_found" });
|
||||||
|
expect(result.results[1]).toMatchObject({ ok: true });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue