gitea: remove the code bound to no event since ADR 0571 clause 13: the verdict setter, the controller's checked and plan types, the build seat's started (issue 1126)

Claude-Session: https://claude.ai/code/session_01JG73KG7AfSdh6FP8VrBqCn
This commit is contained in:
2026-11-04 11:00:53 +01:00
parent bff850f798
commit 052fb473d0
8 changed files with 20 additions and 803 deletions
+2 -131
View File
@@ -2,18 +2,11 @@ import assert from "node:assert/strict";
import { test } from "node:test";
// **A pending check says where it is, and the verdict lands last** (novox/hq issue 446). This module is the one writer
// of a head's mesh/merge-gate: the announcement's pending words, "building on <machine> since …" when the build seat
// says a machine took the check, and the verdict. The forge keeps the newest status of a context, so a pending word
// of a head's mesh/merge-gate: the announcement's pending words and the verdict. The forge keeps the newest status of a context, so a pending word
// written after the verdict hides it — a pass reads pending, a failure hides under "building".
const SHA = "a6b08de8507c1703528277e5cefc80548dbc9bb3";
// The build seat's `started` as a machine said it for novox/hq#309's check on 2026-10-11 (its log, read with the
// controller's `builds --log build-1791680941048622697`, opens at 03:09:01 on shanks); only the forge's address
// replaced. The shape is mesh-controller's link.BuildStart.
const startedOnShanks = { id: "build-1791680941048622697", repository: "http://127.0.0.1:20000/novox/hq.git",
ref: SHA, on: "shanks", at: "2026-10-11T01:09:01.204319Z" };
/** A forge that keeps the newest status per context, as gitea does, and whose writes take as long as a test says. */
function aForge() {
const kept: Record<string, { state: string; description: string; updated_at: string }> = {};
@@ -71,99 +64,8 @@ function aForge() {
/** The clock of the test: the head announced at 01:08:50Z. */
const announcedAt = () => new Date("2026-10-11T01:08:50Z");
test("a build machine taking the check is said on the head: building on which machine, since the local time", async () => {
const { GateWriter, buildingWords } = await import("../gate.ts");
const forge = aForge();
const gates = new GateWriter(forge, announcedAt);
await gates.pending("novox", "hq", SHA);
assert.equal(await gates.started("novox", "hq", SHA, startedOnShanks), true);
assert.equal(forge.kept["mesh/merge-gate"].state, "pending");
assert.equal(forge.kept["mesh/merge-gate"].description, buildingWords("shanks", new Date(startedOnShanks.at)));
assert.equal(buildingWords("shanks", new Date(2026, 9, 11, 3, 9, 1)), "building on shanks since 03:09");
});
test("the verdict heard while a 'building' write is in flight lands last and stays", async () => {
const { GateWriter } = await import("../gate.ts");
const forge = aForge();
const gates = new GateWriter(forge, announcedAt);
await gates.pending("novox", "hq", SHA);
forge.holdWrites(); // the 'building' request is in flight when the verdict comes
const building = gates.started("novox", "hq", SHA, startedOnShanks);
await new Promise((r) => setImmediate(r));
const verdict = gates.verdict("novox", "hq", SHA, () =>
forge.setCommitStatus("novox", "hq", SHA, { state: "success", context: "mesh/merge-gate", description: "pass" }));
forge.release();
await Promise.all([building, verdict]);
assert.equal(`${forge.kept["mesh/merge-gate"].state} ${forge.kept["mesh/merge-gate"].description}`, "success pass",
`the verdict is not the newest status: ${forge.written.join(" | ")}`);
});
// The race the review of mesh-catalog#202 reproduced: a writer reads the forge's gate (pending), the verdict is written,
// then the writer writes "building" — and the pass reads pending until the next push. A head this process did not
// announce is the one case where the forge is read before writing.
test("the verdict written while a start's read of the forge is in flight stays the newest status", async () => {
const { GateWriter } = await import("../gate.ts");
const forge = aForge();
forge.kept["mesh/merge-gate"] = { state: "pending", description: "waiting", updated_at: "2026-10-11T01:08:50Z" };
const gates = new GateWriter(forge, announcedAt);
forge.holdReads();
const building = gates.started("novox", "hq", SHA, startedOnShanks);
await new Promise((r) => setImmediate(r));
const verdict = gates.verdict("novox", "hq", SHA, () =>
forge.setCommitStatus("novox", "hq", SHA, { state: "success", context: "mesh/merge-gate", description: "pass" }));
await new Promise((r) => setImmediate(r));
forge.releaseReads();
await Promise.all([building, verdict]);
assert.equal(`${forge.kept["mesh/merge-gate"].state} ${forge.kept["mesh/merge-gate"].description}`, "success pass",
`the verdict is not the newest status: ${forge.written.join(" | ")}`);
});
test("the verdict heard while a 'building' write still waits its turn: the 'building' is never written", async () => {
const { GateWriter } = await import("../gate.ts");
const forge = aForge();
const gates = new GateWriter(forge, announcedAt);
forge.holdWrites(); // the pending words are still being written
const pending = gates.pending("novox", "hq", SHA);
const building = gates.started("novox", "hq", SHA, startedOnShanks);
const verdict = gates.verdict("novox", "hq", SHA, () =>
forge.setCommitStatus("novox", "hq", SHA, { state: "failure", context: "mesh/merge-gate", description: "fail: 0 of 4 compose" }));
forge.release();
await Promise.all([pending, verdict]);
assert.equal(await building, false);
assert.equal(forge.kept["mesh/merge-gate"].state, "failure");
assert.ok(!forge.written.some((w) => w.includes("building")), forge.written.join(" | "));
});
test("a start heard after the verdict writes nothing", async () => {
const { GateWriter } = await import("../gate.ts");
const forge = aForge();
const gates = new GateWriter(forge, announcedAt);
await gates.pending("novox", "hq", SHA);
await gates.verdict("novox", "hq", SHA, () =>
forge.setCommitStatus("novox", "hq", SHA, { state: "success", context: "mesh/merge-gate", description: "pass" }));
assert.equal(await gates.started("novox", "hq", SHA, startedOnShanks), false);
assert.equal(forge.kept["mesh/merge-gate"].state, "success");
});
test("a start from before the check now pending (a run before a recheck, the stream's history) writes nothing", async () => {
const { GateWriter } = await import("../gate.ts");
const forge = aForge();
let now = announcedAt();
const gates = new GateWriter(forge, () => now);
await gates.pending("novox", "hq", SHA);
await gates.verdict("novox", "hq", SHA, () =>
forge.setCommitStatus("novox", "hq", SHA, { state: "failure", context: "mesh/merge-gate", description: "error" }));
now = new Date("2026-10-11T01:30:00Z"); // checked again: the recheck's pending, as the tool writes it (issue 456)
await gates.rechecked("novox", "hq", SHA, { state: "pending", context: "mesh/merge-gate", description: "checking again: flaky" });
assert.equal(await gates.started("novox", "hq", SHA, startedOnShanks), false, "the first run's start");
assert.equal(forge.kept["mesh/merge-gate"].description, "checking again: flaky");
assert.equal(await gates.started("novox", "hq", SHA, { ...startedOnShanks, at: "2026-10-11T01:31:00Z" }), true,
"the second run's start");
});
// **A recheck's pending goes through the one writer** (novox/hq issue 456). mesh-delivery's recheck puts the gate back
// to pending through the gitea_commit_status tool; before, that write bypassed the head's chain: the first run's
// verdict mark stayed set, so the new run never said "building", and an old run's verdict could land over the pending.
// to pending through the gitea_commit_status tool.
/** The ask of a check, as the controller's link.NewBuildID names it: when it was asked, in unix nanoseconds. */
const askOf = (at: string) => `build-${BigInt(Date.parse(at)) * 1_000_000n}`;
@@ -209,20 +111,6 @@ test("an old run's verdict in flight when the recheck comes lands before the rec
assert.equal(gateOf(forge), "pending checking again: main moved on", forge.written.join(" | "));
});
test("a rechecked head says where its new run is: building on which machine", async () => {
const { GateWriter, buildingWords } = await import("../gate.ts");
const forge = aForge();
let now = announcedAt();
const gates = new GateWriter(forge, () => now);
await gates.pending("novox", "hq", SHA);
await gates.verdict("novox", "hq", SHA, writeOf(forge, "success", "pass"), firstRun);
now = recheckAt;
await gates.rechecked("novox", "hq", SHA, checkingAgain);
const at = "2026-10-11T01:30:03Z";
assert.equal(await gates.started("novox", "hq", SHA, { ...startedOnShanks, id: secondRun, at }), true);
assert.equal(forge.kept["mesh/merge-gate"].description, buildingWords("shanks", new Date(at)));
});
test("the recheck's own verdict is written, even asked a little before the pending by another machine's clock", async () => {
const { GateWriter } = await import("../gate.ts");
const forge = aForge();
@@ -306,16 +194,6 @@ test("when a check was asked is read from its id; the opening of the check now p
assert.equal(openedAt([]), undefined);
});
test("a head this process did not announce (it restarted) is said only while the forge holds its gate pending", async () => {
const { GateWriter } = await import("../gate.ts");
const forge = aForge();
forge.kept["mesh/merge-gate"] = { state: "success", description: "pass", updated_at: "2026-10-11T01:20:00Z" };
const gates = new GateWriter(forge, announcedAt);
assert.equal(await gates.started("novox", "hq", SHA, startedOnShanks), false);
forge.kept["mesh/merge-gate"] = { state: "pending", description: "waiting", updated_at: "2026-10-11T01:08:50Z" };
assert.equal(await gates.started("novox", "hq", SHA, startedOnShanks), true);
});
test("a repository as the mesh clones it is told from another", async () => {
const { clonedAs } = await import("../gate.ts");
assert.equal(clonedAs("http://127.0.0.1:20000/novox/hq.git", "novox", "hq"), true);
@@ -325,10 +203,3 @@ test("a repository as the mesh clones it is told from another", async () => {
// Issue 1301: a build machine's start on a head that holds no mesh/merge-gate (its base requires none, so none was written)
// says nothing: no "building on" status appears on a main that does not require the gate.
test("a start on a head with no gate on the forge writes nothing", async () => {
const { GateWriter } = await import("../gate.ts");
const forge = aForge();
const gates = new GateWriter(forge, announcedAt);
assert.equal(await gates.started("novox", "hq", SHA, startedOnShanks), false);
assert.equal(forge.kept["mesh/merge-gate"], undefined);
});
+2 -153
View File
@@ -29,92 +29,6 @@ test("a pull request retargeted keeps its head and is announced again, into its
assert.notEqual(headKey("novox/de-spiegel", 6, "main"), headKey("novox/de-spiegel", 6, "old-trunk"));
});
test("a verdict is the head commit's status; an error is the forge's error, never a success", async () => {
const { statusFor, commentFor, CHECK_CONTEXT } = await import("../pulls.ts");
const base = { owner: "novox", repo: "mesh-catalog", number: 7, commit: "0123456789abcdef", id: "build-1", on: "laptop" };
assert.equal(statusFor({ ...base, verdict: "pass", summary: "every machine composes" }).state, "success");
// A warning is a note, never a blocking state: the forge combines `warning` as a failure (issue 293).
const wide = statusFor({ ...base, verdict: "warning", summary: "a merge rebuilds 14 module(s)" });
assert.equal(wide.state, "success");
assert.match(wide.description, /^pass, with a note: a merge rebuilds 14 module/);
assert.equal(statusFor({ ...base, verdict: "fail", summary: "x" }).state, "failure");
assert.equal(statusFor({ ...base, verdict: "error", summary: "the check could not run" }).state, "error");
assert.equal(statusFor({ ...base, verdict: "", summary: "" }).state, "error", "no verdict is no pass");
const long = statusFor({ ...base, verdict: "fail", summary: "y".repeat(500) });
assert.ok(long.description.length <= 140 && long.context === CHECK_CONTEXT);
assert.equal(commentFor({ ...base, verdict: "pass", summary: "fine" }), null, "a pass leaves no comment");
const said = commentFor({ ...base, verdict: "fail", summary: "lemurs refused", report: "fails:\n - ```x```" }) ?? "";
assert.match(said, /mesh\/merge-gate`: \*\*FAIL\*\*/);
assert.match(said, /01234567/);
assert.match(said, /builds --log build-1/);
assert.ok(!said.slice(said.indexOf("```") + 3, said.lastIndexOf("```")).includes("```"), "the report cannot close its own block");
});
test("each layer is its own status: the gate with the modules it judged, the repository's own check beside it", async () => {
const { statusesFor, commentFor, CHECK_CONTEXT, REPO_CHECK_CONTEXT } = await import("../pulls.ts");
const base = { owner: "novox", repo: "mesh-catalog", number: 7, commit: "0123456789abcdef", id: "build-1" };
const both = statusesFor({ ...base, verdict: "pass", summary: "every machine composes",
gate: { verdict: "pass", summary: "every machine composes", modules: ["gitea", "keycloak"], dependents: ["node-tools"] },
"repo-check": { verdict: "fail", summary: "its merge-check.sh failed: FAIL x" } });
assert.deepEqual(both.map((s) => [s.context, s.state]), [[CHECK_CONTEXT, "success"], [REPO_CHECK_CONTEXT, "failure"]]);
assert.match(both[0].description, /gitea, keycloak \+1 dependent/);
assert.match(commentFor({ ...base, verdict: "pass", summary: "", gate: { verdict: "pass", summary: "" },
"repo-check": { verdict: "fail", summary: "its merge-check.sh failed" } }) ?? "", /mesh\/repo-check`: \*\*FAIL/);
// Nothing of the graph touched, no script: a pass and a warning, and no comment on every push.
const quiet = { ...base, verdict: "pass", summary: "the change touches no module of the mesh's graph",
gate: { verdict: "pass", summary: "the change touches no module of the mesh's graph" },
"repo-check": { verdict: "warning", summary: "the repository declares no merge-check.sh" } };
assert.deepEqual(statusesFor(quiet, { defined: false, required: false }).map((s) => [s.state, s.description]),
[["success", "pass: the change touches no module of the mesh's graph"], ["success", "no repository check defined"]]);
assert.equal(commentFor(quiet, { defined: false, required: false }), null);
// A repository outside the mesh, touching nothing: the gate alone, a pass.
assert.equal(statusesFor({ ...base, verdict: "pass", summary: "x", gate: { verdict: "pass", summary: "x" } }).length, 1);
// A controller from before the layers: its verdict is the gate's.
assert.deepEqual(statusesFor({ ...base, verdict: "warning", summary: "wide" }).map((s) => [s.context, s.state]),
[[CHECK_CONTEXT, "success"]]);
});
// **A required status is success or it blocks** (novox/hq issue 293): branch protection requires
// mesh/merge-gate (and mesh/repo-check on the core repositories) with no administrator override, and the
// forge combines `warning` as a failure. No verdict ever sets `warning` on either; a note is a success that
// says it; only what a person must decide is a failure, with why.
test("no verdict sets warning on a required check; only a person's decision fails", async () => {
const { statusesFor, commentFor, UNDEFINED_REQUIRED, REPO_CHECK_CONTEXT } = await import("../pulls.ts");
const base = { owner: "novox", repo: "photos", number: 3, commit: "0123456789abcdef", id: "build-2" };
const noScript = { verdict: "warning", summary: "the repository declares no merge-check.sh" };
const quiet = { ...base, verdict: "pass", summary: "x", gate: { verdict: "pass", summary: "x" }, "repo-check": noScript };
for (const verdict of ["pass", "warning", "fail", "error", ""]) {
for (const facts of [{}, { defined: true }, { defined: false }, { defined: false, required: true }, { defined: false, required: false }]) {
const c = { ...base, verdict, summary: "s", gate: { verdict, summary: "s" }, "repo-check": { verdict, summary: "s" } };
for (const s of statusesFor(c, facts)) assert.notEqual(s.state, "warning", `${verdict} ${JSON.stringify(facts)} → ${s.context}`);
}
}
// A repository without a merge-check.sh where repo-check is not required: success, said.
assert.deepEqual(statusesFor(quiet, { defined: false, required: false })[1],
{ state: "success", context: REPO_CHECK_CONTEXT, description: "no repository check defined" });
// Where it is required — or the protection could not be read — a person decides: a failure, with why.
for (const facts of [{ defined: false, required: true }, { defined: false }]) {
const s = statusesFor(quiet, facts)[1];
assert.equal(s.state, "failure");
assert.ok(s.description.length <= 140 && UNDEFINED_REQUIRED.startsWith(s.description.replace(/…$/, "")));
assert.match(s.description, /^fail: mesh\/repo-check is required/);
assert.match(commentFor(quiet, facts) ?? "", /mesh\/repo-check`: \*\*FAIL\*\* — mesh\/repo-check is required/);
}
// A script that defines its check and said a warning itself: a note, a success.
const noted = statusesFor({ ...quiet, "repo-check": { verdict: "warning", summary: "2 tests skipped" } }, { defined: true })[1];
assert.deepEqual([noted.state, noted.description], ["success", "pass, with a note: 2 tests skipped"]);
// The gate's notes — a wide rebuild, a problem already so on the base — are successes that say so.
const already = statusesFor({ ...base, verdict: "warning", summary: "s",
gate: { verdict: "warning", summary: "the module check's problems were all so on main already" } })[0];
assert.deepEqual([already.state, already.description],
["success", "pass, with a note: the module check's problems were all so on main already"]);
assert.match(commentFor({ ...base, verdict: "warning", summary: "wide", gate: { verdict: "warning", summary: "wide" } }) ?? "",
/PASS, WITH A NOTE/);
});
test("a commit status is set on the commit, under the merge check's context", async () => {
const { GiteaClient } = await import("../client.ts");
let seen: { path: string; body: any } | null = null;
@@ -140,24 +54,6 @@ test("a commit status is set on the commit, under the merge check's context", as
assert.equal(seen!.body.context, "mesh/merge-gate");
});
test("a change plan is the gate's result: said on the status and, when it builds something, as a comment", async () => {
const { statusFor, commentFor } = await import("../pulls.ts");
const plan = {
repository: "novox/mesh-catalog", base: "main", head: "0123456789abcdef", moved: ["gitea"],
tiers: [["gitea"]], machines: [{ machine: "anchor", receives: ["gitea"] }], steps: [],
summary: "builds gitea → anchor; no bus step",
};
const c = { owner: "novox", repo: "mesh-catalog", number: 7, commit: "0123456789abcdef", id: "b", verdict: "pass",
summary: "every machine composes", gate: { verdict: "pass", summary: "every machine composes", modules: ["gitea"] }, plan };
assert.equal(statusFor(c).description, "pass: builds gitea → anchor; no bus step; every machine composes");
const said = commentFor(c) ?? "";
assert.match(said, /Delivery plan\*\* — builds gitea → anchor/);
assert.match(said, /- anchor: receives gitea/);
// A plan that builds nothing, passing: the statuses say it, no comment.
const nothing = { ...c, plan: { ...plan, moved: [], tiers: [], machines: [], summary: "builds nothing" } };
assert.equal(commentFor(nothing), null);
});
// **A pending check says where it is** (novox/hq issue 446): announced, it waits in the build queue, since the time the
// operator reads — not "mapping this head onto its module graph" for the ten minutes of a controller check.
test("a head announced is pending in the build queue, since the local time it was announced", async () => {
@@ -166,48 +62,10 @@ test("a head announced is pending in the build queue, since the local time it wa
assert.equal(pendingWords(new Date(2026, 9, 11, 14, 0, 0)), "waiting in the build queue since 14:00");
});
// **A group's composed verdict is each member's merge gate** (novox/hq ADR 0403 §1, issue 978): set at the head the
// composed check read, naming the trunk commit it was merged onto, the group, and what the check said.
test("a composed verdict sets every member's gate at its own head, naming its trunk and the group", async () => {
const { groupGateStatus, CHECK_CONTEXT } = await import("../pulls.ts");
const c = { owner: "", repo: "", commit: "", id: "build-1", group: "feat/x", verdict: "pass", summary: "both compose",
members: [{ owner: "novox", repo: "mesh-controller", number: 1, commit: "c".repeat(40), trunk: "a".repeat(40) },
{ owner: "novox", repo: "mesh-catalog", number: 2, commit: "d".repeat(40) }] };
const [first, second] = c.members.map((m) => groupGateStatus(c, m));
assert.equal(first.context, CHECK_CONTEXT);
assert.equal(first.state, "success");
assert.match(first.description, /^head cccccccccccc merged into trunk aaaaaaaaaaaa — pass: group feat\/x composed: both compose/);
assert.equal(second.state, "success");
assert.match(second.description, /^pass: group feat\/x composed: both compose/, "no trunk named, none said");
// A composed check that failed is every member's failure, never a success.
assert.equal(groupGateStatus({ ...c, verdict: "fail", summary: "a and b do not compose" }, c.members[0]).state, "failure");
assert.equal(groupGateStatus({ ...c, verdict: "error", summary: "could not run" }, c.members[0]).state, "error");
});
// A member's own verdict whose gate layer is the group's composed check's sets its repository check only, and never the
// gate as an error: the gate stays pending until the composed verdict lands, in either order (ADR 0403 §1, issue 978).
test("a member's own verdict with a composed gate layer leaves the gate to the composed verdict, in either order", async () => {
const { statusesFor, composedGate, groupGateStatus, CHECK_CONTEXT, REPO_CHECK_CONTEXT } = await import("../pulls.ts");
const c = { owner: "novox", repo: "mesh-catalog", number: 2, commit: "d".repeat(40), id: "build-1", verdict: "pass", summary: "x",
gate: { verdict: "composed", summary: "judged by its group" }, "repo-check": { verdict: "pass", summary: "suite passed" } };
assert.ok(composedGate(c));
// Before the composed verdict: the repository check, and the gate pending, saying it waits.
const before = statusesFor(c, {}, undefined);
assert.deepEqual(before.map((s) => [s.context, s.state]), [[CHECK_CONTEXT, "pending"], [REPO_CHECK_CONTEXT, "success"]]);
assert.match(before[0].description, /waits for its group's composed check/);
assert.ok(!before.some((s) => s.state === "error"));
// After it: the standing composed gate is left alone.
const standing = groupGateStatus({ ...c, group: "feat/x", members: [] }, { owner: "novox", repo: "mesh-catalog", commit: c.commit }).description;
const after = statusesFor(c, {}, standing);
assert.deepEqual(after.map((s) => [s.context, s.state]), [[REPO_CHECK_CONTEXT, "success"]]);
// A verdict with an ordinary gate is as before.
assert.equal(composedGate({ ...c, gate: { verdict: "pass", summary: "x" } }), false);
});
// Issue 1301 (ADR 0503 §3, §5): gitea writes the controller's statuses only where the base still requires them. hq's main
// requires mesh/repo-check alone: its verdict sets mesh/repo-check and no mesh/merge-gate, and its comment names no gate.
// requires mesh/repo-check alone, so no mesh/merge-gate is written on its heads.
test("a base's required statuses decide which of the controller's statuses are written", async () => {
const { maintained, writesGate, statusesFor, commentFor, CHECK_STATUS } = await import("../pulls.ts");
const { maintained, writesGate, CHECK_STATUS } = await import("../pulls.ts");
assert.equal(CHECK_STATUS, "mesh/check");
assert.deepEqual(maintained(undefined), ["mesh/merge-gate", "mesh/repo-check"], "a rule not read: as before");
assert.deepEqual(maintained(["mesh/repo-check"]), ["mesh/repo-check"]);
@@ -217,15 +75,6 @@ test("a base's required statuses decide which of the controller's statuses are w
assert.equal(writesGate(["mesh/repo-check"]), false);
assert.equal(writesGate(["mesh/check"]), false);
assert.equal(writesGate(["mesh/merge-gate", "mesh/repo-check"]), true);
const c = { owner: "novox", repo: "hq", number: 5, commit: "a".repeat(40), id: "x", verdict: "warning", summary: "no script",
trunk: "b".repeat(12), "repo-check": { verdict: "pass", summary: "ok" } } as any;
const hq = statusesFor(c, {}, undefined, ["mesh/repo-check"]);
assert.deepEqual(hq.map((s: any) => s.context), ["mesh/repo-check"]);
assert.match(hq[0].description, /merged into trunk bbbbbbbbbbbb/);
assert.deepEqual(statusesFor(c).map((s: any) => s.context), ["mesh/merge-gate", "mesh/repo-check"], "unread: as before");
assert.deepEqual(statusesFor(c, {}, undefined, ["mesh/check"]), []);
const comment = commentFor(c, {}, ["mesh/repo-check"]);
assert.ok(comment === null || !comment.includes("mesh/merge-gate"), comment ?? "");
});
test("a rule the forge gives with no contexts (null) requires nothing, and an unread rule is unknown", async () => {
-38
View File
@@ -13,7 +13,6 @@ import { join } from "node:path";
import { approvedHeadsOf, besideWords, groupRefusal, keepSiblings, namesSiblings, noteVerdict, readSiblings, siblingOutOfDate, siblingsFile,
siblingsOf, siblingStatuses, weighSiblings, type SiblingRecord } from "../dist/siblings.js";
import { weighTrunks } from "../dist/trunk.js";
import { commentFor, statusesFor } from "../dist/pulls.js";
const h = (c: string) => c.repeat(40);
const MEMBER_HEAD = h("a"), HOST_HEAD = h("b"), HOST_BASE = h("c"), HOST_NEW = h("d"), HOST_MOVED = h("e"), MERGE = h("f");
@@ -22,43 +21,6 @@ const verdict = { owner: "novox", repo: "mesh-controller", number: 346, commit:
trunk: h("1").slice(0, 12), siblings: [sibling], judge_from: "sibling novox/mesh-controller#346 merge 123456789abc",
gate: { verdict: "pass", summary: "every machine composes ".repeat(10) }, "repo-check": { verdict: "pass", summary: "ok" } };
test("both statuses name the siblings the verdict read, keeping the verdict word and the start of its reason within 140 characters", () => {
// A realistic controller summary for a failure, long enough that the forge clips it.
const reason = "2 of 4 machines do not compose: ace: mesh-host.service needs seat node-reads, which no module on ace claims after this change";
const c = { ...verdict, gate: { verdict: "fail", summary: reason }, "repo-check": { verdict: "fail", summary: "its merge-check.sh failed: internal/link/check.go:145:2: undefined" } };
const [gate, repo] = statusesFor(c);
for (const s of [gate, repo]) {
assert.ok(s.description.startsWith(`head ${MEMBER_HEAD.slice(0, 12)} merged into trunk 111111111111 beside mesh-host#114@bbbbbbbb — fail: `), s.description);
assert.ok(s.description.length <= 140, `${s.description.length} characters`);
assert.ok(namesSiblings(s.description));
}
assert.match(gate.description, /fail: 2 of 4 machines do not compose: ace: mesh-host/, gate.description);
assert.match(repo.description, /fail: its merge-check\.sh failed: internal/, repo.description);
assert.equal(besideWords([...siblingsOf([sibling]), ...siblingsOf([{ ...sibling, number: 115 }])]), "beside mesh-host#114@bbbbbbbb +1 more");
const comment = commentFor({ ...verdict, gate: { verdict: "fail", summary: "no" } })!;
assert.match(comment, /Siblings read beside it: novox\/mesh-host#114 `bbbbbbbbbbbb` merged onto `cccccccccccc` \(main\); judged by sibling novox\/mesh-controller#346 merge/);
});
test("a verdict that could not learn its siblings says so on the status and in the comment, and names none", () => {
const { siblings, ...unknown } = { ...verdict, siblings_unknown: "mesh-delivery did not answer" };
const reason = "2 of 4 machines do not compose: ace: mesh-host.service needs seat node-reads, which no module claims";
for (const s of statusesFor({ ...unknown, gate: { verdict: "fail", summary: reason } })) {
assert.match(s.description, /^head aaaaaaaaaaaa merged into trunk 111111111111 siblings unknown — (fail|pass): /, s.description);
assert.ok(s.description.length <= 140);
assert.equal(namesSiblings(s.description), false, "it read no siblings, so the merge tool asks for none");
}
assert.match(commentFor({ ...unknown, gate: { verdict: "fail", summary: "no" } })!, /Siblings could not be learned: mesh-delivery did not answer/);
});
test("a verdict without siblings is said exactly as before (an older controller)", () => {
const { siblings, judge_from, ...old } = verdict;
const [gate] = statusesFor(old);
assert.equal(namesSiblings(gate.description), false);
assert.match(gate.description, /^head aaaaaaaaaaaa merged into trunk 111111111111 — pass: /);
assert.deepEqual(siblingsOf(undefined), []);
assert.deepEqual(siblingsOf([{ repository: "novox/x" }]), [], "a sibling named without its head is none");
});
test("the siblings a verdict names are kept per member, replaced by its next verdict, and removed by one that read none", () => {
const file = siblingsFile(mkdtempSync(join(tmpdir(), "gitea-siblings-")));
const record: SiblingRecord = {};
-11
View File
@@ -17,7 +17,6 @@ import { join } from "node:path";
import { MintedToken } from "../dist/token.js";
import { GiteaClient } from "../dist/client.js";
import { TOOLS_ACCOUNT, TOOLS_TEAM_UNITS, BUILDER_ACCOUNT, AGENT_ACCOUNT, meshAccounts } from "../dist/tools-account.js";
import { repoCheckFacts } from "../dist/pulls.js";
const ADMIN = "mesh-admin";
const PASSWORD = "the-vault-minted-this";
@@ -364,16 +363,6 @@ test("the manifest hands the tools their account, and the Go forge bundle holds
// Review of issue 469: what reads the rules' list is moved to the branch, and nothing leaves the tools without a token.
test("a repository check said as a warning on a head without merge-check.sh reads the base's protection from the branch", async () => {
const client = fresh(await environment());
const forgeOf = { holdsFile: async () => false, appliedProtection: client.appliedProtection.bind(client) };
const c = { owner: "novox", repo: "hq", commit: "abc123def4567890abc123def4567890abc12345", "repo-check": { verdict: "warning" } } as any;
// main requires mesh/repo-check: required, so a head that defines no check fails it for a person to see.
assert.deepEqual(await repoCheckFacts(forgeOf, c, "main"), { defined: false, required: true });
// A base nothing guards: not required, so the head's repository check passes rather than fails.
assert.deepEqual(await repoCheckFacts(forgeOf, c, "feat/unguarded"), { defined: false, required: false });
});
test("a mint that fails leaves the administrator's old token in place: the tools are never left without one", async () => {
const env = await environment();
const old = { id: 901, name: "mesh-tools", sha1: "sha-admin-kept", user: ADMIN, scopes: ["read:user"] };
-26
View File
@@ -8,37 +8,11 @@ import assert from "node:assert/strict";
import { checkOnItsWay, lateVerdictComment, outOfDateStatuses, outOfDateWords, trunkNamed, verdictAtTrunk, weighTrunks } from "../dist/trunk.js";
import { GateWriter } from "../dist/gate.js";
import { statusesFor, commentFor } from "../dist/pulls.js";
const HEAD = "dfb1415aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
const TRUNK = "a2c2c35bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb";
const MOVED = "d28ca95ccccccccccccccccccccccccccccccccc";
test("both statuses say the head and the trunk commit the verdict judged, first, whatever the summary's length", () => {
const c = { owner: "novox", repo: "mesh-host", number: 66, commit: HEAD, verdict: "pass", summary: "x", id: "build-1",
trunk: TRUNK.slice(0, 12), beside: { "mesh-catalog": "0123456789ab", "mesh-lab": "ba9876543210" },
gate: { verdict: "pass", summary: "4 of 4 compose ".repeat(20) },
"repo-check": { verdict: "fail", summary: "its merge-check.sh failed: internal/store/store.go:145:2: Declared redeclared" } };
const [gate, repo] = statusesFor(c);
for (const s of [gate, repo]) {
assert.ok(s.description.startsWith("head dfb1415aaaaa merged into trunk a2c2c35bbbbb — "), s.description);
assert.ok(s.description.length <= 140, `${s.description.length} characters`);
assert.equal(trunkNamed(s.description), "a2c2c35bbbbb");
}
assert.equal(gate.state, "success");
assert.equal(repo.state, "failure");
const comment = commentFor(c)!;
assert.match(comment, /Judged: head `dfb1415aaaaa` merged into trunk `a2c2c35bbbbb`; read beside it: mesh-catalog `0123456789ab`, mesh-lab `ba9876543210`/);
// A verdict of a build seat from before the rule names no trunk, and is said as before.
const [old] = statusesFor({ ...c, trunk: undefined, gate: { verdict: "pass", summary: "every machine composes" } });
assert.equal(old.description, "pass: every machine composes");
assert.equal(trunkNamed(old.description), undefined);
// ADR 0548 §8: a carried verdict's line names the head on the new trunk first, so the merge tool merges it on that trunk.
assert.equal(trunkNamed(`pass ${HEAD.slice(0, 12)} on ${MOVED.slice(0, 12)} | carried from ${TRUNK.slice(0, 12)}, mesh-catalog 0123456789ab | unit 0 run, 4 kept`),
MOVED.slice(0, 12));
});
test("a trunk's move puts an open head's merge check out of date, naming both commits, unless a check is on its way", () => {
const verdict = (trunk: string, state = "success") => ({
"mesh/merge-gate": { state, description: `head ${HEAD.slice(0, 12)} merged into trunk ${trunk.slice(0, 12)} — pass: x` },