Files
mesh-catalog/modules/gitea/test/announce.test.ts
T

82 lines
6.5 KiB
TypeScript

import assert from "node:assert/strict";
import { test } from "node:test";
// **The forge's announcements carry a message id** (novox/hq ADR 0406 §6, §7, issue 976): the same announcement,
// however many paths make it — the poll, the forge's event, the merge tool's ask — has one id, which the runtime makes
// the bus's message id, so the bus stores it once. This replaces the files of what was announced.
const merged = { owner: "novox", repo: "app", number: 7, head_sha: "aaa111", merge_commit_sha: "mmm222", base: "main" };
const updated = { owner: "novox", repo: "app", number: 7, head_sha: "aaa111", base_sha: "ttt333", base: "main" };
const closed = { owner: "novox", repo: "app", number: 7, head_sha: "aaa111", base: "main" };
test("the same merge, head or close announced twice has the same id, and another has another", async () => {
const { announcementId } = await import("../announce.ts");
assert.equal(announcementId("pull.merged", merged), "merged:novox/app#7@mmm222");
assert.equal(announcementId("pull.merged", { ...merged, title: "worded differently" }), announcementId("pull.merged", merged));
assert.notEqual(announcementId("pull.merged", { ...merged, merge_commit_sha: "other" }), announcementId("pull.merged", merged));
assert.equal(announcementId("pull.updated", updated), "updated:novox/app#7@aaa111:ttt333");
assert.notEqual(announcementId("pull.updated", { ...updated, base_sha: "moved" }), announcementId("pull.updated", updated),
"the check asked again against a new trunk commit is another announcement");
assert.notEqual(announcementId("pull.updated", { ...updated, head_sha: "bbb" }), announcementId("pull.updated", updated));
assert.equal(announcementId("pull.updated", { ...updated, base_sha: undefined }), "updated:novox/app#7@aaa111:");
assert.equal(announcementId("pull.closed", closed), "closed:novox/app#7@aaa111");
});
test("an announcement is said on the seat git with its id, in the contract's body, never the description", async () => {
const { useRuntime } = await import("@novox/mesh-sdk/runtime");
const said: any[] = [];
useRuntime(async (method, params) => { said.push({ method, ...(params as object) }); return null; });
const { announce } = await import("../announce.ts");
const { seatEventOf, SEAT_ONLY } = await import("../seatevents.ts");
await announce("pull.merged", { ...merged, body: "a description", head: "feat/x", paths: ["a/b.go"], removed: [] });
await announce("pull.merged", { ...merged });
useRuntime(undefined);
assert.equal(said.length, 2);
assert.deepEqual([said[0].method, said[0].subject, said[0].id], ["mesh/seat.publish", "mesh.seat.git.event.pull-merged", "merged:novox/app#7@mmm222"]);
assert.equal(said[1].id, said[0].id, "one id for one announcement");
assert.deepEqual(said[0].body, { repository: "novox/app", number: 7, base: "main", branch: "feat/x", head: "aaa111",
"merge-commit": "mmm222", change: { paths: ["a/b.go"] } });
// Every event the seat says, from what the module's paths make; the others are none of the seat's.
const rows: [string, Record<string, unknown>, string | undefined][] = [
["pull.updated", updated, "mesh.seat.git.event.pull-updated"],
["pull.closed", closed, "mesh.seat.git.event.pull-closed"],
["forge.status", { full_name: "novox/app", sha: "aaa111", context: "mesh/check", state: "success" }, "mesh.seat.git.event.forge-status"],
["forge.delete", { owner: "novox", repo: "app", ref: "feat/x", ref_type: "branch" }, "mesh.seat.git.event.forge-delete"],
["repo.writable", { owner: "novox", repo: "app", account: "mesh-tools" }, "mesh.seat.git.event.repo-writable"],
["forge.pull-request.synchronized", { full_name: "novox/app", number: 7, head_sha: "aaa111" }, "mesh.seat.git.event.forge-pull"],
["forge.pull-request.labeled", { full_name: "novox/app", number: 7 }, undefined],
["forge.push", { full_name: "novox/app", ref: "refs/tags/v1", after: "a".repeat(40) }, undefined],
["forge.push", { full_name: "novox/app", ref: "refs/heads/gone", after: "0".repeat(40) }, undefined],
["forge.push", { full_name: "novox/app", ref: "refs/heads/feat/x", after: "a".repeat(40) }, "mesh.seat.git.event.branch-moved"],
];
for (const [type, body, subject] of rows) assert.equal(seatEventOf(type, body)?.subject, subject, type);
assert.deepEqual(seatEventOf("pull.updated", updated)?.body, { repository: "novox/app", number: 7, base: "main", branch: "",
head: "aaa111", "base-head": "ttt333" });
assert.equal(seatEventOf("forge.pull-request.edited", { full_name: "novox/app", number: 7, body: "x" })?.body &&
"body" in (seatEventOf("forge.pull-request.edited", { full_name: "novox/app", number: 7, body: "x" })!.body as object), false);
// A push of a branch (a merge and an admitted push alike) says branch-moved with the new head, one id per move.
const moved = seatEventOf("forge.push", { full_name: "novox/app", ref: "refs/heads/main", after: "b".repeat(40) }, () => new Date("2026-11-11T10:00:00Z"));
assert.deepEqual(moved?.body, { repository: "novox/app", branch: "main", head: "b".repeat(40), at: "2026-11-11T10:00:00.000Z" });
assert.equal(moved?.id, `moved:novox/app:main@${"b".repeat(40)}`);
assert.deepEqual([...SEAT_ONLY].sort(), ["forge.delete", "forge.status", "pull.closed", "pull.merged", "pull.updated", "repo.writable"]);
});
test("an announcement of a kind it has no id for is refused, never published with a made-up one", async () => {
const { announce, announcementId } = await import("../announce.ts");
assert.equal(announcementId("repo.created", { owner: "a" }), undefined);
await assert.rejects(() => announce("repo.created", { owner: "a" }), /not an announcement with a message id/);
});
test("the watermark is kept in the module's state, and an unreachable state is no record", async () => {
const { readWatermark, keepWatermark } = await import("../announce.ts");
const kept = new Map<string, unknown>();
const store = { get: async (k: string) => (kept.has(k) ? { key: k, value: kept.get(k), revision: 1 } : null),
put: async (k: string, v: unknown) => { kept.set(k, v); return 1; } };
assert.equal(await readWatermark(store as any), "", "none yet");
await keepWatermark("2026-10-17T12:00:00.000Z", store as any);
assert.equal(await readWatermark(store as any), "2026-10-17T12:00:00.000Z");
const away = { get: async () => { throw new Error("no runtime"); }, put: async () => { throw new Error("no runtime"); } };
assert.equal(await readWatermark(away as any), "", "a state that cannot be reached is no record");
await keepWatermark("2026-10-17T12:01:00.000Z", away as any); // said, not thrown
});