返回 DeepSeek-Reasonix
release-publication-ledger.test.mjs
根目录 / scripts / release-publication-ledger.test.mjs
1 import assert from "node:assert/strict";
2 import test from "node:test";
3 import { spawnSync } from "node:child_process";
4 import { mkdtempSync, writeFileSync, rmSync, readFileSync } from "node:fs";
5 import { tmpdir } from "node:os";
6 import { join } from "node:path";
7 import { fileURLToPath } from "node:url";
8 import { createCoreLedger, createSiteLedger, mergeLedgers, npmPackageNames, ownsPublicSite } from "./release-publication-ledger.mjs";
9
10 const sha = "a".repeat(40);
11 const release = { isDraft: false, isPrerelease: false, assets: [{ name: "asset.zip", size: 42, digest: "sha256:abc" }] };
12 const packages = latest => npmPackageNames.map(name => ({
13 name,
14 version: "1.2.3",
15 reasonixCandidateSha: sha,
16 gitHead: sha,
17 integrity: "sha512-example",
18 latest,
19 }));
20
21 test("records immutable files and exact current pointers", () => {
22 const core = createCoreLedger({
23 version: "1.2.3", sourceSHA: sha, operation: "publish",
24 cliRelease: release, desktopRelease: release, npmPackages: packages("1.2.3"),
25 });
26 assert.equal(core.surfaces.tags.items.length, 3);
27 assert.ok(core.surfaces.npm.packages.every(item => item.pointerState === "public-entry-updated"));
28 assert.equal(core.surfaces.cli.assets[0].digest, "sha256:abc");
29 });
30
31 test("recovery preserves newer public pointers", () => {
32 const core = createCoreLedger({
33 version: "1.2.3", sourceSHA: sha, operation: "recover",
34 cliRelease: release, desktopRelease: release, npmPackages: packages("1.3.0"),
35 });
36 assert.ok(core.surfaces.npm.packages.every(item => item.pointerState === "newer-entry-preserved"));
37 assert.throws(() => createCoreLedger({
38 version: "1.2.3", sourceSHA: sha, operation: "publish",
39 cliRelease: release, desktopRelease: release, npmPackages: packages("1.3.0"),
40 }), /npm latest is inconsistent/);
41 });
42
43 test("site evidence merges only for the same candidate", () => {
44 const core = createCoreLedger({
45 version: "1.2.3", sourceSHA: sha, operation: "publish",
46 cliRelease: release, desktopRelease: release, npmPackages: packages("1.2.3"),
47 });
48 const site = createSiteLedger({
49 version: "1.2.3", sourceSHA: sha, operation: "publish", manifest: { version: "v1.2.3" },
50 });
51 assert.equal(mergeLedgers(core, site).surfaces.homepage.state, "public-entry-updated");
52 assert.throws(() => mergeLedgers(core, { ...site, sourceSHA: "b".repeat(40) }), /one release/);
53 });
54
55 test("site ownership requires an exact release or a proven newer recovery pointer", () => {
56 for (const operation of ["publish", "recover"]) {
57 assert.equal(ownsPublicSite("1.2.3", operation, { version: "v1.2.3" }), true);
58 for (const version of [undefined, "", "v1.2.2", "1.2.3", "v1.2.3-beta.1"]) {
59 assert.throws(() => ownsPublicSite("1.2.3", operation, { version }));
60 }
61 }
62 assert.equal(ownsPublicSite("1.2.3", "recover", { version: "v1.10.0" }), false);
63 assert.throws(() => ownsPublicSite("1.2.3", "publish", { version: "v1.10.0" }));
64 assert.throws(() => ownsPublicSite("1.2.3", "unknown", { version: "v1.2.3" }));
65 });
66
67 test("actual site observation fails closed on HTTP errors and malformed or stale responses", () => {
68 const directory = mkdtempSync(join(tmpdir(), "release-site-observation-"));
69 try {
70 writeFileSync(join(directory, "curl"), '#!/bin/sh\nprintf "%s" "$RESPONSE_BODY"\nexit "$RESPONSE_STATUS"\n', { mode: 0o755 });
71 const helper = fileURLToPath(new URL("./observe-release-site.sh", import.meta.url));
72 for (const [status, body, expected] of [
73 [22, "", null], [22, '{"version":"v1.3.0"}', null],
74 [0, "", null], [0, "<html>Forbidden</html>", null],
75 [0, '{"version":"v1.2.2"}', null],
76 [0, '{"version":"v1.2.3"}', "true"],
77 [0, '{"version":"v1.10.0"}', "false"],
78 ]) {
79 const result = spawnSync("bash", [helper, "1.2.3", "recover"], {
80 encoding: "utf8", env: { ...process.env, PATH: `${directory}:${process.env.PATH}`, RESPONSE_STATUS: String(status), RESPONSE_BODY: body },
81 });
82 if (expected === null) {
83 assert.notEqual(result.status, 0, `must reject HTTP ${status}: ${body}`);
84 assert.equal(result.stdout, "");
85 } else {
86 assert.equal(result.status, 0, result.stderr);
87 assert.equal(result.stdout.trim(), expected);
88 }
89 }
90 for (const file of ["../.github/workflows/release-promote.yml", "./verify-stable-release-artifacts.sh"]) {
91 assert.match(readFileSync(new URL(file, import.meta.url), "utf8"), /observe-release-site\.sh/);
92 }
93 } finally {
94 rmSync(directory, { recursive: true, force: true });
95 }
96 });
97
97 lines Plain Text