93 lines
4.3 KiB
TypeScript
93 lines
4.3 KiB
TypeScript
import assert from "node:assert/strict";
|
|
import { test } from "node:test";
|
|
|
|
// **One writer of a head's mesh/merge-gate** (novox/hq issue 446): the announcement's pending words, a recheck's pending and
|
|
// a trunk's move, each write of a head in the order it was asked.
|
|
|
|
const SHA = "a6b08de8507c1703528277e5cefc80548dbc9bb3";
|
|
|
|
/** 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 }> = {};
|
|
const written: string[] = [];
|
|
let failNext = 0;
|
|
let hold: Promise<void> | null = null;
|
|
let release = () => {};
|
|
return {
|
|
kept, written,
|
|
/** The next writes fail: a request the forge did not take. */
|
|
failWrites(n = 1) {
|
|
failNext = n;
|
|
},
|
|
/** The next writes wait until released: a request in flight. */
|
|
holdWrites() {
|
|
hold = new Promise<void>((r) => (release = r));
|
|
},
|
|
release() {
|
|
hold = null;
|
|
release();
|
|
},
|
|
async headChecks() {
|
|
return {};
|
|
},
|
|
async setCommitStatus(_o: string, _r: string, _sha: string, s: { state: string; context: string; description: string }) {
|
|
if (hold) await hold;
|
|
if (failNext > 0) {
|
|
failNext--;
|
|
throw new Error("the forge answered 502");
|
|
}
|
|
kept[s.context] = { state: s.state, description: s.description, updated_at: new Date().toISOString() };
|
|
written.push(`${s.state} ${s.description}`);
|
|
},
|
|
};
|
|
}
|
|
|
|
// **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.
|
|
|
|
const recheckAt = new Date("2026-10-11T01:30:00Z");
|
|
const checkingAgain = { state: "pending" as const, context: "mesh/merge-gate", description: "checking again: main moved on" };
|
|
const gateOf = (forge: ReturnType<typeof aForge>) =>
|
|
`${forge.kept["mesh/merge-gate"].state} ${forge.kept["mesh/merge-gate"].description}`;
|
|
|
|
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);
|
|
assert.equal(clonedAs("http://127.0.0.1:20000/novox/mesh-hq.git", "novox", "hq"), false);
|
|
assert.equal(clonedAs("http://127.0.0.1:20000/novox/mesh-controller.git", "novox", "hq"), false);
|
|
});
|
|
|
|
test("a recheck the forge does not take fails its caller, and the next write still takes its turn", async () => {
|
|
const { GateWriter } = await import("../gate.ts");
|
|
const forge = aForge();
|
|
const gates = new GateWriter(forge, () => recheckAt);
|
|
await gates.pending("novox", "hq", SHA);
|
|
forge.failWrites();
|
|
await assert.rejects(gates.rechecked("novox", "hq", SHA, checkingAgain));
|
|
await gates.rechecked("novox", "hq", SHA, checkingAgain);
|
|
assert.equal(gateOf(forge), "pending checking again: main moved on");
|
|
});
|
|
|
|
test("the status tool writes the merge check's pending through the writer, and the mesh's other statuses as they were", async () => {
|
|
const { GateWriter } = await import("../gate.ts");
|
|
const { commitStatusTool } = await import("../delivery.ts");
|
|
const forge = aForge();
|
|
const direct: string[] = [];
|
|
const gates = new GateWriter(forge, () => recheckAt);
|
|
const tool = commitStatusTool({
|
|
setCommitStatus: async (_o, _r, _sha, s) => void direct.push(`${s.context} ${s.state}`),
|
|
gates,
|
|
});
|
|
await tool.run({ owner: "novox", repo: "hq", sha: SHA, context: "mesh/merge-gate", state: "pending", description: "checking again: x" });
|
|
await tool.run({ owner: "novox", repo: "hq", sha: SHA, context: "mesh/repo-check", state: "pending", description: "checking again: x" });
|
|
await tool.run({ owner: "novox", repo: "hq", sha: SHA, context: "mesh/delivery", state: "success", description: "delivered" });
|
|
assert.equal(gateOf(forge), "pending checking again: x");
|
|
assert.equal(forge.kept["mesh/repo-check"].state, "pending");
|
|
assert.deepEqual(direct, ["mesh/delivery success"]);
|
|
// A head named short is another head to the writer: refused, nothing sent.
|
|
await assert.rejects(tool.run({ owner: "novox", repo: "hq", sha: SHA.slice(0, 12), context: "mesh/merge-gate", state: "pending" }),
|
|
/whole sha/);
|
|
await assert.rejects(tool.run({ owner: "novox", repo: "hq", sha: SHA, context: "mesh/merge-gate", state: "success" }),
|
|
/set by its verdict only/);
|
|
});
|