2026-08-05 21:07:14 +08:00
|
|
|
import assert from "node:assert/strict";
|
|
|
|
|
import fs from "node:fs";
|
|
|
|
|
import os from "node:os";
|
|
|
|
|
import path from "node:path";
|
|
|
|
|
import test from "node:test";
|
|
|
|
|
import {
|
2026-08-05 21:08:28 +08:00
|
|
|
isJsonRecordName,
|
2026-08-05 21:07:14 +08:00
|
|
|
normalizeBranch,
|
|
|
|
|
normalizeCodeChannelRepository,
|
|
|
|
|
normalizeReceipt,
|
|
|
|
|
parseArgs,
|
|
|
|
|
safeCacheRelative,
|
|
|
|
|
selfTest,
|
2026-08-05 21:13:01 +08:00
|
|
|
sha256,
|
2026-08-05 21:07:14 +08:00
|
|
|
} from "./finalize-development.mjs";
|
|
|
|
|
|
|
|
|
|
test("the finalizer accepts only the registered Fifth Domain code channel", () => {
|
|
|
|
|
assert.equal(
|
|
|
|
|
normalizeCodeChannelRepository(
|
|
|
|
|
"https://guanghulab.com/code/bingshuo/guanghu-ice-heart.git",
|
|
|
|
|
).identity,
|
|
|
|
|
"repo://guanghulab.com/code/bingshuo/guanghu-ice-heart",
|
|
|
|
|
);
|
|
|
|
|
for (const invalid of [
|
|
|
|
|
"https://guanghulab.com/fifth-domain/bingshuo/fifth-domain.git",
|
|
|
|
|
"https://example.com/code/bingshuo/guanghu-ice-heart.git",
|
|
|
|
|
"https://user:secret@guanghulab.com/code/bingshuo/guanghu-ice-heart.git",
|
|
|
|
|
]) {
|
|
|
|
|
assert.throws(() => normalizeCodeChannelRepository(invalid));
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test("receipt and branch validation fail closed", () => {
|
|
|
|
|
assert.equal(normalizeBranch("main"), "main");
|
|
|
|
|
assert.equal(
|
|
|
|
|
normalizeReceipt("deployment/receipts/EXACT.json"),
|
|
|
|
|
"deployment/receipts/EXACT.json",
|
|
|
|
|
);
|
|
|
|
|
assert.throws(() => normalizeReceipt("../EXACT.json"));
|
|
|
|
|
assert.throws(() => normalizeReceipt("docs/EXACT.json"));
|
|
|
|
|
assert.throws(() => normalizeBranch("../main"));
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test("repeated receipts and cleanup paths are retained", () => {
|
|
|
|
|
const parsed = parseArgs([
|
|
|
|
|
"--development-id",
|
|
|
|
|
"DEV-20260805-003",
|
|
|
|
|
"--receipt",
|
|
|
|
|
"deployment/receipts/A.json",
|
|
|
|
|
"--receipt",
|
|
|
|
|
"deployment/receipts/B.json",
|
|
|
|
|
"--cleanup-path",
|
|
|
|
|
"target",
|
|
|
|
|
]);
|
|
|
|
|
assert.deepEqual(parsed.receipts, [
|
|
|
|
|
"deployment/receipts/A.json",
|
|
|
|
|
"deployment/receipts/B.json",
|
|
|
|
|
]);
|
|
|
|
|
assert.deepEqual(parsed.cleanupPaths, ["target"]);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test("cache deletion scope accepts only exact allowlisted descendants", () => {
|
|
|
|
|
const root = fs.mkdtempSync(path.join(os.tmpdir(), "finalizer-cache-test-"));
|
|
|
|
|
try {
|
|
|
|
|
assert.equal(safeCacheRelative(root, "target").relative, "target");
|
|
|
|
|
assert.equal(
|
|
|
|
|
safeCacheRelative(root, "src/__pycache__").relative,
|
|
|
|
|
"src/__pycache__",
|
|
|
|
|
);
|
|
|
|
|
assert.throws(() => safeCacheRelative(root, "."));
|
|
|
|
|
assert.throws(() => safeCacheRelative(root, "../outside"));
|
|
|
|
|
assert.throws(() => safeCacheRelative(root, "outputs"));
|
|
|
|
|
} finally {
|
|
|
|
|
fs.rmSync(root, { recursive: true, force: true });
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test("embedded finalizer self-test passes", () => {
|
|
|
|
|
assert.deepEqual(selfTest(), { result: "PASS_100" });
|
|
|
|
|
});
|
2026-08-05 21:08:28 +08:00
|
|
|
|
|
|
|
|
test("macOS AppleDouble sidecars are never parsed as JSON records", () => {
|
|
|
|
|
assert.equal(isJsonRecordName("lease.json"), true);
|
|
|
|
|
assert.equal(isJsonRecordName("._lease.json"), false);
|
|
|
|
|
assert.equal(isJsonRecordName("lease.txt"), false);
|
|
|
|
|
});
|
2026-08-05 21:13:01 +08:00
|
|
|
|
|
|
|
|
test("receipt hashes preserve exact trailing bytes", () => {
|
|
|
|
|
assert.notEqual(sha256(Buffer.from("{}")), sha256(Buffer.from("{}\n")));
|
|
|
|
|
});
|