Skip to content

Where people and agents ship software together. The open-source git platform for the whole job: issues, agents, checks and deploys to the edge.

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