fix(import): honor cancellation after ID reservation waits
This commit is contained in:
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);
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in new issue
Block a user