test(orders): add tests for cross-order, filter escape, unique-constraint

Cross-order rejection (Fix #3), PB filter quote escape (Fix #6), and
mapping of PB unique-index write errors to OrderNumberCollisionError
(Fix #7) get full coverage.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

Co-authored-by: Orca <help@stably.ai>
This commit is contained in:
SJCoetz 2026-07-29 16:20:48 +02:00
parent 53b778691e
commit deb224ac10

View file

@ -188,6 +188,88 @@ describe("updateOrderReviewFields", () => {
expect(result.order_number).toBe("ORD-2026-07-001");
expect(result.customer).toBe("cust-1");
});
it("escapes embedded quotes in order_number before building the collision filter", async () => {
// An order_number containing `"` would break the filter
// expression unless we escape embedded quotes before
// interpolating. Spy on getList to capture the filter
// the service actually sends.
const order = await pbMock.collection("orders").create({
order_number: "ORD-2026-07-001",
customer: "cust-1",
});
const originalCollection = pbMock.collection;
const captured: string[] = [];
pbMock.collection = (name: string) => {
const col = originalCollection(name);
if (name === "orders") {
const originalGetList = col.getList;
col.getList = vi.fn(async (...args: unknown[]) => {
const opts = (args[2] as { filter?: string } | undefined) || {};
if (opts.filter) captured.push(opts.filter);
return originalGetList(...(args as Parameters<typeof col.getList>));
});
}
return col;
};
try {
// Pick an order_number that contains a literal quote. The
// unescaped filter would be `order_number = "ORD "foo" && id != "..."`,
// which would either fail to parse or match the wrong rows.
await updateOrderReviewFields(order.id, {
order_number: 'ORD "weird"',
});
expect(captured.length).toBeGreaterThan(0);
// The filter MUST escape the `"` inside the literal — look for
// the backslash-escaped quote in the captured string.
const filter = captured[captured.length - 1];
expect(filter).toContain('ORD \\"weird\\"');
} finally {
pbMock.collection = originalCollection;
}
});
it("maps a PB unique-constraint write error back to OrderNumberCollisionError", async () => {
// Two callers race past the pre-write check; the loser's update
// then throws on the unique index. The service should catch that
// and surface a structured collision error (with the colliding
// row's id) rather than letting it bubble up as a 500.
const order = await pbMock.collection("orders").create({
order_number: "ORD-2026-07-001",
customer: "cust-1",
});
await pbMock.collection("orders").create({
order_number: "ORD-2026-07-002",
customer: "cust-2",
});
const originalCollection = pbMock.collection;
pbMock.collection = (name: string) => {
const col = originalCollection(name);
if (name === "orders") {
col.update = vi.fn(async () => {
const err = new Error(
"UNIQUE constraint failed: orders.order_number",
);
throw err;
});
}
return col;
};
try {
await expect(
updateOrderReviewFields(order.id, {
order_number: "ORD-2026-07-002",
}),
).rejects.toBeInstanceOf(OrderNumberCollisionError);
} finally {
pbMock.collection = originalCollection;
}
});
});
describe("updateOrderItemsPrices", () => {
@ -394,4 +476,49 @@ describe("updateOrderItemsPrices", () => {
// markup_percent must mirror markup — the two fields stay in lock-step.
expect(item.markup_percent).toBe(140);
});
it("rejects items that belong to a different order", async () => {
// Two orders, each with its own item.
const orderA = await pbMock.collection("orders").create({
order_number: "ORD-2026-07-001",
});
const orderB = await pbMock.collection("orders").create({
order_number: "ORD-2026-07-002",
});
await pbMock.collection("order_items").create({
id: "item-a",
order: orderA.id,
description: "A's drill",
quantity: 2,
selling_price: 50,
cost_price: 25,
markup: 100,
});
await pbMock.collection("order_items").create({
id: "item-b",
order: orderB.id,
description: "B's bit",
quantity: 1,
selling_price: 10,
cost_price: 5,
markup: 100,
});
// Caller targets order A but slips in order B's item — the cross-
// order check must catch it and not modify item-b.
const result = await updateOrderItemsPrices(orderA.id, [
{ id: "item-a", quantity: 7 },
{ id: "item-b", quantity: 99 },
]);
expect(result.updated).toBe(1);
expect(result.failed).toEqual([
{ id: "item-b", error: "Item does not belong to this order" },
]);
const itemA = await pbMock.collection("order_items").getOne("item-a");
expect(itemA.quantity).toBe(7);
const itemB = await pbMock.collection("order_items").getOne("item-b");
expect(itemB.quantity).toBe(1); // unchanged
});
});