92 lines
4.0 KiB
JavaScript
92 lines
4.0 KiB
JavaScript
import assert from "node:assert/strict";
|
|
import { createHash } from "node:crypto";
|
|
import { access, lstat, readFile, rm } from "node:fs/promises";
|
|
import { join } from "node:path";
|
|
import test from "node:test";
|
|
import { fileURLToPath } from "node:url";
|
|
import { dirname, resolve } from "node:path";
|
|
|
|
import {
|
|
cleanupManual,
|
|
prepareManual,
|
|
readManualOwnership,
|
|
serveManual,
|
|
stopManual,
|
|
} from "./p11-manual-acceptance.mjs";
|
|
|
|
const repoRoot = resolve(dirname(fileURLToPath(import.meta.url)), "../..");
|
|
const fixedRoot = join(repoRoot, ".artifacts", "manual-acceptance", "p11");
|
|
|
|
async function safeCleanup() {
|
|
try {
|
|
const owned = await readManualOwnership({ repositoryRoot: repoRoot });
|
|
if (owned.status === "RUNNING") await stopManual({ repositoryRoot: repoRoot }).catch(() => {});
|
|
await cleanupManual({ repositoryRoot: repoRoot }).catch(() => {});
|
|
} catch {
|
|
await rm(fixedRoot, { recursive: true, force: true }).catch(() => {});
|
|
}
|
|
}
|
|
|
|
test.beforeEach(async () => {
|
|
await safeCleanup();
|
|
});
|
|
|
|
test.afterEach(async () => {
|
|
await safeCleanup();
|
|
});
|
|
|
|
test("prepare creates an independent pending lab without verdict", { concurrency: false }, async () => {
|
|
const root = await prepareManual({ repositoryRoot: repoRoot });
|
|
assert.equal(root, fixedRoot);
|
|
const owned = await readManualOwnership({ repositoryRoot: repoRoot });
|
|
assert.equal(owned.kind, "p11-manual-acceptance");
|
|
assert.equal(owned.status, "PENDING");
|
|
await access(join(root, "GUIDE.md"));
|
|
await access(join(root, "author", "thoth-workspaces.yaml"));
|
|
await access(join(root, "author", "p11-filesystem", "evidence", "guide.md"));
|
|
await access(join(root, "requests", "validate-p11-filesystem.json"));
|
|
await access(join(root, "commands", "http-01-status.sh"));
|
|
await access(join(root, "commands", "render-1.sh"));
|
|
await assert.rejects(access(join(root, "VERDICT.md")));
|
|
const guide = await readFile(join(root, "GUIDE.md"), "utf8");
|
|
assert.match(guide, /VERDICT\.md/);
|
|
assert.match(guide, /read-only/);
|
|
});
|
|
|
|
test("serve, stop, and cleanup manage the owned backend and frontend listeners", { concurrency: false }, async () => {
|
|
await prepareManual({ repositoryRoot: repoRoot });
|
|
const running = await serveManual({ repositoryRoot: repoRoot });
|
|
assert.equal(running.status, "RUNNING");
|
|
assert.equal(typeof running.backend.pid, "number");
|
|
assert.equal(typeof running.frontend.pid, "number");
|
|
const status = await fetch("http://127.0.0.1:8791/workspace-registry/status");
|
|
assert.equal(status.status, 200);
|
|
const frontend = await fetch("http://127.0.0.1:8792/");
|
|
assert.equal(frontend.status, 200);
|
|
await assert.rejects(cleanupManual({ repositoryRoot: repoRoot }), /still live/);
|
|
const stopped = await stopManual({ repositoryRoot: repoRoot });
|
|
assert.equal(stopped.status, "STOPPED");
|
|
await cleanupManual({ repositoryRoot: repoRoot });
|
|
await assert.rejects(lstat(fixedRoot));
|
|
});
|
|
|
|
test("stop fails closed when ownership is tampered", { concurrency: false }, async () => {
|
|
await prepareManual({ repositoryRoot: repoRoot });
|
|
const running = await serveManual({ repositoryRoot: repoRoot });
|
|
const ownershipPath = join(fixedRoot, "ownership.json");
|
|
const digestPath = join(fixedRoot, "ownership.sha256");
|
|
const original = JSON.parse(await readFile(ownershipPath, "utf8"));
|
|
const tampered = { ...original, backend: { ...original.backend, pid: original.backend.pid + 1 } };
|
|
await rm(ownershipPath);
|
|
await readFile(join(fixedRoot, "logs", "backend.log"));
|
|
await import("node:fs/promises").then(({ writeFile }) => writeFile(ownershipPath, `${JSON.stringify(tampered, null, 2)}
|
|
`));
|
|
await assert.rejects(stopManual({ repositoryRoot: repoRoot }), /manual ownership digest mismatch/);
|
|
const restored = `${JSON.stringify(running, null, 2)}
|
|
`;
|
|
const restoredDigest = `${createHash("sha256").update(restored).digest("hex")}
|
|
`;
|
|
await import("node:fs/promises").then(({ writeFile }) => Promise.all([writeFile(ownershipPath, restored), writeFile(digestPath, restoredDigest)]));
|
|
await stopManual({ repositoryRoot: repoRoot });
|
|
});
|