import { randomUUID } from "node:crypto"; import { promises as fs } from "node:fs"; import { tmpdir } from "node:os"; import path from "node:path"; import { afterEach, expect, it } from "vitest"; import type { ImportJob } from "@/lib/furni/import-job"; import { ImportJobStore } from "./furni-job-store"; const roots: string[] = []; afterEach(async () => { for (const root of roots.splice(0)) await fs.rm(root, { recursive: true, force: true }); }); const job = (): ImportJob => ({ id: randomUUID(), userId: 4, createdAt: new Date().toISOString(), updatedAt: new Date().toISOString(), state: "queued", translate: false, items: [], }); async function store() { const root = await fs.mkdtemp(path.join(tmpdir(), "furni-jobs-test-")); roots.push(root); return new ImportJobStore(root); } it("persists history across store instances", async () => { const s = await store(), j = job(); await s.create(j); expect(await new ImportJobStore(s.root).read(j.id)).toEqual(j); }); it("deduplicates concurrent submissions of the same request", async () => { const s = await store(), j = job(); await Promise.all([s.create(j), s.create(j)]); expect(await s.list()).toHaveLength(1); }); it("does not overwrite a completed job on a repeated request", async () => { const s = await store(), j = job(); await s.create(j); await s.save({ ...j, state: "completed" }); expect((await s.create(j)).state).toBe("completed"); }); it("rejects another user attempting to reuse an ID", async () => { const s = await store(), j = job(); await s.create(j); await expect(s.create({ ...j, userId: 8 })).rejects.toThrow( "Request ID already used", ); }); it("rejects unsafe job paths", async () => { const s = await store(); await expect(s.create({ ...job(), id: "../escape" })).rejects.toThrow( "Invalid import ID", ); await expect(s.read("../escape")).rejects.toThrow("Invalid import ID"); });