Remap occupied furniture IDs during Catalog Studio imports
This commit is contained in:
1 parent
9f791ba284
commit
9dc6b8a261
6 files changed
+206
-9
No files matched your search
@@ -0,0 +1,80 @@
|
||||
// @vitest-environment node
|
||||
import { MySqlDialect } from "drizzle-orm/mysql-core";
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
const mocks = vi.hoisted(() => ({ execute: vi.fn(), conflict: vi.fn() }));
|
||||
vi.mock("@/lib/db", () => ({ db: { execute: mocks.execute } }));
|
||||
vi.mock("./furni-data", () => ({ findFurniDataIdConflict: mocks.conflict }));
|
||||
|
||||
import { reserveFurnitureId } from "./furniture-id-reservation";
|
||||
|
||||
const dialect = new MySqlDialect();
|
||||
let occupied: Set<number>;
|
||||
beforeEach(() => {
|
||||
occupied = new Set([16940]);
|
||||
mocks.conflict.mockReset().mockResolvedValue(null);
|
||||
mocks.execute.mockReset().mockImplementation(async (query) => {
|
||||
const { sql, params } = dialect.sqlToQuery(query);
|
||||
return sql.includes("MAX(")
|
||||
? [[{ maximum: Math.max(...occupied) }], []]
|
||||
: [
|
||||
occupied.has(Number(params[0]))
|
||||
? [{ item_name: "recycler_kintsugi" }]
|
||||
: [],
|
||||
[],
|
||||
];
|
||||
});
|
||||
});
|
||||
describe("furniture ID reservation", () => {
|
||||
it("remaps a source ID already owned by another furniture", async () => {
|
||||
const insert = vi.fn();
|
||||
expect(await reserveFurnitureId(16940, "recycler_kintsugiB", insert)).toBe(
|
||||
16941,
|
||||
);
|
||||
expect(insert).toHaveBeenCalledWith(16941);
|
||||
});
|
||||
it("checks furnidata-only collisions too", async () => {
|
||||
mocks.conflict.mockImplementation(async (id) =>
|
||||
id === 16941 ? "other" : null,
|
||||
);
|
||||
expect(await reserveFurnitureId(16940, "new", vi.fn())).toBe(16942);
|
||||
});
|
||||
it("reserves distinct IDs for concurrent imports", async () => {
|
||||
const insert = async (id: number) => {
|
||||
await Promise.resolve();
|
||||
occupied.add(id);
|
||||
};
|
||||
expect(
|
||||
await Promise.all([
|
||||
reserveFurnitureId(16940, "first", insert),
|
||||
reserveFurnitureId(16940, "second", insert),
|
||||
]),
|
||||
).toEqual([16941, 16942]);
|
||||
});
|
||||
it("retries competing primary-key inserts without overwriting the owner", async () => {
|
||||
const insert = vi
|
||||
.fn()
|
||||
.mockImplementationOnce(async () => {
|
||||
occupied.add(16941);
|
||||
throw Object.assign(new Error("duplicate"), { code: "ER_DUP_ENTRY" });
|
||||
})
|
||||
.mockResolvedValue(undefined);
|
||||
expect(await reserveFurnitureId(16940, "new", insert)).toBe(16942);
|
||||
});
|
||||
it("propagates non-collision errors and releases the queue", async () => {
|
||||
await expect(
|
||||
reserveFurnitureId(16940, "new", async () => {
|
||||
throw new Error("DB unavailable");
|
||||
}),
|
||||
).rejects.toThrow("DB unavailable");
|
||||
expect(await reserveFurnitureId(16940, "next", vi.fn())).toBe(16941);
|
||||
});
|
||||
it("fails safely at the client integer limit", async () => {
|
||||
occupied.add(2147483647);
|
||||
const insert = vi.fn();
|
||||
await expect(reserveFurnitureId(16940, "new", insert)).rejects.toThrow(
|
||||
"No valid furniture ID",
|
||||
);
|
||||
expect(insert).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user