// The messages page's logic: the stream URL, the filter, the cap, and how a // message becomes a terminal row. Run by `npm test`, which Node runs with its // built-in test runner and type stripping, so the `.ts` module is imported as // it is. import assert from "node:assert/strict"; import { test } from "node:test"; import { clock, filterMessages, messageRow, messagesUrl, newest, snippet, } from "../src/pages/messages/messages.ts"; const msg = (from, to, body = "hi", extra = {}) => ({ ts: "2026-10-18T12:00:00Z", from, to, body, ...extra, }); test("the stream URL carries the history window", () => { assert.equal(messagesUrl(60), "/api/messages/stream?since_minutes=60"); assert.equal(messagesUrl(0), "/api/messages/stream?since_minutes=0"); }); test("a blank filter keeps every message", () => { const all = [msg("a", "b"), msg("c", "d")]; assert.deepEqual(filterMessages(all, ""), all); assert.deepEqual(filterMessages(all, " "), all); }); test("the filter matches sender or recipient, ignoring case", () => { const all = [msg("atlas", "iris"), msg("iris", "coder"), msg("a", "b")]; assert.deepEqual( filterMessages(all, "IRIS").map((m) => `${m.from}>${m.to}`), ["atlas>iris", "iris>coder"], ); assert.deepEqual(filterMessages(all, "cod").length, 1); assert.deepEqual(filterMessages(all, "nobody"), []); }); test("the filter does not search the body", () => { assert.deepEqual(filterMessages([msg("a", "b", "talk to iris")], "iris"), []); }); test("the cap keeps the newest messages and leaves a short list alone", () => { const all = [1, 2, 3, 4, 5].map((n) => msg("a", "b", String(n))); assert.deepEqual( newest(all, 3).map((m) => m.body), ["3", "4", "5"], ); assert.equal(newest(all, 5), all); assert.equal(newest(all, 10), all); }); test("a snippet is the first non-blank line, cut with an ellipsis", () => { assert.equal(snippet("\n\n hello there \nsecond", 80), "hello there"); assert.equal(snippet("abcdefghij", 5), "abcd…"); assert.equal(snippet("abcde", 5), "abcde"); assert.equal(snippet(" \n ", 5), ""); }); test("the clock reads hours, minutes and seconds, and passes a non-date through", () => { assert.match(clock("2026-10-18T12:00:05Z"), /^\d\d:\d\d:\d\d$/); assert.equal(clock("not a date"), "not a date"); }); test("a message becomes an info row with its parties in the summary", () => { const row = messageRow(msg("atlas", "iris", "ready\nmore"), "k1"); assert.equal(row.key, "k1"); assert.equal(row.level, "info"); assert.equal(row.body, "ready\nmore"); assert.equal(row.fromHistory, false); assert.match(row.summary, /atlas → iris — ready$/); }); test("the body format rides on the row, and an empty body leaves no dash", () => { assert.equal( messageRow(msg("a", "b", "x", { body_format: "diff" }), "k").body_format, "diff", ); assert.equal(messageRow(msg("a", "b", "x"), "k").body_format, undefined); assert.doesNotMatch(messageRow(msg("a", "b", ""), "k").summary, / — /); });