Files

69 lines
2.4 KiB
TypeScript
Raw Permalink Normal View History

2026-07-12 21:45:26 +00:00
import { createHash } from "node:crypto";
import { access, mkdtemp, readFile, readdir } from "node:fs/promises";
2026-07-12 21:12:18 +00:00
import { tmpdir } from "node:os";
import { join } from "node:path";
import { Readable } from "node:stream";
import { describe, expect, it } from "vitest";
import { SafeOutputTree } from "../src/artifacts/SafeOutputTree.js";
const LIMITS = {
maxEntries: 10,
maxTotalBytes: 1_024,
maxEntryBytes: 512,
maxCompressionRatio: 10,
maxDepth: 8,
maxPathBytes: 256,
} as const;
2026-07-12 21:45:26 +00:00
describe("safe artifact output tree", () => {
2026-07-12 21:12:18 +00:00
it("removes only its owned tree after digest failure and proves absence", async () => {
const parent = await mkdtemp(join(tmpdir(), "rea-safe-output-"));
const output = join(parent, "published");
2026-07-12 21:45:26 +00:00
const tree = await SafeOutputTree.create(output, LIMITS);
2026-07-12 21:12:18 +00:00
await expect(
2026-07-12 21:45:26 +00:00
tree.write(
2026-07-12 21:12:18 +00:00
"nested/file.txt",
Readable.from(Buffer.from("unexpected")),
"0".repeat(64),
),
).rejects.toThrow(/disagrees/u);
2026-07-12 21:45:26 +00:00
expect(await tree.rollback()).toMatchObject({
2026-07-12 21:12:18 +00:00
attempted: true,
verified: true,
residualPaths: [],
});
await expect(access(output)).rejects.toThrow();
expect(await readdir(parent)).toEqual([]);
});
2026-07-12 21:45:26 +00:00
it("publishes files while building and preserves them after sealing", async () => {
const parent = await mkdtemp(join(tmpdir(), "rea-safe-output-"));
const output = join(parent, "published");
const tree = await SafeOutputTree.create(output, LIMITS);
const bytes = Buffer.from("visible before seal");
const digest = createHash("sha256").update(bytes).digest("hex");
await tree.write("file.txt", Readable.from(bytes), digest);
expect(await readFile(join(output, "file.txt"), "utf8")).toBe(
"visible before seal",
);
await tree.commit();
expect(await tree.rollback()).toMatchObject({ attempted: false });
expect(await readFile(join(output, "file.txt"), "utf8")).toBe(
"visible before seal",
);
});
2026-07-14 14:39:02 +08:00
it("returns detached cleanup reports", async () => {
const parent = await mkdtemp(join(tmpdir(), "rea-safe-output-"));
const tree = await SafeOutputTree.create(join(parent, "published"), LIMITS);
const cleanup = await tree.rollback();
(cleanup.residualPaths as string[]).push("/forged/path");
expect(tree.cleanup.residualPaths).toEqual([]);
expect((await tree.rollback()).residualPaths).toEqual([]);
});
2026-07-12 21:12:18 +00:00
});