159 lines
6.8 KiB
JavaScript
159 lines
6.8 KiB
JavaScript
import assert from "node:assert/strict";
|
|
import { mkdir, mkdtemp, readFile, rm, stat, writeFile } from "node:fs/promises";
|
|
import { tmpdir } from "node:os";
|
|
import { dirname, join } from "node:path";
|
|
import test from "node:test";
|
|
import { fileURLToPath } from "node:url";
|
|
|
|
import {
|
|
CHECK_IDS,
|
|
canonicalIntegrationBase,
|
|
cleanupOwnedRun,
|
|
createOwnedRun,
|
|
readAndValidateOwnership,
|
|
runIntegration,
|
|
validateReport,
|
|
validateRunRoot,
|
|
} from "./p3-acceptance.mjs";
|
|
|
|
const roots = [];
|
|
async function fakeRepository() {
|
|
const root = await mkdtemp(join(tmpdir(), "p3-acceptance-repo-"));
|
|
roots.push(root);
|
|
await mkdir(join(root, ".artifacts", "p3-integration"), { recursive: true });
|
|
await mkdir(join(root, ".artifacts", "p2-integration"), { recursive: true });
|
|
await mkdir(join(root, ".artifacts", "p1-integration"), { recursive: true });
|
|
await mkdir(join(root, ".artifacts", "manual-acceptance", "p11"), { recursive: true });
|
|
return root;
|
|
}
|
|
|
|
test.afterEach(async () => {
|
|
await Promise.all(roots.splice(0).map((root) => rm(root, { recursive: true, force: true })));
|
|
});
|
|
|
|
test("run roots are only canonical direct p3 integration children", async () => {
|
|
const repositoryRoot = await fakeRepository();
|
|
const base = canonicalIntegrationBase(repositoryRoot);
|
|
const id = `p3-${"a".repeat(32)}`;
|
|
assert.equal(validateRunRoot(repositoryRoot, join(base, id), id), join(base, id));
|
|
for (const candidate of [
|
|
base,
|
|
join(repositoryRoot, ".artifacts", "manual-acceptance", "p11"),
|
|
join(repositoryRoot, ".artifacts", "p1-integration", id),
|
|
join(repositoryRoot, ".artifacts", "p2-integration", id),
|
|
join(base, id, "nested"),
|
|
join(base, "foreign"),
|
|
]) {
|
|
assert.throws(() => validateRunRoot(repositoryRoot, candidate, id));
|
|
}
|
|
assert.throws(() => validateRunRoot(repositoryRoot, join(base, `p3-${"A".repeat(32)}`), `p3-${"A".repeat(32)}`));
|
|
});
|
|
|
|
test("cleanup refuses p1, p2, p11, manual, sibling, and wrong-nonce roots", async () => {
|
|
const repositoryRoot = await fakeRepository();
|
|
const run = await createOwnedRun({ repositoryRoot });
|
|
await readAndValidateOwnership({ repositoryRoot, runRoot: run.root, expectedNonce: run.nonce });
|
|
for (const bad of [
|
|
join(repositoryRoot, ".artifacts", "p1-integration", `p1-${"b".repeat(32)}`),
|
|
join(repositoryRoot, ".artifacts", "p2-integration", `p2-${"c".repeat(32)}`),
|
|
join(repositoryRoot, ".artifacts", "manual-acceptance", "p11"),
|
|
join(canonicalIntegrationBase(repositoryRoot), `p3-${"d".repeat(32)}`),
|
|
]) {
|
|
await assert.rejects(cleanupOwnedRun({ repositoryRoot, runRoot: bad, expectedNonce: run.nonce }));
|
|
}
|
|
await assert.rejects(cleanupOwnedRun({ repositoryRoot, runRoot: run.root, expectedNonce: "0".repeat(64) }));
|
|
});
|
|
|
|
test("cleanup removes exactly one owned p3 root", async () => {
|
|
const repositoryRoot = await fakeRepository();
|
|
const run = await createOwnedRun({ repositoryRoot });
|
|
const sibling = join(canonicalIntegrationBase(repositoryRoot), `p3-${"e".repeat(32)}`);
|
|
await mkdir(sibling);
|
|
await writeFile(join(sibling, "sentinel"), "foreign");
|
|
await cleanupOwnedRun({ repositoryRoot, runRoot: run.root, expectedNonce: run.nonce });
|
|
await assert.rejects(readFile(join(run.root, "ownership.json")));
|
|
assert.equal(await readFile(join(sibling, "sentinel"), "utf8"), "foreign");
|
|
});
|
|
|
|
function resultFor(id) {
|
|
return {
|
|
id,
|
|
status: "PASS",
|
|
startedAt: "2026-08-12T00:00:00.000Z",
|
|
finishedAt: "2026-08-12T00:00:01.000Z",
|
|
commands: ["node"],
|
|
artifacts: [{ path: `logs/${id}.json`, sha256: "a".repeat(64) }],
|
|
};
|
|
}
|
|
|
|
test("report validation requires exact p3 identity, check order, and unique artifacts", () => {
|
|
const report = {
|
|
schemaVersion: 1,
|
|
runId: `p3-${"f".repeat(32)}`,
|
|
startedAt: "2026-08-12T00:00:00.000Z",
|
|
finishedAt: "2026-08-12T00:00:10.000Z",
|
|
command: "p3-acceptance integration --keep",
|
|
overall: "PASS",
|
|
checks: CHECK_IDS.map(resultFor),
|
|
};
|
|
assert.doesNotThrow(() => validateReport(report));
|
|
const invalid = structuredClone(report);
|
|
invalid.runId = `p2-${"f".repeat(32)}`;
|
|
assert.throws(() => validateReport(invalid));
|
|
const duplicate = structuredClone(report);
|
|
duplicate.checks[1].artifacts[0].path = duplicate.checks[0].artifacts[0].path;
|
|
assert.throws(() => validateReport(duplicate), /duplicated/);
|
|
const reordered = structuredClone(report);
|
|
reordered.checks.reverse();
|
|
reordered.overall = "FAIL";
|
|
assert.throws(() => validateReport(reordered));
|
|
});
|
|
|
|
test("public wrapper uses a strict empty environment", async () => {
|
|
const wrapper = await readFile(join(dirname(fileURLToPath(import.meta.url)), "..", "..", "scripts", "p3-acceptance.sh"), "utf8");
|
|
assert.match(wrapper, /safe_env=\(\/usr\/bin\/env -i/);
|
|
assert.doesNotMatch(wrapper, /LANG|LC_ALL|TZ/);
|
|
assert.doesNotMatch(wrapper, /P3_ACCEPTANCE_FAIL_AT/);
|
|
});
|
|
|
|
test("synthetic integration cleans up successful non-kept runs", async () => {
|
|
const repositoryRoot = await fakeRepository();
|
|
const result = await runIntegration({ repositoryRoot, keep: false, env: { P3_ACCEPTANCE_SYNTHETIC: "1" } });
|
|
assert.equal(result.exitCode, 0);
|
|
assert.equal(result.retained, false);
|
|
await assert.rejects(readFile(join(result.runRoot, "ownership.json")));
|
|
});
|
|
|
|
test("synthetic integration retains kept runs with bounded reports", async () => {
|
|
const repositoryRoot = await fakeRepository();
|
|
const result = await runIntegration({ repositoryRoot, keep: true, env: { P3_ACCEPTANCE_SYNTHETIC: "1" } });
|
|
assert.equal(result.exitCode, 0);
|
|
assert.equal(result.retained, true);
|
|
const report = JSON.parse(await readFile(join(result.runRoot, "report.json"), "utf8"));
|
|
assert.equal(report.overall, "PASS");
|
|
const reportMd = await readFile(join(result.runRoot, "report.md"), "utf8");
|
|
assert.match(reportMd, /P3 automated integration: PASS/);
|
|
assert.match(reportMd, /P3 manual acceptance: PENDING/);
|
|
const reportJsonStat = await stat(join(result.runRoot, "report.json"));
|
|
const reportMdStat = await stat(join(result.runRoot, "report.md"));
|
|
assert.ok(reportJsonStat.size <= 64 * 1024, `report.json too large: ${reportJsonStat.size}`);
|
|
assert.ok(reportMdStat.size <= 32 * 1024, `report.md too large: ${reportMdStat.size}`);
|
|
});
|
|
|
|
test("synthetic injected failure retains the owned run and records a single failed report", async () => {
|
|
const repositoryRoot = await fakeRepository();
|
|
const result = await runIntegration({
|
|
repositoryRoot,
|
|
keep: false,
|
|
env: { P3_ACCEPTANCE_SYNTHETIC: "1", P3_ACCEPTANCE_FAIL_AT: CHECK_IDS[2] },
|
|
});
|
|
assert.equal(result.exitCode, 1);
|
|
assert.equal(result.retained, true);
|
|
const report = JSON.parse(await readFile(join(result.runRoot, "report.json"), "utf8"));
|
|
assert.equal(report.overall, "FAIL");
|
|
const failed = report.checks.find((check) => check.id === CHECK_IDS[2]);
|
|
assert.equal(failed.status, "FAIL");
|
|
const roots = await readFile(join(result.runRoot, "ownership.json"), "utf8");
|
|
assert.match(roots, /p3-acceptance/);
|
|
});
|