import { mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync, } from "node:fs"; import { tmpdir } from "node:os"; import { join } from "node:path"; import { afterEach, describe, expect, it } from "vitest"; import { deliverGit, digestTree, npmPackFiles, snapshot, verifiedReceipt, } from "./index.ts"; const roots: string[] = []; function repo(publish = true): { root: string; dist: string; release: string } { const root = mkdtempSync(join(tmpdir(), "pi-ext-release-test-")); roots.push(root); const ext = join(root, "extensions", "demo"); const dist = join(root, "dist"); mkdirSync(ext, { recursive: true }); writeFileSync(join(root, "package.json"), '{"version":"1.2.3"}\n'); writeFileSync( join(ext, "scenario.mjs"), "export async function verify() {}\n", ); writeFileSync( join(ext, "package.json"), JSON.stringify({ "pi-ext": publish ? { publish: true, publicName: "@owned/demo", git: "file:///never-push", releaseScenario: "scenario.mjs", } : {}, }), ); mkdirSync(join(dist, "demo"), { recursive: true }); writeFileSync(join(dist, "demo", "index.js"), "export default () => {};\n"); writeFileSync( join(dist, "demo", ".pi-ext-build.json"), '{"files":["index.js","package.json"]}\n', ); writeFileSync( join(dist, "demo", "package.json"), '{"name":"@bugabinga/pi-ext-demo","private":true}\n', ); return { root, dist, release: join(root, "release") }; } afterEach(() => roots .splice(0) .forEach((root) => rmSync(root, { recursive: true, force: true })), ); describe("release snapshots", () => { it("fails closed for private extensions", () => { const input = repo(false); expect(() => snapshot("demo", { root: input.root, distRoot: input.dist, releaseRoot: input.release, }), ).toThrow(/not eligible/u); }); it("hashes CI and verifier before receipt, packs only runtime bytes, and rejects tampering", () => { const input = repo(); const receipt = snapshot("demo", { root: input.root, distRoot: input.dist, releaseRoot: input.release, }); expect(digestTree(receipt.path)).toBe(receipt.digest); expect( readFileSync(join(receipt.path, ".ci", "standalone.kdl"), "utf8"), ).toContain('image "node:24-alpine"'); expect(npmPackFiles(receipt.path)).toEqual(["index.js", "package.json"]); expect( verifiedReceipt("demo", { distRoot: input.dist, releaseRoot: input.release, }).digest, ).toBe(receipt.digest); writeFileSync(join(input.dist, "demo", "index.js"), "tampered\n"); expect(() => verifiedReceipt("demo", { distRoot: input.dist, releaseRoot: input.release, }), ).toThrow(/current dist differs/u); }); it("delivers a root package in an isolated local Git repository", () => { const input = repo(); const receipt = snapshot("demo", { root: input.root, distRoot: input.dist, releaseRoot: input.release, }); expect(receipt.extension).toBe("demo"); const git = deliverGit("demo", { distRoot: input.dist, releaseRoot: input.release, }); roots.push(git); expect(readFileSync(join(git, "package.json"), "utf8")).toContain( "@owned/demo", ); expect(() => readFileSync(join(git, "extensions", "demo", "index.ts"), "utf8"), ).toThrow(); }); });