import { expect, it } from "vitest"; import { mkdtemp, mkdir, writeFile, rm } from "node:fs/promises"; import { tmpdir } from "node:os"; import path from "node:path"; import { randomUUID, createHash } from "node:crypto"; import { SafeWorkspace } from "../src/workspace.js"; import { validatedPpt } from "../src/ppt-tools.js"; import { validateReportDelivery } from "../src/delivery.js"; import { MemoryEventStore } from "../src/store.js"; import { saveTask, updateTask } from "../src/task.js"; it("rejects traversal, stale PPTs and files changed after validation", async () => { const root = await mkdtemp(path.join(tmpdir(), "ppt-gate-test-")); try { const ws = await SafeWorkspace.create(root, "test"); await expect(validatedPpt(ws, "../../secret")).rejects.toThrow("PPT_NOT_FOUND"); const id = randomUUID(); const dir = ws.resolve(`presentations/${id}`); await mkdir(path.join(dir, "output"), { recursive: true }); const bytes = Buffer.from("mock validated artifact"); await writeFile(path.join(dir, "output/deck.pptx"), bytes); const task = updateTask(undefined, "生成PPT", "new")!; await saveTask(ws, task); const receipt = { validated: true, purpose: "final", deliveryAccepted: true, taskRevision: task.revision, runId: "new", sha256: createHash("sha256").update(bytes).digest("hex") }; await writeFile(path.join(dir, "result.json"), JSON.stringify({ ...receipt, purpose: "debug", deliveryAccepted: false })); await expect(validatedPpt(ws, id)).rejects.toThrow("PPT_NOT_ACCEPTED"); await writeFile(path.join(dir, "result.json"), JSON.stringify(receipt)); expect(await validateReportDelivery("pptx", ws, [], "new")).toBe(false); const events = new MemoryEventStore(); events.append("s", { type: "tool.completed", runId: "new", payload: { toolName: "generate_pptx", result: { details: { artifactId: id } } } }); expect(await validateReportDelivery("pptx", ws, events.listAfter("s"), "new")).toBe(true); await writeFile(path.join(dir, "output/deck.pptx"), "changed"); expect(await validateReportDelivery("pptx", ws, events.listAfter("s"), "new")).toBe(false); } finally { await rm(root, { recursive: true, force: true }); } });