90 lines
3.5 KiB
JavaScript
90 lines
3.5 KiB
JavaScript
import assert from "node:assert/strict";
|
|
import { createHash } from "node:crypto";
|
|
import { spawnSync } from "node:child_process";
|
|
import { mkdtemp, readFile, rm } 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";
|
|
|
|
const scriptDir = dirname(fileURLToPath(import.meta.url));
|
|
const builder = join(scriptDir, "build-ops-agents-reboot-resilience-artifact.mjs");
|
|
const basename = "nodedc-ops-agents-reboot-resilience-20260809-001";
|
|
|
|
test("builds a deterministic, minimal Ops Agents application overlay", async () => {
|
|
const firstRoot = await mkdtemp(join(tmpdir(), "ops-agents-artifact-first-"));
|
|
const secondRoot = await mkdtemp(join(tmpdir(), "ops-agents-artifact-second-"));
|
|
|
|
try {
|
|
const first = runBuilder(firstRoot);
|
|
const second = runBuilder(secondRoot);
|
|
const firstArtifact = join(firstRoot, `${basename}.tgz`);
|
|
const secondArtifact = join(secondRoot, `${basename}.tgz`);
|
|
|
|
assert.equal(first.artifactSha256, second.artifactSha256);
|
|
assert.equal(first.artifactSha256, sha256(await readFile(firstArtifact)));
|
|
assert.deepEqual(first.files, [
|
|
"docker-compose.synology.yml",
|
|
"src/db/wait-for-database.test.ts",
|
|
"src/db/wait-for-database.ts",
|
|
"src/scripts/migrate.ts",
|
|
]);
|
|
assert.equal(first.runnerChanged, false);
|
|
assert.equal(first.databaseVolumeSelected, false);
|
|
|
|
const members = new Set(runTar(["-tzf", firstArtifact]).trim().split("\n"));
|
|
assert.deepEqual(members, new Set([
|
|
"manifest.env",
|
|
"files.txt",
|
|
"payload/",
|
|
"payload/docker-compose.synology.yml",
|
|
"payload/src/",
|
|
"payload/src/db/",
|
|
"payload/src/db/wait-for-database.test.ts",
|
|
"payload/src/db/wait-for-database.ts",
|
|
"payload/src/scripts/",
|
|
"payload/src/scripts/migrate.ts",
|
|
]));
|
|
assert.equal([...members].some((member) => member.includes("._")), false);
|
|
|
|
assert.equal(
|
|
runTar(["-xOzf", firstArtifact, "manifest.env"]),
|
|
"id=ops-agents-reboot-resilience-20260809-001\ncomponent=ops-agents\ntype=app-overlay\n",
|
|
);
|
|
assert.equal(
|
|
runTar(["-xOzf", firstArtifact, "files.txt"]),
|
|
`${first.files.join("\n")}\n`,
|
|
);
|
|
|
|
const predecessors = await readFile(join(firstRoot, `${basename}.predecessor.sha256`), "utf8");
|
|
assert.match(predecessors, /^e78d37368c3d255f257086773ba908e44aae2e475661b80ee17f98c3d55d178f .*docker-compose\.synology\.yml$/m);
|
|
assert.match(predecessors, /^8396a42deded31f1fe2381f772838a33b05bfd389075ebd34a5f546593f08769 .*src\/scripts\/migrate\.ts$/m);
|
|
|
|
const newPaths = await readFile(join(firstRoot, `${basename}.new-paths`), "utf8");
|
|
assert.match(newPaths, /src\/db\/wait-for-database\.ts/);
|
|
assert.match(newPaths, /src\/db\/wait-for-database\.test\.ts/);
|
|
} finally {
|
|
await rm(firstRoot, { recursive: true, force: true });
|
|
await rm(secondRoot, { recursive: true, force: true });
|
|
}
|
|
});
|
|
|
|
function runBuilder(outputRoot) {
|
|
const result = spawnSync(process.execPath, [builder], {
|
|
encoding: "utf8",
|
|
env: { ...process.env, NODEDC_DEPLOY_ARTIFACT_DIR: outputRoot },
|
|
});
|
|
assert.equal(result.status, 0, result.stderr || result.stdout);
|
|
return JSON.parse(result.stdout);
|
|
}
|
|
|
|
function runTar(args) {
|
|
const result = spawnSync("tar", args, { encoding: "utf8" });
|
|
assert.equal(result.status, 0, result.stderr || result.stdout);
|
|
return result.stdout;
|
|
}
|
|
|
|
function sha256(bytes) {
|
|
return createHash("sha256").update(bytes).digest("hex");
|
|
}
|