import { describe, expect, it, vi } from "vitest"; // 不挂载 HTTP 路由;隔离 Deno 静态文件适配器,以便本单元测试在 Node 下执行。 vi.mock("hono/deno", () => ({ serveStatic: vi.fn() })); import { verifyInPlace } from "../src/modules/acquisition-organize/services/organize.ts"; const path = "/media/unchanged.mkv"; const frozen = { rootPath: "/media", sentinel: ".uhd-root", libraryRootId: "root", entityId: "work", sourceFacts: { [path]: { size: 100, mtimeMs: 200 } }, } as unknown as Parameters[2]; async function verify(observed: unknown, owner: string | null = null) { const executeTakeFirst = vi.fn().mockResolvedValue(owner ? { media_entity_id: owner } : undefined); const query = { innerJoin: () => query, select: () => query, where: () => query, executeTakeFirst }; const d = { ctx: { db: { selectFrom: () => query } } } as unknown as Parameters[0]; const statSafe = vi.fn().mockResolvedValue(observed); // 仅提供 statSafe;若意外调用复制、移动、清理或写入,本测试立即失败。 const fsr = { proxy: { statSafe } } as unknown as Parameters[1]; return { result: await verifyInPlace(d, fsr, frozen, path, 100), statSafe, executeTakeFirst }; } const observed = (extra = {}) => ({ kind: "ok", facts: { isFile: true, isSymbolicLink: false, size: 100, mtimeMs: 200, ...extra }, }); describe("原地登记执行核验", () => { it("只读核验,不复制或移动文件;同作品重复登记可继续", async () => { const r = await verify(observed(), "work"); expect(r.result).toEqual({ status: "succeeded", bytes: 0 }); expect(r.statSafe).toHaveBeenCalledWith(path, { root: "/media", sentinel: ".uhd-root" }); }); it("文件消失、链接、变为目录或计划后的大小/时间变化均不登记", async () => { for (const value of [ { kind: "missing" }, observed({ isSymbolicLink: true }), observed({ isFile: false }), observed({ size: 101 }), observed({ mtimeMs: 201 }), ]) { const r = await verify(value); expect(r.result.status).toBe("failed"); expect(r.executeTakeFirst).not.toHaveBeenCalled(); } }); it("不会把已有视频悄悄登记为另一个作品", async () => { expect((await verify(observed(), "other")).result).toMatchObject({ status: "failed", error: { code: "ASSET_ENTITY_CONFLICT" }, }); }); });