import assert from "node:assert/strict"; import { mock, test } from "node:test"; import type { GiteaPull } from "../client.ts"; // **No poll of the forge** (issue 1422, notes 10 and 11): the forge's events announce heads and merges; a pass runs once at // the start and when the receiver listens. const edited = (base: string) => ({ name: "forge.pull-request.edited", body: { owner: "novox", repo: "hq", number: 7, base, merged: false } }); function deps() { const calls: string[] = []; return { calls, d: { getRepo: async () => ({ r: 1 }), getPull: async () => ({ state: "open", base: "trunk" }) as any, announceHead: async () => { calls.push("head"); }, announceMerge: async () => { calls.push("merge"); }, announceClosed: async () => { calls.push("closed"); }, siblingMoves: async () => { calls.push("siblings"); }, } }; } test("an edited event announces the head into the base it now has, without a poll", async () => { const { actOn } = await import("../watch.ts"); const { calls, d } = deps(); await actOn(edited("trunk"), d); assert.deepEqual(calls, ["head"]); }); test("a close without a merge is announced by its event, a merged close is left to the merge event", async () => { const { actOn } = await import("../watch.ts"); const { calls, d } = deps(); await actOn({ name: "forge.pull-request.closed", body: { owner: "novox", repo: "hq", number: 7, merged: false } }, d); assert.deepEqual(calls, ["closed", "siblings"]); calls.length = 0; await actOn({ name: "forge.pull-request.closed", body: { owner: "novox", repo: "hq", number: 7, merged: true } }, d); assert.deepEqual(calls, []); }); test("no timer polls after the start: one pass ends and nothing is scheduled; a failed one is asked again", async () => { const { startPass } = await import("../watch.ts"); mock.timers.enable({ apis: ["setTimeout", "setInterval"] }); try { let runs = 0; await startPass(async () => { runs++; }, { retryMs: 60_000, log: () => {} }); mock.timers.tick(24 * 3600_000); assert.equal(runs, 1, "a day later the forge was not asked again"); let tries = 0; const retried = startPass(async () => { if (++tries < 2) throw new Error("forge down"); }, { retryMs: 60_000, log: () => {} }); await Promise.resolve(); await Promise.resolve(); mock.timers.tick(60_000); await retried; assert.equal(tries, 2); } finally { mock.timers.reset(); } }); test("a start pass announces a head nobody announced, and only remembers one the forge shows a check of", async () => { const { announceNewHeads } = await import("../watch.ts"); const pull = (n: number, sha: string): GiteaPull => ({ number: n, state: "open", head_sha: sha, base: "main", updated_at: new Date().toISOString() } as unknown as GiteaPull); const announced: number[] = [], said: string[] = []; const heads: Record = {}; await announceNewHeads("novox/hq", [pull(1, "a".repeat(40)), pull(2, "b".repeat(40))], heads, false, new Date(Date.now() - 24 * 3600_000).toISOString(), { alreadyChecked: async (p) => p.number === 2, announceHead: async (p) => { announced.push(p.number); return true; }, missed: (w) => said.push(w), }); assert.deepEqual(announced, [1]); assert.equal(heads["novox/hq#2@main"], "b".repeat(40), "the checked head is remembered, not announced"); assert.equal(said.length, 1); });