eleven / messenger Public

Clone
git clone https://amadan.net/eleven/messenger

Plain git — no account needed to clone.

Download

Download this file

1// Fold tests for the GitHub companion app's reducers (pure, no DOM):
2// pick-projected rows normalise into plain fields, malformed rows drop, the
3// per-repo issues map stays bounded. node test/github.test.mjs
4import assert from "node:assert/strict";
5import { app } from "../web/static/apps/github.js";
6
7let failures = 0;
8async function test(name, fn) {
9 try {
10 await fn();
11 console.log(`PASS ${name}`);
12 } catch (e) {
13 failures++;
14 console.log(`FAIL ${name} — ${e.message.split("\n")[0]}`);
15 }
16}
17
18const initial = () => app.reduce(undefined, { type: "noop" }) || { repos: [], fetchedAt: 0, issues: {} };
19const fold = (state, event) => app.reduce(state, event);
20
21await test("github.repos replaces the list and normalises projected rows", () => {
22 const s = fold(
23 { repos: [{ full_name: "old/gone" }], fetchedAt: 1, issues: {} },
24 {
25 type: "github.repos",
26 ts: 42,
27 repos: [
28 { full_name: "paul/lchat", description: "chat", private: true, archived: false, fork: false, html_url: "https://github.com/paul/lchat", open_issues_count: 3, stargazers_count: 7, pushed_at: "2026-07-01T00:00:00Z" },
29 { full_name: "paul/min" }, // sparse row survives with defaults
30 { description: "no name — dropped" },
31 "not-an-object",
32 ],
33 },
34 );
35 assert.equal(s.repos.length, 2);
36 assert.deepEqual(s.repos[0], {
37 full_name: "paul/lchat",
38 description: "chat",
39 private: true,
40 archived: false,
41 fork: false,
42 html_url: "https://github.com/paul/lchat",
43 open_issues: 3,
44 stars: 7,
45 pushed_at: "2026-07-01T00:00:00Z",
46 });
47 assert.equal(s.repos[1].full_name, "paul/min");
48 assert.equal(s.repos[1].open_issues, null); // absent count reads "—", never 0
49 assert.equal(s.fetchedAt, 42);
50});
51
52await test("a malformed github.repos folds to a no-op", () => {
53 const before = { repos: [{ full_name: "keep/me" }], fetchedAt: 5, issues: {} };
54 assert.equal(fold(before, { type: "github.repos", repos: "nope" }), before);
55});
56
57await test("github.issues normalises flattened dot-path keys into plain fields", () => {
58 const s = fold(initial(), {
59 type: "github.issues",
60 repo: "paul/lchat",
61 ts: 9,
62 issues: [
63 { number: 12, title: "Fix login", html_url: "https://github.com/paul/lchat/issues/12", "user.login": "amy", comments: 4, updated_at: "2026-07-08T00:00:00Z" },
64 { number: 13, title: "Ship it", "pull_request.url": "https://api.github.com/x", draft: true },
65 { title: "no number — dropped" },
66 ],
67 });
68 const items = s.issues["paul/lchat"].items;
69 assert.equal(items.length, 2);
70 assert.equal(items[0].by, "amy");
71 assert.equal(items[0].is_pr, false);
72 assert.equal(items[1].is_pr, true);
73 assert.equal(items[1].draft, true);
74});
75
76await test("the issues map is trimmed to the most recently fetched repos", () => {
77 let s = initial();
78 for (let i = 0; i < 9; i++) {
79 s = fold(s, { type: "github.issues", repo: `o/r${i}`, ts: i + 1, issues: [{ number: 1, title: "t" }] });
80 }
81 const keys = Object.keys(s.issues);
82 assert.equal(keys.length, 6); // bounded — the snapshot can't balloon
83 assert.ok(!keys.includes("o/r0") && !keys.includes("o/r1") && !keys.includes("o/r2")); // oldest dropped
84 assert.ok(keys.includes("o/r8"));
85});
86
87await test("the app is gated on the github connected service", () => {
88 assert.equal(app.requiresService, "github");
89 assert.deepEqual([...app.types].sort(), ["github.issues", "github.repos"]);
90});
91
92console.log(failures === 0 ? "\nALL PASSED" : `\n${failures} FAILED`);
93process.exit(failures === 0 ? 0 : 1);
94