g1t/apps/status/src/model.test.ts

255 lines12,243 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 { components } from "./components.ts";
5import {
6 type DayRow,
7 barFor,
8 buildPage,
9 classify,
10 dayState,
11 effectiveState,
12 lastDays,
13 maintenanceState,
14 openOn,
15 percent,
16 recentIncidents,
17 summarize,
18 uptimeOf,
19 uptimeOver,
20} from "./model.ts";
21
22const VARS = {
23 SITE_URL: "https://g1t.sh",
24 API_URL: "https://api.g1t.sh",
25 MCP_URL: "https://mcp.g1t.sh",
26 DOCS_URL: "https://docs.g1t.sh",
27 PAGES_URL: "https://g1t.page",
28 MODELS_URL: "https://models.g1t.sh",
29 PROBE_REPO: "flagon-io/g1t",
30};
31
32const row = (component: string, day: string, up: number, degraded = 0, down = 0): DayRow => ({
33 component,
34 day,
35 checks: up + degraded + down,
36 up,
37 degraded,
38 down,
39 latency_total: (up + degraded) * 100,
40 latency_count: up + degraded,
41});
42
43test("classify: fast is up, slow is degraded, failed is down, none is unmonitored", () => {
44 assert.equal(classify({ ok: true, ms: 84.4 }).state, "up");
45 assert.equal(classify({ ok: true, ms: 84.4 }).detail, "Answered in 84 ms");
46 assert.equal(classify({ ok: true, ms: 2000 }).state, "degraded");
47 assert.deepEqual(classify({ ok: false, ms: 5000, error: "timed out" }), { state: "down", detail: "Failed: timed out" });
48 assert.equal(classify(null).state, "unmonitored");
49});
50
51test("uptime rounds down, so one failure never reads as 100%", () => {
52 assert.equal(uptimeOf(1439, 1440), 99.93);
53 assert.equal(uptimeOf(99999, 100000), 99.99);
54 assert.equal(uptimeOf(10, 10), 100);
55 assert.equal(uptimeOf(0, 0), null);
56 assert.equal(percent(100), "100%");
57 assert.equal(percent(99.5), "99.50%");
58 assert.equal(percent(null), "No data");
59});
60
61test("a day's colour: a blip stays green, a bad hour is degraded, worse is down", () => {
62 assert.equal(dayState(null), "none");
63 assert.equal(dayState({ checks: 1440, up: 1439, degraded: 0, down: 1 }), "up");
64 assert.equal(dayState({ checks: 1440, up: 1380, degraded: 0, down: 60 }), "degraded");
65 assert.equal(dayState({ checks: 1440, up: 1000, degraded: 0, down: 440 }), "down");
66 // Answering, but slow much of the day.
67 assert.equal(dayState({ checks: 100, up: 60, degraded: 40, down: 0 }), "degraded");
68 // An incident colours the day at least as badly as its impact.
69 assert.equal(dayState({ checks: 1440, up: 1440, degraded: 0, down: 0 }, "down"), "down");
70 assert.equal(dayState(null, "degraded"), "degraded");
71});
72
73test("lastDays ends today, oldest first", () => {
74 const days = lastDays(new Date("2026-10-05T12:00:00Z"));
75 assert.equal(days.length, 90);
76 assert.equal(days.at(-1), "2026-10-05");
77 assert.equal(days[0], "2026-07-08");
78});
79
80test("an incident is on every day it was open", () => {
81 const incident = { started_at: "2026-10-03T23:30:00Z", resolved_at: "2026-10-04T00:10:00Z" };
82 assert.equal(openOn(incident, "2026-10-02"), false);
83 assert.equal(openOn(incident, "2026-10-03"), true);
84 assert.equal(openOn(incident, "2026-10-04"), true);
85 assert.equal(openOn(incident, "2026-10-05"), false);
86 assert.equal(openOn({ started_at: "2026-10-01T00:00:00Z", resolved_at: null }, "2026-10-05"), true);
87});
88
89test("bars and 90-day uptime come from the daily tally", () => {
90 const days = ["2026-10-03", "2026-10-04", "2026-10-05"];
91 const rows = [row("api", "2026-10-04", 1439, 0, 1), row("api", "2026-10-05", 600), row("git", "2026-10-05", 10)];
92 const bar = barFor("api", rows, [], days);
93 assert.deepEqual(
94 bar.map((d) => d.state),
95 ["none", "up", "up"],
96 );
97 assert.equal(bar[1]!.failed, 1);
98 assert.equal(bar[1]!.avg_ms, 100);
99 assert.equal(uptimeOver("api", rows), uptimeOf(2039, 2040));
100 assert.equal(uptimeOver("docs", rows), null);
101});
102
103test("a bar marks the days an incident on its part was open", () => {
104 const incident = {
105 id: "1",
106 title: "Clones failing",
107 impact: "down" as const,
108 status: "resolved" as const,
109 components: ["git"],
110 component_impacts: [{ key: "git", impact: "major_outage" as const }],
111 started_at: "2026-10-04T10:00:00Z",
112 resolved_at: "2026-10-04T11:00:00Z",
113 url: "https://status.g1t.sh/incidents/1",
114 postmortem_published_at: null,
115 updates: [],
116 };
117 const days = ["2026-10-04", "2026-10-05"];
118 const git = barFor("git", [row("git", "2026-10-04", 1440)], [incident], days);
119 assert.equal(git[0]!.state, "down");
120 assert.deepEqual(git[0]!.incidents, ["Clones failing"]);
121 assert.equal(barFor("api", [row("api", "2026-10-04", 1440)], [incident], days)[0]!.state, "up");
122});
123
124test("summarize: core down is a major outage, others partial, slow is degraded performance", () => {
125 const core = new Set(["site", "api"]);
126 const up = { key: "api", name: "API", state: "up" as const };
127 assert.equal(summarize([up], core).title, "All systems normal");
128 assert.equal(summarize([{ ...up, state: "down" }], core).title, "Major outage");
129 assert.equal(summarize([up, { key: "docs", name: "Documentation", state: "down" }], core).title, "Partial outage");
130 const slow = summarize([up, { key: "docs", name: "Documentation", state: "degraded" }], core);
131 assert.equal(slow.title, "Degraded performance");
132 assert.match(slow.line, /Documentation/);
133 assert.equal(summarize([{ key: "sandboxes", name: "Sandboxes", state: "unmonitored" }], core).state, "unknown");
134 const incident = summarize([up], core, [{ title: "Pushes failing", impact: "degraded", status: "identified" }]);
135 assert.equal(incident.title, "Partial outage");
136 assert.equal(incident.line, "Identified: Pushes failing.");
137});
138
139test("recent incidents: open ones and those resolved in 90 days, newest first", () => {
140 const now = new Date("2026-10-05T00:00:00Z");
141 const list = recentIncidents(
142 [
143 { id: "old", started_at: "2026-01-01T00:00:00Z", resolved_at: "2026-01-01T01:00:00Z" },
144 { id: "open", started_at: "2026-01-02T00:00:00Z", resolved_at: null },
145 { id: "new", started_at: "2026-10-01T00:00:00Z", resolved_at: "2026-10-01T01:00:00Z" },
146 ],
147 now,
148 );
149 assert.deepEqual(
150 list.map((i) => i.id),
151 ["new", "open"],
152 );
153});
154
155test("buildPage: parts with no check are unmonitored and have no uptime; staleness is noticed", () => {
156 const now = new Date("2026-10-05T12:00:00Z");
157 const parts = components(VARS, true);
158 const current = new Map(parts.map((p) => [p.key, { state: "up" as const, detail: "Answered in 80 ms", latency_ms: 80 }]));
159 const page = buildPage({
160 parts,
161 current,
162 checkedAt: "2026-10-05T11:59:30Z",
163 days: [row("api", "2026-10-05", 700)],
164 incidents: [],
165 now,
166 });
167 const sandboxes = page.report.components.find((c) => c.key === "sandboxes")!;
168 assert.equal(sandboxes.state, "unmonitored");
169 assert.equal(sandboxes.uptime_90d, null);
170 assert.equal(page.report.components.find((c) => c.key === "api")!.uptime_90d, 100);
171 assert.equal(page.report.overall.title, "All systems normal");
172 assert.equal(page.stale, false);
173 assert.equal(page.bars.api!.length, 90);
174 assert.equal(buildPage({ parts, current, checkedAt: "2026-10-05T11:00:00Z", days: [], incidents: [], now }).stale, true);
175});
176
177test("components: an installation without a host leaves that part off", () => {
178 const keys = components({ SITE_URL: "http://localhost:8787", API_URL: "http://localhost:8787/api", PROBE_REPO: "me/demo" }, false).map(
179 (c) => c.key,
180 );
181 assert.deepEqual(keys, ["site", "api", "git"]);
182 const bare = components({ SITE_URL: "http://g1t:8787" }, false);
183 assert.deepEqual(
184 bare.map((c) => c.key),
185 ["site"],
186 );
187 assert.equal(bare[0]!.check.kind === "http" && bare[0]!.check.steps.length, 1);
188 const all = components(VARS, true);
189 assert.deepEqual(
190 all.map((c) => c.key),
191 ["site", "api", "git", "mcp", "docs", "deployments", "agents", "sandboxes", "billing"],
192 );
193 const git = all.find((c) => c.key === "git")!;
194 assert.deepEqual(git.check, { kind: "http", steps: [{ url: "https://g1t.sh/flagon-io/g1t.git/info/refs?service=git-upload-pack" }] });
195});
196
197test("a part's state: the worse of its check and open incidents; maintenance unless an incident says worse", () => {
198 assert.equal(effectiveState("up", [], false), "up");
199 assert.equal(effectiveState("up", ["partial_outage"], false), "partial");
200 assert.equal(effectiveState("down", ["degraded"], false), "down", "a failing check beats a milder report");
201 assert.equal(effectiveState("degraded", ["major_outage", "degraded"], false), "down", "the worst impact wins");
202 assert.equal(effectiveState("up", ["operational"], false), "up");
203 assert.equal(effectiveState("unmonitored", ["degraded"], false), "degraded", "an incident speaks for a part with no check");
204 assert.equal(effectiveState("down", [], true), "maintenance", "failing checks in a window are expected");
205 assert.equal(effectiveState("up", ["major_outage"], true), "down", "an incident during maintenance still shows");
206});
207
208test("maintenance moves with its window, and cancelled or completed stays put", () => {
209 const m = { state: "scheduled" as const, starts_at: "2026-10-05T12:00:00Z", ends_at: "2026-10-05T13:00:00Z" };
210 assert.equal(maintenanceState(m, new Date("2026-10-05T11:59:00Z")), "scheduled");
211 assert.equal(maintenanceState(m, new Date("2026-10-05T12:00:00Z")), "in_progress");
212 assert.equal(maintenanceState(m, new Date("2026-10-05T13:00:00Z")), "completed");
213 assert.equal(maintenanceState({ ...m, state: "in_progress" }, new Date("2026-10-05T11:00:00Z")), "in_progress", "started early by hand");
214 assert.equal(maintenanceState({ ...m, state: "cancelled" }, new Date("2026-10-05T12:30:00Z")), "cancelled");
215 assert.equal(maintenanceState({ ...m, state: "completed" }, new Date("2026-10-05T12:30:00Z")), "completed", "finished early by hand");
216});
217
218test("buildPage: incident impacts and maintenance reach the parts and the banner", () => {
219 const now = new Date("2026-10-05T12:30:00Z");
220 const parts = components(VARS, false);
221 const current = new Map(parts.map((p) => [p.key, { state: "up" as const, detail: "Answered in 80 ms", latency_ms: 80 }]));
222 current.set("docs", { state: "down", detail: "Failed: HTTP 502", latency_ms: null });
223 const maintenance = [
224 { id: "m1", title: "Docs move", message: "Moving docs.", components: ["docs"], starts_at: "2026-10-05T12:00:00Z", ends_at: "2026-10-05T13:00:00Z", state: "scheduled" as const, url: "", updates: [] },
225 { id: "m2", title: "Later", message: "Later.", components: ["api"], starts_at: "2026-10-06T12:00:00Z", ends_at: "2026-10-06T13:00:00Z", state: "scheduled" as const, url: "", updates: [] },
226 { id: "m3", title: "Called off", message: "No.", components: ["api"], starts_at: "2026-10-05T12:00:00Z", ends_at: "2026-10-05T13:00:00Z", state: "cancelled" as const, url: "", updates: [] },
227 ];
228 const quiet = buildPage({ parts, current, checkedAt: "2026-10-05T12:29:30Z", days: [], incidents: [], maintenance, now });
229 const docs = quiet.report.components.find((c) => c.key === "docs")!;
230 assert.equal(docs.state, "maintenance");
231 assert.equal(docs.check_state, "down");
232 assert.equal(quiet.report.overall.state, "maintenance");
233 assert.equal(quiet.report.overall.title, "Under maintenance");
234 assert.deepEqual(quiet.report.maintenance.map((m) => [m.id, m.state]), [["m1", "in_progress"], ["m2", "scheduled"]]);
235
236 const incident = {
237 id: "i1",
238 title: "API errors",
239 impact: "degraded" as const,
240 status: "identified" as const,
241 components: ["api"],
242 component_impacts: [{ key: "api", impact: "partial_outage" as const }],
243 started_at: "2026-10-05T12:10:00Z",
244 resolved_at: null,
245 url: "",
246 postmortem_published_at: null,
247 updates: [],
248 };
249 const busy = buildPage({ parts, current, checkedAt: "2026-10-05T12:29:30Z", days: [], incidents: [incident], maintenance, now });
250 assert.equal(busy.report.components.find((c) => c.key === "api")!.state, "partial");
251 assert.equal(busy.report.overall.title, "Partial outage");
252 assert.equal(busy.report.overall.line, "Identified: API errors.");
253 const resolved = buildPage({ parts, current, checkedAt: "2026-10-05T12:29:30Z", days: [], incidents: [{ ...incident, resolved_at: "2026-10-05T12:20:00Z" }], now });
254 assert.equal(resolved.report.components.find((c) => c.key === "api")!.state, "up", "a resolved incident no longer colours its part");
255});