import assert from "node:assert/strict";
import { readFileSync } from "node:fs";
import { test } from "node:test";
import { fetchPinnedRecentCommits, getCodeActivity, parseTangledHistoryUrl, parseTangledRecentCommits } from "./code-activity.ts";
const fixture = readFileSync(new URL("./code-activity.fixture.html", import.meta.url), "utf8");
const repo = (name = "charter") => ({ repoDid: name, name, description: "", website: null, url: `https://tangled.org/cameron.stream/${name}` });
const landing = (name = "charter", branch = "main") => `
`;
test("real history shape selects subjects rather than SHA links and pairs each row's time", () => {
const commits = parseTangledRecentCommits(fixture);
assert.equal(commits.length, 2);
assert.equal(commits[0].subject, "Make Charter a Jazz-native distributed runtime.");
assert.equal(commits[0].time, "2026-07-11T19:19:35-07:00");
assert.equal(commits[1].subject, "Make Charter runtime-first.");
assert.notEqual(commits[0].time, commits[1].time);
assert.equal(parseTangledRecentCommits(fixture, 1).length, 1);
assert.equal(parseTangledRecentCommits(fixture, 0).length, 0);
assert.deepEqual(parseTangledRecentCommits(fixture + fixture), commits);
});
test("invalid and missing time never borrow from the next row or outside the history", () => {
for (const replacement of ["", ' ']) {
const html = fixture.replace(/]*>[\s\S]*?<\/time>/, replacement);
const result = parseTangledRecentCommits(html + ' ');
assert.equal(result.length, 1);
assert.equal(result[0].subject, "Make Charter runtime-first.");
}
});
test("HTML entities and nested subject markup decode without losing text", () => {
const html = fixture.replace("Make Charter a Jazz-native distributed runtime.", "fix & <parser> 'quote' —")
.replace("2026-07-11T19:19:35-07:00", "2026-07-11T19:19:35+00:00");
assert.equal(parseTangledRecentCommits(html)[0].subject, "fix & 'quote' —");
assert.equal(parseTangledRecentCommits(html)[0].time, "2026-07-11T19:19:35+00:00");
});
test("default history navigation supports non-main branches and rejects unrelated links", () => {
assert.equal(parseTangledHistoryUrl(landing("charter", "trunk"), repo().url), `${repo().url}/commits/trunk`);
assert.equal(parseTangledHistoryUrl(landing("charter", "feature%2Fnext"), repo().url), `${repo().url}/commits/feature%2Fnext`);
for (const html of ["", 'README ', landing("other"), '']) {
assert.equal(parseTangledHistoryUrl(html, repo().url), null);
}
});
test("mocked fetch bounds to four unique repos, deduplicates and sorts, fails soft", async (t) => {
const calls: string[] = [];
t.mock.method(globalThis, "fetch", async (input: string, init: RequestInit) => {
calls.push(input);
assert.ok(init.signal);
if (input === repo("broken").url) throw new Error("timeout");
if (input.includes("/commits/")) return new Response(fixture.replaceAll("/charter/", "/other/").replaceAll("2026-07-11", "2026-09-08") + fixture + fixture);
if (input === repo("empty").url) return new Response("unavailable", { status: 503 });
return new Response(landing(input.split("/").at(-1), "trunk"));
});
const result = await fetchPinnedRecentCommits([repo(), repo(), repo("other"), repo("broken"), repo("empty"), repo("fifth")]);
assert.equal(result.length, 4);
assert.equal(result[0].repository, "cameron.stream/other");
assert.equal(new Set(result.map((c) => c.url)).size, 4);
assert.equal(calls.length, 6);
assert.ok(calls.includes(`${repo().url}/commits/trunk`));
assert.ok(!calls.some((url) => url.includes("fifth")));
});
test("getCodeActivity fetches public records without defaultBranch and caches the populated result", async (t) => {
let calls = 0;
t.mock.method(globalThis, "fetch", async (input: string) => {
calls++;
if (input.includes("plc.directory")) return Response.json({ service: [{ type: "AtprotoPersonalDataServer", serviceEndpoint: "https://public.example" }] });
if (input.includes("getRecord")) return Response.json({ value: { pinnedRepositories: ["charter"] } });
if (input.includes("listRecords")) return Response.json({ records: [{ uri: "at://did:plc:example/sh.tangled.repo/charter", value: { repoDid: "charter" } }] });
if (input === repo().url) return new Response(landing());
if (input === `${repo().url}/commits/main`) return new Response(fixture);
return new Response("", { status: 503 });
});
const first = await getCodeActivity();
assert.equal(first.tangledRecent.length, 2);
const before = calls;
assert.deepEqual(await getCodeActivity(), first);
assert.equal(calls, before);
});