Files
mesh-catalog/modules/gitea/test/delivery.test.ts
T
jochen 74f912e1fb
mesh/merge-gate pass: builds gitea, mesh-delivery → novox; no bus step; every machine composes with the change as it did without (4 of 4 compose)
mesh/repo-check pass: its merge-check.sh passed
mesh/delivery delivered
Put the merge check back to pending before a recheck asks it (hq issue 305)
A recheck asks of the head the forge already judged, and the forge keeps
the newest status of each context on it. Nobody reset mesh/merge-gate or
mesh/repo-check, so the old green stood and branch protection would merge
on it while the fresh check ran; rechecked heads did turn red.

mesh-delivery's recheck now asks the forge's holder to set the gate, and
the repository check when the head holds one, to pending ("checking
again: <why>") before it asks the controller; a forge that cannot be told
refuses the recheck whole. The gitea holder takes pending, and only
pending, on the merge check's statuses by hand: no verdict, so issue 293
still holds.
2026-10-08 10:11:26 +02:00

64 lines
4.1 KiB
TypeScript

import assert from "node:assert/strict";
import { test } from "node:test";
// What the forge's holder does for a delivery (novox/hq ADR 0239): a note appended once, in the forge's own
// repository as its own user; one view per pull request; only the mesh's statuses.
test("a note line is appended once, as the forge's user, in its own repository", async () => {
const { appendNote, noteArgs } = await import("../delivery.ts");
const notes: Record<string, string[]> = {};
const calls: string[][] = [];
const runner = async (file: string, args: string[]) => {
calls.push([file, ...args]);
const commit = args[args.length - 1];
if (args.includes("show")) {
const lines = notes[commit];
return lines ? { stdout: lines.join("\n") + "\n", code: 0 } : { stdout: "", code: 1 };
}
const line = args[args.indexOf("-m") + 1];
(notes[commit] ??= []).push(line);
return { stdout: "", code: 0 };
};
const sha = "0123456789abcdef0123456789abcdef01234567";
assert.deepEqual(await appendNote(runner, "gitea", "Novox", "Mesh-Catalog", sha, "mesh-plan", "a -> b\n(merged)"),
{ added: true, lines: 1 });
assert.deepEqual(await appendNote(runner, "gitea", "Novox", "Mesh-Catalog", sha, "mesh-plan", "a -> b (merged)"),
{ added: false, lines: 1 }, "the same line twice adds nothing");
assert.deepEqual(await appendNote(runner, "gitea", "Novox", "Mesh-Catalog", sha, "mesh-plan", "b -> c"),
{ added: true, lines: 2 });
const append = calls.find((c) => c.includes("append"))!;
assert.deepEqual(append.slice(0, 7), ["docker", "exec", "-u", "git", "gitea", "git", "--git-dir"]);
assert.equal(append[7], "/data/git/repositories/novox/mesh-catalog.git");
assert.ok(append.includes("--ref=mesh-plan"));
assert.throws(() => noteArgs("gitea", "novox", "x; rm -rf /", sha, "mesh-plan"), /not a repository/);
assert.throws(() => noteArgs("gitea", "novox", "x", "HEAD", "mesh-plan"), /not a commit/);
assert.throws(() => noteArgs("gitea", "novox", "x", sha, "../commits"), /not a notes ref/);
});
test("a note that cannot be written is said, never read as written", async () => {
const { appendNote } = await import("../delivery.ts");
const runner = async (_file: string, args: string[]) => ({ stdout: "", code: args.includes("append") ? 128 : 1 });
await assert.rejects(appendNote(runner, "gitea", "novox", "x", "abcdef1234567", "mesh-plan", "l"), /could not be appended/);
});
test("one view per pull request, found by its marker; only the mesh's statuses", async () => {
const { viewBody, viewComment, deliveryStatus, VIEW_MARKER } = await import("../delivery.ts");
assert.ok(viewBody("**Delivery**").startsWith(VIEW_MARKER));
assert.equal(viewBody(`${VIEW_MARKER}\nx`), `${VIEW_MARKER}\nx`, "a marked body is kept as it is");
const comments = [{ id: 1, body: "a review" }, { id: 2, body: `${VIEW_MARKER}\nold` }, { id: 3, body: `${VIEW_MARKER}\nlater` }];
assert.equal(viewComment(comments)?.id, 2);
assert.equal(viewComment([{ id: 1, body: "x" }]), undefined);
const s = deliveryStatus("mesh/delivery", "pending", "y".repeat(300), "https://forge.invalid/novox/x/pulls/1");
assert.ok(s.description.length <= 140 && s.target_url);
assert.throws(() => deliveryStatus("ci/other", "success", "x"), /mesh's own/);
assert.throws(() => deliveryStatus("mesh/delivery", "green", "x"), /not a status/);
// The merge check's statuses are its verdict's alone: required by a branch, they are never set by hand.
assert.throws(() => deliveryStatus("mesh/merge-gate", "success", "x"), /merge check's status/);
assert.throws(() => deliveryStatus("mesh/repo-check", "warning", "x"), /merge check's status/);
assert.throws(() => deliveryStatus("mesh/merge-gate", "failure", "x"), /merge check's status/);
// Pending is no verdict: a recheck says it so the old verdict does not let the head merge meanwhile.
assert.equal(deliveryStatus("mesh/merge-gate", "pending", "checking again: main moved on").state, "pending");
assert.equal(deliveryStatus("mesh/repo-check", "pending", "checking again: main moved on").context, "mesh/repo-check");
assert.equal(deliveryStatus("mesh/delivery", "success", "x", "javascript:alert(1)").target_url, undefined);
});