commit 6d34aa9d6ea97406c4e6f27af121a7a982315ea9
parent 4e9763e04c776c7841fe42abc2bbab08501a7a60
Author: I Mean I'm Just Saying <imeanimjustsaying@kiwifarms.st>
Date: Mon, 5 Oct 2026 03:35:25 -0400
common: tests for the converters' disk callbacks and the reports convert / to-manifest CLI
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Diffstat:
1 file changed, 212 insertions(+), 0 deletions(-)
diff --git a/common/bin/reports-convert.test.ts b/common/bin/reports-convert.test.ts
@@ -0,0 +1,212 @@
+// The converters over files: the disk callbacks (cues behind the text guard,
+// a post's channel and record off the posts archive) and the two CLI mains,
+// on a temp channels tree.
+
+import { test } from "node:test";
+import assert from "node:assert/strict";
+import { existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
+import { tmpdir } from "node:os";
+import path from "node:path";
+import { convertMain, toManifestMain } from "./reports-convert";
+import { diskCuesOf, diskPostChannelOf, diskPostOf } from "../lib/report/convert-server";
+import { resolveCommand } from "./_cli";
+import { COMMANDS } from "./archilyzer";
+
+const ORIGIN = "https://archive.example";
+
+function tree() {
+ const root = mkdtempSync(path.join(tmpdir(), "reports-convert-"));
+ const channels = path.join(root, "channels");
+ const rec = path.join(channels, "demo-channel", "data", "abc123");
+ mkdirSync(rec, { recursive: true });
+ writeFileSync(
+ path.join(rec, "transcript.cues.json"),
+ JSON.stringify({
+ cues: [
+ { start: 60.1, end: 62.0, text: "and then the host says" },
+ { start: 58.2, end: 60.1, text: "This is where it starts." },
+ { start: 62.4, end: 64.9, text: "we never said that." },
+ ],
+ }),
+ );
+ const social = path.join(channels, "demo-social");
+ mkdirSync(path.join(social, "posts"), { recursive: true });
+ writeFileSync(path.join(social, "posts-archive"), "twitter 987654321\n");
+ writeFileSync(
+ path.join(social, "posts", "2024-05.jsonl"),
+ JSON.stringify({
+ id: "987654321",
+ channelSlug: "demo-social",
+ author: "demo",
+ authorName: "Demo Account",
+ createdAt: "2024-05-01T12:00:00.000Z",
+ text: "a platform post",
+ url: "https://x.com/demo/status/987654321",
+ platform: "twitter",
+ }) + "\n",
+ );
+ // A second channel holding the same post id: the handle picks.
+ const mirror = path.join(channels, "other-mirror");
+ mkdirSync(mirror, { recursive: true });
+ writeFileSync(path.join(mirror, "posts-archive"), "twitter 987654321\n");
+ return { root, channels, done: () => rmSync(root, { recursive: true, force: true }) };
+}
+
+const quiet = () => {
+ const lines = { log: [] as string[], error: [] as string[] };
+ return { lines, out: { log: (s: string) => lines.log.push(s), error: (s: string) => lines.error.push(s) } };
+};
+
+test("diskCuesOf reads a record's cues sorted; a missing record or an unsafe id is null", async () => {
+ const t = tree();
+ try {
+ const cuesOf = diskCuesOf(t.channels);
+ const cues = await cuesOf("demo-channel", "abc123");
+ assert.deepEqual(cues?.map((c) => c.start), [58.2, 60.1, 62.4]);
+ assert.equal(await cuesOf("demo-channel", "nope"), null);
+ assert.equal(await cuesOf("demo-channel", ".."), null);
+ } finally {
+ t.done();
+ }
+});
+
+test("diskCuesOf: a channel whose text is not readable is a warning, not a read", async () => {
+ const t = tree();
+ try {
+ const legacy = path.join(t.channels, "legacy-channel");
+ mkdirSync(legacy, { recursive: true });
+ writeFileSync(path.join(legacy, "config.json"), JSON.stringify({ dataDir: "/elsewhere/legacy-channel/data" }));
+ const warnings: string[] = [];
+ assert.equal(await diskCuesOf(t.channels, warnings)("legacy-channel", "abc123"), null);
+ assert.equal(warnings.length, 1);
+ assert.match(warnings[0], /^legacy-channel: /);
+ } finally {
+ t.done();
+ }
+});
+
+test("diskPostChannelOf finds the channel that keeps a post, the handle breaking a tie; diskPostOf reads its record", async () => {
+ const t = tree();
+ try {
+ const channelOf = diskPostChannelOf(t.channels);
+ assert.equal(await channelOf({ platform: "x", id: "987654321", handle: "demo" }), "demo-social");
+ assert.equal(await channelOf({ platform: "x", id: "987654321", handle: "other" }), "other-mirror");
+ assert.equal(await channelOf({ platform: "x", id: "1" }), null);
+ const postOf = diskPostOf(t.channels);
+ assert.deepEqual(await postOf("demo-social", "987654321"), {
+ platform: "x",
+ url: "https://x.com/demo/status/987654321",
+ createdAt: "2024-05-01T12:00:00.000Z",
+ author: "demo",
+ authorName: "Demo Account",
+ });
+ assert.equal(await postOf("demo-social", "2"), null);
+ } finally {
+ t.done();
+ }
+});
+
+test("reports convert sweep: widened from the cues, the platform post found, written", async () => {
+ const t = tree();
+ try {
+ const input = path.join(t.root, "sweep.md");
+ writeFileSync(
+ input,
+ [
+ "# A demo sweep",
+ "",
+ "## Findings",
+ "",
+ `- "we never said that" — [Demo @ 1:02](${ORIGIN}/?v=demo-channel%2Fabc123&t=62)`,
+ `- "a platform post" [post by demo](https://x.com/demo/status/987654321)`,
+ ].join("\n"),
+ );
+ const out = path.join(t.root, "reports", "demo-sweep", "report.json");
+ const { lines, out: o } = quiet();
+ assert.equal(await convertMain({ from: "sweep", input, out, channelsDir: t.channels }, o), 0);
+ const report = JSON.parse(readFileSync(out, "utf8"));
+ assert.equal(report.id, "a-demo-sweep");
+ assert.deepEqual([report.citations.c01.start, report.citations.c01.end], [60.1, 64.9]);
+ assert.equal(report.citations.p01.channel, "demo-social");
+ assert.deepEqual(lines.error, []);
+ assert.match(lines.log[0], /report a-demo-sweep \(sweep\) — 1 section\(s\), 2 claim\(s\), 2 citation\(s\)/);
+ } finally {
+ t.done();
+ }
+});
+
+test("reports convert refuses to write a report with problems; an unreadable input is exit 2", async () => {
+ const t = tree();
+ try {
+ const input = path.join(t.root, "answer.json");
+ writeFileSync(input, JSON.stringify({ answer: "Said [1 @ 0:05].", sources: [{ key: "demo-channel/abc123", snippets: [] }] }));
+ const out = path.join(t.root, "out.json");
+ const { lines, out: o } = quiet();
+ assert.equal(await convertMain({ from: "ask", input, out, id: "Not A Slug" }, o), 1);
+ assert.equal(existsSync(out), false);
+ assert.ok(lines.error.some((l) => l.startsWith(" id: must be a lowercase slug")));
+ assert.equal(await convertMain({ from: "ask", input: path.join(t.root, "missing.json"), out }, quiet().out), 2);
+ writeFileSync(input, JSON.stringify({ unrelated: true }));
+ assert.equal(await convertMain({ from: "ask", input, out }, quiet().out), 2);
+ } finally {
+ t.done();
+ }
+});
+
+test("reports to-manifest: a starter manifest, its posts read off the channels tree", async () => {
+ const t = tree();
+ try {
+ const input = path.join(t.root, "report.json");
+ writeFileSync(
+ input,
+ JSON.stringify({
+ format: "archilyzer-report",
+ version: 1,
+ id: "demo-report",
+ kind: "sweep",
+ title: "A demo report",
+ citations: {
+ c01: { kind: "video", channel: "demo-channel", id: "abc123", start: 60.1, end: 64.9, quote: "we never said that." },
+ p01: { kind: "post", channel: "demo-social", id: "987654321", quote: "a platform post" },
+ },
+ sections: [{ id: "s1", title: "Findings", claims: [{ id: "k1", text: "It was said.", citations: ["c01", "p01"] }] }],
+ }),
+ );
+ const out = path.join(t.root, "video.manifest.json");
+ const { lines, out: o } = quiet();
+ assert.equal(await toManifestMain({ input, out, channelsDir: t.channels, siteOrigin: ORIGIN }, o), 0);
+ const m = JSON.parse(readFileSync(out, "utf8"));
+ assert.deepEqual(
+ m.timeline.map((e: { type: string; id: string }) => `${e.type}:${e.id}`),
+ ["card:title", "card:s1", "clip:c01"],
+ );
+ assert.deepEqual(m.posts, [
+ {
+ id: "p01",
+ platform: "x",
+ author: "Demo Account",
+ handle: "demo",
+ date: "2024-05-01T12:00:00.000Z",
+ text: "a platform post",
+ url: "https://x.com/demo/status/987654321",
+ attachTo: "c01",
+ siteChannel: "demo-social",
+ postId: "987654321",
+ },
+ ]);
+ assert.equal(m.provenance.siteOrigin, ORIGIN);
+ assert.deepEqual(lines.error, []);
+ // An invalid report is refused, and nothing is written.
+ writeFileSync(input, JSON.stringify({ format: "archilyzer-report" }));
+ rmSync(out);
+ assert.equal(await toManifestMain({ input, out }, quiet().out), 1);
+ assert.equal(existsSync(out), false);
+ } finally {
+ t.done();
+ }
+});
+
+test("the archilyzer table routes reports convert and reports to-manifest", () => {
+ assert.deepEqual(resolveCommand(COMMANDS, ["reports", "convert", "sweep", "in.md"])?.command.path, ["reports", "convert"]);
+ assert.deepEqual(resolveCommand(COMMANDS, ["reports", "to-manifest", "r.json"])?.command.path, ["reports", "to-manifest"]);
+});