eleven / ocho Public

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

Plain git — no account needed to clone.

Download

Download this file

1// Fold tests for the Linear companion app's reducers (pure, no DOM):
2// pick-projected rows normalise into plain fields (flattened dot-path keys
3// like "state.name" become plain ones), malformed rows drop, the per-team
4// issues map stays bounded. node test/linear.test.mjs
5import assert from "node:assert/strict";
6import { app } from "../web/static/apps/linear.js";
7
8let failures = 0;
9async function test(name, fn) {
10 try {
11 await fn();
12 console.log(`PASS ${name}`);
13 } catch (e) {
14 failures++;
15 console.log(`FAIL ${name} — ${e.message.split("\n")[0]}`);
16 }
17}
18
19const initial = () => app.reduce(undefined, { type: "noop" }) || { teams: [], fetchedAt: 0, issues: {} };
20const fold = (state, event) => app.reduce(state, event);
21
22await test("linear.teams replaces the list and normalises projected rows", () => {
23 const s = fold(
24 { teams: [{ id: "old", key: "OLD", name: "gone" }], fetchedAt: 1, issues: {} },
25 {
26 type: "linear.teams",
27 ts: 42,
28 teams: [
29 { id: "9cfb482a-81e3-4154-b5b9-2c805e70a02d", key: "ENG", name: "Engineering" },
30 { id: "u2", key: "OPS" }, // sparse row survives; name falls back to the key
31 { key: "NOID" }, // no id — dropped (issue.create needs the UUID)
32 "not-an-object",
33 ],
34 },
35 );
36 assert.equal(s.teams.length, 2);
37 assert.deepEqual(s.teams[0], { id: "9cfb482a-81e3-4154-b5b9-2c805e70a02d", key: "ENG", name: "Engineering" });
38 assert.equal(s.teams[1].name, "OPS");
39 assert.equal(s.fetchedAt, 42);
40});
41
42await test("a malformed linear.teams folds to a no-op", () => {
43 const before = { teams: [{ id: "u1", key: "KEEP", name: "Keep" }], fetchedAt: 5, issues: {} };
44 assert.equal(fold(before, { type: "linear.teams", teams: "nope" }), before);
45});
46
47await test("linear.issues normalises flattened dot-path keys into plain fields", () => {
48 const s = fold(initial(), {
49 type: "linear.issues",
50 team: "ENG",
51 ts: 9,
52 issues: [
53 { identifier: "ENG-412", title: "Crash on login", url: "https://linear.app/x/issue/ENG-412", "state.name": "In Progress", "assignee.displayName": "Amy", priorityLabel: "Urgent", updatedAt: "2026-07-08T00:00:00Z" },
54 { identifier: "ENG-9", title: "Flicker" }, // sparse row keeps defaults
55 { title: "no identifier — dropped" },
56 ],
57 });
58 const items = s.issues["ENG"].items;
59 assert.equal(items.length, 2);
60 assert.equal(items[0].state, "In Progress");
61 assert.equal(items[0].assignee, "Amy");
62 assert.equal(items[0].priority, "Urgent");
63 assert.equal(items[1].state, "");
64 assert.equal(items[1].url, "");
65});
66
67await test("the issues map is trimmed to the most recently fetched teams", () => {
68 let s = initial();
69 for (let i = 0; i < 9; i++) {
70 s = fold(s, { type: "linear.issues", team: `T${i}`, ts: i + 1, issues: [{ identifier: `T${i}-1`, title: "t" }] });
71 }
72 const keys = Object.keys(s.issues);
73 assert.equal(keys.length, 6); // bounded — the snapshot can't balloon
74 assert.ok(!keys.includes("T0") && !keys.includes("T1") && !keys.includes("T2")); // oldest dropped
75 assert.ok(keys.includes("T8"));
76});
77
78await test("the app is gated on the linear connected service", () => {
79 assert.equal(app.requiresService, "linear");
80 assert.deepEqual([...app.types].sort(), ["linear.issues", "linear.teams"]);
81});
82
83console.log(failures === 0 ? "\nALL PASSED" : `\n${failures} FAILED`);
84process.exit(failures === 0 ? 0 : 1);
85