g1t/apps/sudo/app/lib/incidents.test.ts

117 lines5,989 bytesCodeBlame

Pick any line to see why it is the way it is: the commit, the pull request and issue it came from, and what the agent was thinking.

status.g1t.sh with incident management, invites that land you in the workspace, settings as pages, usage without quotas1import assert from "node:assert/strict";
2import { test } from "node:test";
3
4import { mergeAudit, accountPath, accountName, statusAuditActions } from "./ledgers.ts";
5import {
6 duration,
7 filterIncidents,
8 incidentsHref,
9 maintenanceList,
10 median,
11 nextStatus,
12 parseFilters,
13 phase,
14 readImpacts,
15 staffSuggestions,
16 tabCounts,
17 tabOf,
18 utc,
19 wantsNotify,
20} from "./incidents.ts";
21import { proseBlocks } from "./postmortem.ts";
22
23const incident = (over: Record<string, unknown> = {}) =>
24 ({
25 title: "Pushes failing",
26 severity: "sev2",
27 visibility: "public",
28 resolved_at: null,
29 components: [{ key: "git", impact: "major_outage" }],
30 status: "investigating",
31 postmortem_published_at: null,
32 ...over,
33 }) as Parameters<typeof tabOf>[0] & Parameters<typeof phase>[0] & { title: string; severity: "sev2"; components: { key: string; impact: "major_outage" }[] };
34
35test("filters come from the address; unknown values are dropped", () => {
36 assert.deepEqual(parseFilters(new URLSearchParams("tab=drafts&severity=sev1&component=git&q=%20push%20")), { tab: "drafts", severity: "sev1", component: "git", q: "push" });
37 assert.deepEqual(parseFilters(new URLSearchParams("tab=nope&severity=sev9&component=%3Cx%3E")), { tab: "open", severity: null, component: null, q: "" });
38 assert.equal(incidentsHref({ tab: "open" }), "/incidents");
39 assert.equal(incidentsHref({ tab: "resolved", severity: "sev1", q: "a b" }), "/incidents?tab=resolved&severity=sev1&q=a+b");
40});
41
42test("tabs: open, drafts, and resolved (dismissed drafts included)", () => {
43 assert.equal(tabOf(incident()), "open");
44 assert.equal(tabOf(incident({ visibility: "draft" })), "drafts");
45 assert.equal(tabOf(incident({ resolved_at: "2026-10-05T00:00:00Z" })), "resolved");
46 assert.equal(tabOf(incident({ visibility: "dismissed", resolved_at: "2026-10-05T00:00:00Z" })), "resolved");
47 const list = [incident(), incident({ title: "Docs slow", severity: "sev3", components: [{ key: "docs", impact: "degraded" }] }), incident({ visibility: "draft" })];
48 const all = { tab: "open" as const, severity: null, component: null, q: "" };
49 assert.equal(filterIncidents(list, all).length, 2);
50 assert.equal(filterIncidents(list, { ...all, severity: "sev3" }).length, 1);
51 assert.equal(filterIncidents(list, { ...all, component: "git" }).length, 1);
52 assert.equal(filterIncidents(list, { ...all, q: "DOCS" })[0]!.title, "Docs slow");
53 assert.deepEqual(tabCounts(list, [{ state: "scheduled" }, { state: "completed" }]), { open: 2, maintenance: 1, resolved: 0, drafts: 1 });
54});
55
56test("maintenance: live first by start, then finished newest first", () => {
57 const m = (id: string, state: string, starts_at: string) => ({ id, state, starts_at, components: ["git"] }) as never;
58 assert.deepEqual(
59 maintenanceList([m("done-old", "completed", "2026-09-01"), m("later", "scheduled", "2026-10-09"), m("done-new", "cancelled", "2026-10-01"), m("soon", "in_progress", "2026-10-05")], null).map((x: { id: string }) => x.id),
60 ["soon", "later", "done-new", "done-old"],
61 );
62});
63
64test("phase chips: draft, dismissed, postmortem, else the status", () => {
65 assert.deepEqual(phase(incident({ visibility: "draft" })), { label: "Draft", tone: "lavender" });
66 assert.equal(phase(incident({ visibility: "dismissed" })).label, "Dismissed");
67 assert.equal(phase(incident({ postmortem_published_at: "x", status: "resolved" })).label, "Postmortem published");
68 assert.deepEqual(phase(incident({ status: "monitoring" })), { label: "Monitoring", tone: "info" });
69});
70
71test("durations, medians and UTC times", () => {
72 assert.equal(duration(null), "—");
73 assert.equal(duration(59.6), "1m");
74 assert.equal(duration(3 * 3600 + 5 * 60), "3h 05m");
75 assert.equal(median([300, null, 100, 200]), 200);
76 assert.equal(median([100, 200]), 150);
77 assert.equal(median([null]), null);
78 assert.equal(utc("2026-10-05T14:00"), "2026-10-05T14:00:00.000Z");
79 assert.equal(utc(""), null);
80 assert.equal(utc("garbage"), null);
81});
82
83test("forms: impacts per part, notify by severity, next status, staff suggestions", () => {
84 const form = new FormData();
85 form.set("impact.git", "partial_outage");
86 form.set("impact.api", "");
87 form.set("impact.docs", "bogus");
88 assert.deepEqual(readImpacts(form, ["git", "api", "docs"]), [{ key: "git", impact: "partial_outage" }]);
89 assert.equal(wantsNotify("auto", "sev1"), true);
90 assert.equal(wantsNotify("auto", "sev3"), false);
91 assert.equal(wantsNotify("yes", "sev4"), true);
92 assert.equal(wantsNotify("no", "sev1"), false);
93 assert.equal(nextStatus("investigating"), "identified");
94 assert.equal(nextStatus("monitoring"), "resolved");
95 assert.deepEqual(staffSuggestions("syntaqx@gmail.com, @g1t.sh", ["Ana@g1t.sh", null, "syntaqx@gmail.com"]), ["ana@g1t.sh", "syntaqx@gmail.com"]);
96});
97
98test("the status page's audit lines join the Audit log and link to their pages", () => {
99 const lines = statusAuditActions([
100 { id: "a1", at: "2026-10-05T12:00:00Z", by: "ana@g1t.sh", action: "incident_declared", target: "abc123", detail: "SEV2: Pushes failing" },
101 { id: "a2", at: "2026-10-05T10:00:00Z", by: "ana@g1t.sh", action: "maintenance_scheduled", target: "m1", detail: "Upgrade" },
102 ]);
103 assert.equal(accountPath(lines[0]!.account), "/incidents/abc123");
104 assert.equal(accountPath(lines[1]!.account), "/incidents/maintenance/m1");
105 assert.equal(accountName(lines[0]!.account), "Incident");
106 const billing = [{ id: "b", account: "ws_acme", action: "terms", detail: "", by: "x", createdAt: "2026-10-05T11:00:00Z" }];
107 assert.deepEqual(mergeAudit(billing, lines).map((l) => l.id), ["status-a1", "b", "status-a2"]);
108 assert.equal(mergeAudit(billing, lines, 2).length, 2);
109});
110
111test("the postmortem preview reads text as the status page does", () => {
112 assert.deepEqual(proseBlocks("One\ntwo\n\n- a\n- b\n\n\n"), [
113 { kind: "paragraph", text: "One\ntwo" },
114 { kind: "list", items: ["a", "b"] },
115 ]);
116 assert.deepEqual(proseBlocks(""), []);
117});