文件
hyb-oa/server/tests/unit/scripts/check-source-provenance.test.js

179 行
7.4 KiB
JavaScript

const { execFile } = require("node:child_process");
const { mkdtemp, rm, writeFile } = require("node:fs/promises");
const os = require("node:os");
const path = require("node:path");
const { promisify } = require("node:util");
const { pathToFileURL } = require("node:url");
const execFileAsync = promisify(execFile);
const ROOT_DIR = path.resolve(__dirname, "../../../..");
let provenance;
let repoDir;
async function git(args, cwd = repoDir) {
return execFileAsync("git", args, { cwd });
}
async function createCommit(message = "initial") {
await git(["add", "tracked.txt"]);
await git(["commit", "-m", message]);
}
describe("deployment source provenance", () => {
beforeAll(async () => {
const scriptPath = path.join(ROOT_DIR, "deploy/trialrun/check-source-provenance.mjs");
provenance = await import(pathToFileURL(scriptPath).href);
});
it("derives the complete candidate build environment as one immutable contract", () => {
const commit = "a".repeat(40);
const buildEnv = provenance.deriveCandidateBuildEnv(commit);
expect(Object.keys(buildEnv)).toEqual(["BUILD_SHA", "VITE_BUILD_SHA"]);
expect(buildEnv).toEqual({
BUILD_SHA: commit,
VITE_BUILD_SHA: commit.slice(0, 12),
});
expect(Object.isFrozen(buildEnv)).toBe(true);
expect(() => provenance.deriveCandidateBuildEnv("a".repeat(39))).toThrowError(
expect.objectContaining({ code: "commit_invalid" }),
);
expect(() => provenance.deriveCandidateBuildEnv("A".repeat(40))).toThrowError(
expect.objectContaining({ code: "commit_invalid" }),
);
});
beforeEach(async () => {
repoDir = await mkdtemp(path.join(os.tmpdir(), "hyb-oa-provenance-test-"));
await git(["init", "--initial-branch=main"]);
await git(["config", "user.name", "Test User"]);
await git(["config", "user.email", "[email protected]"]);
await writeFile(path.join(repoDir, "tracked.txt"), "initial\n", "utf8");
await createCommit();
});
afterEach(async () => {
await rm(repoDir, { recursive: true, force: true });
});
it("requires an upstream by default and permits an explicit unpublished override", () => {
expect(() => provenance.collectSourceProvenance({ rootDir: repoDir })).toThrowError(
expect.objectContaining({ code: "upstream_missing" }),
);
expect(provenance.collectSourceProvenance({ rootDir: repoDir, allowUnpublished: true })).toMatchObject({
status: "PASS",
published: false,
overrideUsed: true,
branch: "main",
});
});
it("rejects tracked and untracked working-tree changes before any deployment", async () => {
await writeFile(path.join(repoDir, "tracked.txt"), "changed\n", "utf8");
await writeFile(path.join(repoDir, "untracked.txt"), "untracked\n", "utf8");
expect(() => provenance.collectSourceProvenance({ rootDir: repoDir, allowUnpublished: true })).toThrowError(
expect.objectContaining({ code: "dirty_worktree" }),
);
});
it("accepts a clean commit that is contained in its upstream", async () => {
const remoteDir = await mkdtemp(path.join(os.tmpdir(), "hyb-oa-provenance-remote-"));
try {
await execFileAsync("git", ["init", "--bare", remoteDir]);
await git(["remote", "add", "origin", remoteDir]);
await git(["push", "--set-upstream", "origin", "main"]);
expect(provenance.collectSourceProvenance({ rootDir: repoDir })).toMatchObject({
status: "PASS",
published: true,
overrideUsed: false,
upstream: "origin/main",
buildEnv: {
BUILD_SHA: expect.stringMatching(/^[0-9a-f]{40,64}$/u),
VITE_BUILD_SHA: expect.stringMatching(/^[0-9a-f]{12}$/u),
},
});
} finally {
await rm(remoteDir, { recursive: true, force: true });
}
});
it("rejects a clean local commit that has not reached upstream", async () => {
const remoteDir = await mkdtemp(path.join(os.tmpdir(), "hyb-oa-provenance-remote-"));
try {
await execFileAsync("git", ["init", "--bare", remoteDir]);
await git(["remote", "add", "origin", remoteDir]);
await git(["push", "--set-upstream", "origin", "main"]);
await writeFile(path.join(repoDir, "tracked.txt"), "ahead\n", "utf8");
await createCommit("ahead");
expect(() => provenance.collectSourceProvenance({ rootDir: repoDir })).toThrowError(
expect.objectContaining({ code: "commit_not_in_upstream" }),
);
expect(provenance.collectSourceProvenance({ rootDir: repoDir, allowUnpublished: true })).toMatchObject({
published: false,
overrideUsed: true,
});
} finally {
await rm(remoteDir, { recursive: true, force: true });
}
});
it("refreshes the remote-tracking ref before deciding whether a commit is still published", async () => {
const remoteDir = await mkdtemp(path.join(os.tmpdir(), "hyb-oa-provenance-remote-"));
const replacementDir = await mkdtemp(path.join(os.tmpdir(), "hyb-oa-provenance-replacement-"));
try {
await execFileAsync("git", ["init", "--bare", remoteDir]);
await git(["remote", "add", "origin", remoteDir]);
await git(["push", "--set-upstream", "origin", "main"]);
expect(provenance.collectSourceProvenance({ rootDir: repoDir })).toMatchObject({ published: true });
await execFileAsync("git", ["init", "--initial-branch=main"], { cwd: replacementDir });
await execFileAsync("git", ["config", "user.name", "Replacement"], { cwd: replacementDir });
await execFileAsync("git", ["config", "user.email", "[email protected]"], { cwd: replacementDir });
await writeFile(path.join(replacementDir, "replacement.txt"), "replacement\n", "utf8");
await execFileAsync("git", ["add", "replacement.txt"], { cwd: replacementDir });
await execFileAsync("git", ["commit", "-m", "replacement"], { cwd: replacementDir });
await execFileAsync("git", ["remote", "add", "origin", remoteDir], { cwd: replacementDir });
await execFileAsync("git", ["push", "--force", "origin", "main"], { cwd: replacementDir });
expect(provenance.collectSourceProvenance({ rootDir: repoDir })).toMatchObject({ published: true });
expect(provenance.refreshUpstreamRef({ rootDir: repoDir })).toMatchObject({
refreshed: true,
upstream: "origin/main",
remote: "origin",
});
expect(() => provenance.collectSourceProvenance({ rootDir: repoDir })).toThrowError(
expect.objectContaining({ code: "commit_not_in_upstream" }),
);
} finally {
await rm(remoteDir, { recursive: true, force: true });
await rm(replacementDir, { recursive: true, force: true });
}
});
it("requires an allowlisted audit reason for the unpublished override", async () => {
const script = path.join(ROOT_DIR, "deploy/trialrun/check-source-provenance.mjs");
await expect(execFileAsync("node", [script, `--root=${repoDir}`], {
env: { ...process.env, DEPLOY_ALLOW_UNPUBLISHED_COMMIT: "1" },
})).rejects.toMatchObject({ code: 1 });
const { stdout } = await execFileAsync("node", [script, `--root=${repoDir}`], {
env: {
...process.env,
DEPLOY_ALLOW_UNPUBLISHED_COMMIT: "1",
DEPLOY_UNPUBLISHED_REASON: "offline-disaster-recovery",
},
});
expect(JSON.parse(stdout)).toMatchObject({
published: false,
remoteVerified: false,
overrideUsed: true,
overrideRequested: true,
overrideReason: "offline-disaster-recovery",
});
});
});