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

63 lines
4.5 KiB
TypeScript

import assert from "node:assert/strict";
import { test } from "node:test";
import { ViewFollower, findingKey, unansweredFinding, viewTools, type ViewAnswer, type ViewChange, type ViewEvent } from "../views.ts";
// ADR 0615: the module asks the indexer's read tool once, then keeps its picture of `modules` from the indexer's views
// events; a gap asks once more; an owner that is down is asked again and never read instead.
const made = "2026-11-04T12:00:00Z";
const answer = (revision: number, values: Record<string, unknown>): ViewAnswer =>
({ v: 1, owner: "o", state: "modules", revision, created: made, values });
const ev = (revision: number, key: string, value?: unknown, extra: Partial<ViewEvent> = {}): ViewEvent =>
({ v: 1, owner: "o", state: "modules", key, revision, created: made, value, deleted: value === undefined, ...extra });
test("a follower takes the first view, then the events, and asks again only on a gap", async () => {
const changes: ViewChange[] = [];
const said: string[] = [];
let asks = 0;
let down = 2;
const raised: string[][] = [];
const f = new ViewFollower({ owner: "o", state: "modules", tool: "o.read",
ask: async () => { if (down-- > 0) throw new Error("no responders"); asks++; return answer(9, { a: { repository: "novox/a" }, b: { repository: "novox/b" } }); },
take: (c) => { changes.push(c); }, raise: (_a, e) => { raised.push(["raised", e]); }, clear: () => { raised.push(["cleared", ""]); }, say: (l) => said.push(l), sleep: async () => {}, now: (() => { let t = 0; return () => (t += 200_000); })() });
await f.start();
assert.equal(f.ready, true);
assert.equal(f.values.size, 2);
assert.deepEqual(raised.map((r) => r.slice(0, 2)), [["raised", "no responders"], ["cleared", ""]]);
assert.ok(said.some((l) => l.includes("no first view of o's modules") && l.includes("o.read")), said.join("|"));
await f.hear(ev(8, "a", { repository: "old" })); // already in the picture
await f.hear(ev(10, "c", { repository: "novox/c" }));
await f.hear(ev(11, "a")); // deleted
assert.deepEqual([f.revision, f.values.has("a"), f.values.has("c"), asks], [11, false, true, 1]);
assert.deepEqual(changes.slice(-2).map((c) => [c.key, c.op]), [["c", "put"], ["a", "delete"]]);
await f.hear(ev(13, "d", { repository: "x" })); // revision 12 missed: a gap
assert.equal(asks, 2);
await f.hear(ev(1, "e", { repository: "x" }, { created: "2026-11-04T13:00:00Z" })); // the state made again: a gap
assert.equal(asks, 3);
await f.hear(ev(10, "f", undefined, { gap: true })); // the next revision, too large to carry: a gap
assert.equal(asks, 4);
await f.hear({ ...ev(10, "g", { repository: "x" }), state: "other" }); // another state
assert.equal(f.values.has("g"), false);
});
test("the reader's two tools say where the first view stands and ask it again by hand", async () => {
let asks = 0;
const acts: Record<string, unknown>[] = [];
const follower = new ViewFollower({ owner: "o", state: "modules", tool: "o.read", ask: async () => { asks++; return answer(5, { a: 1 }); },
take: () => {}, sleep: async () => {} });
const [first, again] = viewTools(() => ({ follower, owner: "o", tool: "o.read", state: "modules" }), async (act) => { acts.push(act); });
assert.deepEqual(await first.run({}), { views: [{ owner: "o", tool: "o.read", state: "modules", revision: 0, created: "", keys: 0, answered: false, asks: 0, "by-hand": 0 }] });
const after = (await again.run({})) as { views: { revision: number; "by-hand": number; asks: number }[] };
assert.deepEqual([after.views[0].revision, after.views[0]["by-hand"], asks, acts.length], [5, 1, 1, 1]);
await assert.rejects(viewTools(() => ({ follower, owner: "o", tool: "o.read", state: "modules" }), async () => { throw new Error("no holder"); })[1].run({}), /not recorded/);
assert.deepEqual(await viewTools(() => undefined, async () => {})[0].run({}), { views: [] });
});
test("the finding of a first view unanswered is the one mesh-sdk writes: key, kind, subject and the words", () => {
const f = unansweredFinding("gitea", "mesh-delivery-indexer", "modules", "mesh-delivery-indexer.indexer_read_modules", new Date("2026-11-04T12:00:00Z"), "no responders", new Date("2026-11-04T12:03:00Z"));
assert.equal(f.key, "module.gitea.mesh-delivery-indexer.modules.first-view-unanswered");
assert.equal(findingKey("module", "a.b", "k"), "module.a.b.k");
assert.equal(f.value.severity, "warning");
assert.match(String(f.value.summary), /gitea has no first view of mesh-delivery-indexer's modules: .* \(no responders\)/);
});