Block invalid catalog_items writes at the API level (points currency allowlist, non-negative prices, positive amount, limited stack >= sold count, unique sibling order numbers) and auto-assign unique order numbers on bulk create. Add a catalog-maintenance scan + transactional repair that fixes pre-existing rows: resets unsupported points_type, clamps negative costs, sets amount to 1, raises limited_stack, renumbers duplicate orders and deletes offers with missing page/item references. Surface the issue count and a fix button in the admin maintenance panel. Also: add enabled/retired flag to clone sources, classify poster and currency furniture in item-kind, and remove the obsolete update-Nitrov3.sh.
71 lines
2.1 KiB
TypeScript
71 lines
2.1 KiB
TypeScript
import { expect, it } from "vitest";
|
|
import { bulkOfferInputSchema, projectBulkOffer } from "./bulk-offers";
|
|
|
|
const row = { pageId: 4, costCredits: 3, costPoints: 10, pointsType: 5 };
|
|
it("combines selected changes, rounds percentages, preserves explicit zero", () => {
|
|
expect(
|
|
projectBulkOffer(row, {
|
|
costCredits: { mode: "set", value: 0 },
|
|
costPoints: { mode: "percent", value: 25 },
|
|
pageId: 9,
|
|
}),
|
|
).toEqual({ ...row, costCredits: 0, costPoints: 13, pageId: 9 });
|
|
});
|
|
it("rejects negative results and integer overflow instead of silently clamping", () => {
|
|
expect(() =>
|
|
projectBulkOffer(row, { costCredits: { mode: "add", value: -4 } }),
|
|
).toThrow();
|
|
expect(() =>
|
|
projectBulkOffer(row, { costCredits: { mode: "set", value: 2147483648 } }),
|
|
).toThrow();
|
|
});
|
|
it("leaves legacy signed fields untouched when changing a different field", () => {
|
|
expect(
|
|
projectBulkOffer({ ...row, costCredits: -1 }, { pageId: 5 }).costCredits,
|
|
).toBe(-1);
|
|
});
|
|
it("rejects empty, duplicate, oversized selections and unknown mutations", () => {
|
|
for (const input of [
|
|
{ ids: [], changes: { pageId: 1 } },
|
|
{ ids: [1, 1], changes: { pageId: 1 } },
|
|
{
|
|
ids: Array.from({ length: 501 }, (_, i) => i + 1),
|
|
changes: { pageId: 1 },
|
|
},
|
|
{ ids: [1], changes: {} },
|
|
{ ids: [1], changes: { itemIds: "2" } },
|
|
])
|
|
expect(() => bulkOfferInputSchema.parse(input)).toThrow();
|
|
});
|
|
it("rejects fractional set/add prices, accepts percentage discounts", () => {
|
|
expect(() =>
|
|
bulkOfferInputSchema.parse({
|
|
ids: [1],
|
|
changes: { costCredits: { mode: "set", value: 1.5 } },
|
|
}),
|
|
).toThrow();
|
|
expect(
|
|
projectBulkOffer(row, { costPoints: { mode: "percent", value: -50 } })
|
|
.costPoints,
|
|
).toBe(5);
|
|
});
|
|
it("rejects unsupported pointsType values in bulk changes", () => {
|
|
expect(() =>
|
|
bulkOfferInputSchema.parse({
|
|
ids: [1],
|
|
changes: { pointsType: 101 },
|
|
}),
|
|
).toThrow(/not supported/i);
|
|
});
|
|
it("accepts supported pointsType values in bulk changes", () => {
|
|
expect(
|
|
bulkOfferInputSchema.parse({
|
|
ids: [1],
|
|
changes: { pointsType: 5 },
|
|
}),
|
|
).toEqual({
|
|
ids: [1],
|
|
changes: { pointsType: 5 },
|
|
});
|
|
});
|