scripts/build-agent-node.test.mjs
Rama main · réplica de lectura del repositorio original.
import assert from "node:assert/strict";
import { test } from "node:test";
import { mkdtemp, readFile, rm } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { spawnSync } from "node:child_process";
import { fileURLToPath } from "node:url";
import { buildAgentNode } from "./build-agent-node.mjs";
test("the release builder injects the exact version into an executable binary", { timeout: 360_000 }, async () => {
const root = await mkdtemp(join(tmpdir(), "agent-node-version-"));
try {
for (const version of ["dev", "v1.2.3-rc.4", "sha-0123456789ab"]) {
const binary = join(root, process.platform === "win32" ? `${version}.exe` : version);
// Run the same builder as the release workflow, using this test host's
// architecture; cross-platform archive builds are a separate gate.
const env = { ...process.env };
delete env.GOOS;
delete env.GOARCH;
buildAgentNode(version, binary, { env });
const result = spawnSync(binary, ["--version"], {
cwd: root, env: process.platform === "win32" ? { SystemRoot: process.env.SystemRoot } : {},
encoding: "utf8", timeout: 5000,
});
assert.equal(result.error, undefined);
assert.equal(result.status, 0);
assert.equal(result.stderr, "");
assert.equal(result.stdout, `openlinker-agent-node/${version}\n`);
}
} finally {
await rm(root, { recursive: true, force: true });
}
});
test("invalid versions fail before any build can run", () => {
for (const version of [undefined, "", "latest", "v1.2.3 extra", "openlinker-cli/v1.2.3", "sha-NOTHEX", "v1.2.3\n"]) {
assert.throws(() => buildAgentNode(version, "unused", { env: { PATH: "" } }), /version must/);
}
assert.throws(() => buildAgentNode("dev", ""), /output path is required/);
});
const releaseGate = fileURLToPath(new URL("./check-release-upgrade-readiness.mjs", import.meta.url));
function checkRelease(args, env = process.env) {
const result = spawnSync(process.execPath, [releaseGate, ...args], {
env, encoding: "utf8", timeout: 5000,
});
assert.equal(result.error, undefined);
return result;
}
test("the executable release gate retains canonical pre-1.0 test prereleases", () => {
for (const tag of ["v0.0.0-alpha.0", "v0.1.59-alpha.1", "v0.1.59-beta.2", "v0.1.59-rc.1", "v0.20.100-rc.10"]) {
const result = checkRelease([tag]);
assert.equal(result.status, 0, tag);
assert.equal(result.stderr, "", tag);
assert.match(result.stdout, /^NODE_TEST_PRERELEASE_ALLOWED:/, tag);
}
});
test("the executable release gate rejects missing, untested stable, production and malformed versions", () => {
for (const args of [
[], [""], ["v0.1.59"], ["v0.2.1"], ["v0.3.0"], ["v0.2.0\n"], ["v0.2.0", "extra"], ["v1.0.0"], ["v1.0.0-rc.1"], ["v2.0.0-alpha.1"],
["dev"], ["latest"], ["sha-0123456789ab"], ["0.1.59-rc.1"], ["refs/tags/v0.1.59-rc.1"],
["v0.01.59-rc.1"], ["v0.1.059-rc.1"], ["v0.1.59-rc.01"], ["v00.1.59-rc.1"],
["v0.1.59-preview.1"], ["v0.1.59-RC.1"], ["v0.1.59-rc"], ["v0.1.59-rc.-1"],
["v0.1.59-rc.1.2"], ["v0.1.59-rc.1+build.1"], ["v0.1.59-rc.1\n"], [" v0.1.59-rc.1"],
["v0.1.59-rc.1", "extra"],
]) {
const result = checkRelease(args);
assert.equal(result.status, 1, JSON.stringify(args));
assert.equal(result.stdout, "", JSON.stringify(args));
assert.match(result.stderr, /^NODE_SUPPORTED_RELEASE_REQUIRED:/, JSON.stringify(args));
}
});
test("the exact supported stable version remains a candidate pending artifact acceptance", () => {
const result = checkRelease(["v0.2.0"]);
assert.equal(result.status, 0);
assert.match(result.stdout, /^NODE_CONTROLLED_RELEASE_CANDIDATE:/);
});
test("environment values cannot replace or bypass the explicit release tag", () => {
const env = {
...process.env,
GITHUB_REF_NAME: "v0.1.59-rc.1",
NODE_UPGRADE_COMPATIBILITY_READY: "true",
ALLOW_NODE_UPGRADE_WITHOUT_CORE: "1",
};
for (const args of [[], ["v0.1.59"], ["v1.0.0-rc.1"]]) {
const result = checkRelease(args, env);
assert.equal(result.status, 1);
assert.equal(result.stdout, "");
}
});
test("release workflow gates the actual tag and keeps tested six-platform packaging behind artifact acceptance", async () => {
// Wiring coverage, distinct from the real gate and binary executions above.
const workflow = await readFile(new URL("../.github/workflows/release.yml", import.meta.url), "utf8");
const packaging = workflow.slice(workflow.indexOf(" package-binaries:"), workflow.indexOf(" publish-release:"));
assert.match(packaging, /if: github\.ref_type == 'tag'\n\s+run: node scripts\/check-release-upgrade-readiness\.mjs "\$GITHUB_REF_NAME"/);
assert.ok(packaging.indexOf("check-release-upgrade-readiness.mjs") < packaging.indexOf("go mod download"));
assert.ok(packaging.includes("go test ./..."));
assert.ok(packaging.indexOf("go test ./...") < packaging.indexOf("- name: Package binary"));
assert.ok(packaging.includes("node --test scripts/build-agent-node.test.mjs"));
assert.deepEqual([...packaging.matchAll(/- goos: (\w+)\n\s+goarch: (\w+)/g)].map((match) => `${match[1]}/${match[2]}`), [
"linux/amd64", "linux/arm64", "darwin/amd64", "darwin/arm64", "windows/amd64", "windows/arm64",
]);
assert.ok(packaging.includes('version="sha-${GITHUB_SHA::12}"'));
assert.ok(packaging.includes("sha256sum"));
assert.ok(packaging.includes(".sha256"));
assert.match(workflow, /publish-release:[\s\S]*?always\(\) && github\.ref_type == 'tag'/);
assert.match(workflow, /needs: \[package-binaries, controlled-upgrade\]/);
assert.match(workflow, /controlled-upgrade:[\s\S]*?needs: package-binaries[\s\S]*?uses: \.\/\.github\/workflows\/controlled-upgrade\.yml/);
assert.match(workflow, /artifact_name: openlinker-agent-node-v0\.2\.0-linux-amd64/);
assert.match(workflow, /prerelease="false"/);
assert.match(workflow, /\[\[ "\$\{tag\}" == \*-\* \]\] && prerelease="true"/);
assert.match(workflow, /gh release create "\$\{tag\}"[^\n]*--prerelease="\$prerelease"/);
assert.match(workflow, /gh release edit "\$\{tag\}" --prerelease="\$prerelease"/);
});