fix(import): honor cancellation after ID reservation waits

This commit is contained in:
Simo committed 2026-09-05 16:08:20 +02:00
1 parent 36ac116dc2
commit e7549bbbd2
2 files changed
+43

No files matched your search

@@ -60,6 +60,7 @@ vi.mock("@/lib/services/swf-to-nitro", () => ({
}));
import { importSingleFurni } from "./furni-import";
import { reserveFurnitureId } from "./furniture-id-reservation";
const input = {
id: 42,
@@ -120,6 +121,46 @@ describe("completion of existing furniture", () => {
expect(mocks.execute).not.toHaveBeenCalled();
expect(mocks.append).not.toHaveBeenCalled();
});
it("cancels while waiting for another ID reservation without subsequent writes", async () => {
const controller = new AbortController();
const held = Promise.withResolvers<void>();
const release = Promise.withResolvers<void>();
const writing = Promise.withResolvers<void>();
mocks.select.mockImplementation(() => ({
from: () => ({ where: () => ({ limit: async () => [] }) }),
}));
mocks.execute.mockImplementation(async (query) => {
const { sql } = dialect.sqlToQuery(query);
return sql.includes("SELECT id, page_id")
? [[{ id: 123, page_id: "15" }], []]
: [[], []];
});
const prior = reserveFurnitureId(41, "prior_furniture", async () => {
held.resolve();
await release.promise;
});
await held.promise;
const importing = importSingleFurni({
...input,
signal: controller.signal,
onProgress: (stage) => {
if (stage === "writing_db") writing.resolve();
},
});
await writing.promise;
controller.abort();
release.resolve();
await prior;
await expect(importing).rejects.toMatchObject({ name: "AbortError" });
expect(
queries().every((query) => query.sql.trim().startsWith("SELECT")),
).toBe(true);
expect(mocks.append).not.toHaveBeenCalled();
// Cancellation also releases the reservation queue for the next importer.
await expect(
reserveFurnitureId(43, "next_furniture", async () => {}),
).resolves.toBe(43);
});
it("never deletes existing SQL when the nitro cannot be recovered", async () => {
mocks.stat.mockRejectedValue(new Error("missing"));
const result = await importSingleFurni(input);
+2
View File
@@ -857,6 +857,7 @@ export async function importSingleFurni(params: {
originalId,
classname,
async (allocatedId) => {
signal?.throwIfAborted();
await db.execute(
sql`INSERT INTO items_base (id, sprite_id, public_name, item_name, type) VALUES (${allocatedId}, ${allocatedId}, ${bestName}, ${classname}, ${itemType})`,
);
@@ -869,6 +870,7 @@ export async function importSingleFurni(params: {
);
}
} catch (err) {
if (signal?.aborted) throw err;
return {
ok: false,
warnings,