Files
mesh-catalog/modules/gitea/test/pulls.test.ts
T
jochen dba71a97a8 gitea: tell the controller which files a pull request deletes; say the dependents a merge would build (hq ADR 0238)
The controller asks its planner what a pull request reaches, as if merged: a module whose
manifest the change deletes is one the merge removes, and the plan's dependents are said
on the gate's status beside the modules it moves.
2026-10-06 22:34:53 +02:00

86 lines
5.5 KiB
TypeScript

import assert from "node:assert/strict";
import { test } from "node:test";
import { createServer } from "node:http";
// A pull request's merge check (novox/hq to-be 45 §9): each new head announced once, and the verdict set
// where the pull request shows it — an error never as a success.
test("each open pull request's new head is announced once, and nothing closed or merged", async () => {
const { headsToAnnounce } = await import("../pulls.ts");
const pulls = [
{ number: 1, title: "a", state: "open", merged: false, head_sha: "aaa", html_url: "" },
{ number: 2, title: "b", state: "open", merged: false, head_sha: "bbb", html_url: "" },
{ number: 3, title: "c", state: "closed", merged: true, head_sha: "ccc", html_url: "" },
{ number: 4, title: "d", state: "open", merged: false, html_url: "" },
];
const announced = { "novox/mesh-catalog#1": "aaa", "novox/mesh-catalog#2": "old" };
assert.deepEqual(headsToAnnounce("novox/mesh-catalog", pulls, announced).map((p) => p.number), [2],
"only a head not announced, of a pull request that is open");
});
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");
assert.equal(statusFor({ ...base, verdict: "warning", summary: "a merge rebuilds 14 module(s)" }).state, "warning");
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).map((s) => s.state), ["success", "warning"]);
assert.equal(commentFor(quiet), 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, "warning"]]);
});
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;
const server = createServer((req, res) => {
let raw = "";
req.on("data", (c) => (raw += c));
req.on("end", () => {
seen = { path: `${req.method} ${req.url}`, body: JSON.parse(raw) };
res.statusCode = 201;
res.end("{}");
});
});
await new Promise<void>((r) => server.listen(0, r));
const port = (server.address() as any).port;
const client = new GiteaClient(`http://127.0.0.1:${port}`, "t");
await client.setCommitStatus("novox", "mesh-catalog", "abc123", { state: "failure", context: "mesh/merge-gate", description: "x" });
server.close();
assert.equal(seen!.path, "POST /api/v1/repos/novox/mesh-catalog/statuses/abc123");
assert.equal(seen!.body.state, "failure");
assert.equal(seen!.body.context, "mesh/merge-gate");
});