import assert from "node:assert/strict"; import { ChildProcess, spawn } from "node:child_process"; import fs from "node:fs"; import os from "node:os"; import path from "node:path"; import test from "node:test"; import { BuildEngine } from "./buildEngine"; import { commit, createRepository, loadFixture, } from "./testing/repositoryFixture"; const waitForExit = (child: ChildProcess): Promise => new Promise((resolve, reject) => { let stdout = ""; let stderr = ""; child.stdout?.on("data", (chunk) => { stdout += chunk; }); child.stderr?.on("data", (chunk) => { stderr += chunk; }); child.on("error", reject); child.on("exit", (code) => { if (code === 0) resolve(stdout.trim()); else reject(new Error(`worker exited ${code}: ${stderr}`)); }); }); const waitFor = async (predicate: () => boolean): Promise => { const deadline = Date.now() + 10_000; while (!predicate()) { if (Date.now() > deadline) throw new Error("Timed out waiting for workers."); await new Promise((resolve) => setTimeout(resolve, 10)); } }; test("concurrent writers converge on one verified immutable release", async () => { const root = createRepository(); const output = fs.mkdtempSync(path.join(os.tmpdir(), "concurrent-release-")); const barrier = path.join(output, "barrier"); const worker = path.resolve("src/backend/testing/concurrentBuildWorker.ts"); try { const children = [1, 2].map(() => spawn(process.execPath, ["--import", "tsx", worker, root, output, barrier], { cwd: process.cwd(), stdio: ["ignore", "pipe", "pipe"], }), ); const completions = children.map(waitForExit); await waitFor( () => fs .readdirSync(output) .filter((entry) => entry.startsWith("barrier.") && entry.endsWith(".ready")) .length === 2, ); fs.writeFileSync(`${barrier}.go`, ""); const checksums = await Promise.all(completions); assert.equal(new Set(checksums).size, 1); const releases = fs.readdirSync(path.join(output, "releases")); assert.equal(releases.length, 1); new BuildEngine(output).verifyRelease(releases[0]); assert.equal( fs.readdirSync(output).some((entry) => entry.startsWith(".release-")), false, ); } finally { fs.rmSync(root, { recursive: true, force: true }); fs.rmSync(output, { recursive: true, force: true }); } }); test("partial rendering failure leaves no release or temporary directory", () => { const root = createRepository(); const output = fs.mkdtempSync(path.join(os.tmpdir(), "partial-release-")); try { const template = path.join( root, "packages/site-definition/.theme/templates/home.liquid", ); fs.appendFileSync(template, "\n{{ undefined.value }}\n"); commit(root, "break rendering"); const fixture = loadFixture(root, "partial-failure"); try { const engine = new BuildEngine(output); assert.throws(() => engine.build(fixture.input), /undefined|not defined/i); assert.equal(fs.existsSync(engine.buildDirectory(fixture.input.artifactBuildId)), false); assert.equal( fs.readdirSync(output).some((entry) => entry.startsWith(".release-")), false, ); } finally { fixture.manager.dispose(); } } finally { fs.rmSync(root, { recursive: true, force: true }); fs.rmSync(output, { recursive: true, force: true }); } }); test("staging and current pointers promote only checksum-verified releases", () => { const root = createRepository(); const output = fs.mkdtempSync(path.join(os.tmpdir(), "promotion-release-")); const first = loadFixture(root, "first"); try { const engine = new BuildEngine(output); const firstResult = engine.build(first.input); engine.stage(first.input.artifactBuildId); engine.activate(first.input.artifactBuildId); const css = path.join( root, "packages/site-definition/.theme/assets/styles/theme.css", ); fs.appendFileSync(css, "\n.second-release{display:block}\n"); commit(root, "second release"); const second = loadFixture(root, "second"); try { const secondResult = engine.build(second.input); engine.stage(second.input.artifactBuildId); assert.equal(path.basename(fs.realpathSync(engine.currentDirectory())), first.input.artifactBuildId); assert.equal(path.basename(fs.realpathSync(engine.stagingDirectory())), second.input.artifactBuildId); engine.activate(second.input.artifactBuildId); assert.equal(path.basename(fs.realpathSync(engine.currentDirectory())), second.input.artifactBuildId); assert.equal(path.basename(fs.realpathSync(engine.stagingDirectory())), second.input.artifactBuildId); fs.appendFileSync(path.join(firstResult.outputDirectory, "index.html"), "tamper"); assert.throws(() => engine.activate(first.input.artifactBuildId), /E_RELEASE_CHECKSUM/); assert.equal(path.basename(fs.realpathSync(engine.currentDirectory())), second.input.artifactBuildId); assert.ok(fs.existsSync(secondResult.outputDirectory)); } finally { second.manager.dispose(); } } finally { first.manager.dispose(); fs.rmSync(root, { recursive: true, force: true }); fs.rmSync(output, { recursive: true, force: true }); } }); test("repeated identical builds retain identical artifact checksums", () => { const root = createRepository(); const output = fs.mkdtempSync(path.join(os.tmpdir(), "idempotent-release-")); const fixture = loadFixture(root, "idempotent"); try { const engine = new BuildEngine(output); const checksums = Array.from( { length: 20 }, () => engine.build(fixture.input).artifactChecksum, ); assert.equal(new Set(checksums).size, 1); } finally { fixture.manager.dispose(); fs.rmSync(root, { recursive: true, force: true }); fs.rmSync(output, { recursive: true, force: true }); } });