g1t/apps/status/src/model.test.ts
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 quotas | 1 | import assert from "node:assert/strict"; |
| 2 | import { test } from "node:test"; | |
| 3 | ||
| 4 | import { components } from "./components.ts"; | |
| 5 | import { | |
| 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 | ||
| 22 | const 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 | ||
| 32 | const 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 | ||
| 43 | test("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 | ||
| 51 | test("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 | ||
| 61 | test("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 | ||
| 73 | test("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 | ||
| 80 | test("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 | ||
| 89 | test("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 | ||
| 103 | test("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 | ||
| 124 | test("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 | ||
| 139 | test("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 | ||
| 155 | test("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 | ||
| 177 | test("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 | ); | |
| Fast pages, required checks on the branch, self-hosted runners, honest incidents | 181 | assert.deepEqual(keys, ["site", "api", "git", "speed"]); |
| status.g1t.sh with incident management, invites that land you in the workspace, settings as pages, usage without quotas | 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), | |
| Fast pages, required checks on the branch, self-hosted runners, honest incidents | 191 | ["site", "api", "git", "speed", "mcp", "docs", "deployments", "agents", "sandboxes", "billing"], |
| status.g1t.sh with incident management, invites that land you in the workspace, settings as pages, usage without quotas | 192 | ); |
| Fast pages, required checks on the branch, self-hosted runners, honest incidents | 193 | const speed = all.find((c) => c.key === "speed")!; |
| 194 | assert.deepEqual(speed.check, { kind: "http", steps: [{ url: "https://g1t.sh/flagon-io/g1t" }, { url: "https://g1t.sh/explore" }] }); | |
| 195 | assert.equal(classify({ ok: true, ms: 900 }, speed.slowMs).state, "degraded"); | |
| 196 | assert.equal(classify({ ok: true, ms: 300 }, speed.slowMs).state, "up"); | |
| status.g1t.sh with incident management, invites that land you in the workspace, settings as pages, usage without quotas | 197 | const git = all.find((c) => c.key === "git")!; |
| 198 | assert.deepEqual(git.check, { kind: "http", steps: [{ url: "https://g1t.sh/flagon-io/g1t.git/info/refs?service=git-upload-pack" }] }); | |
| 199 | }); | |
| 200 | ||
| 201 | test("a part's state: the worse of its check and open incidents; maintenance unless an incident says worse", () => { | |
| 202 | assert.equal(effectiveState("up", [], false), "up"); | |
| 203 | assert.equal(effectiveState("up", ["partial_outage"], false), "partial"); | |
| 204 | assert.equal(effectiveState("down", ["degraded"], false), "down", "a failing check beats a milder report"); | |
| 205 | assert.equal(effectiveState("degraded", ["major_outage", "degraded"], false), "down", "the worst impact wins"); | |
| 206 | assert.equal(effectiveState("up", ["operational"], false), "up"); | |
| 207 | assert.equal(effectiveState("unmonitored", ["degraded"], false), "degraded", "an incident speaks for a part with no check"); | |
| 208 | assert.equal(effectiveState("down", [], true), "maintenance", "failing checks in a window are expected"); | |
| 209 | assert.equal(effectiveState("up", ["major_outage"], true), "down", "an incident during maintenance still shows"); | |
| 210 | }); | |
| 211 | ||
| 212 | test("maintenance moves with its window, and cancelled or completed stays put", () => { | |
| 213 | const m = { state: "scheduled" as const, starts_at: "2026-10-05T12:00:00Z", ends_at: "2026-10-05T13:00:00Z" }; | |
| 214 | assert.equal(maintenanceState(m, new Date("2026-10-05T11:59:00Z")), "scheduled"); | |
| 215 | assert.equal(maintenanceState(m, new Date("2026-10-05T12:00:00Z")), "in_progress"); | |
| 216 | assert.equal(maintenanceState(m, new Date("2026-10-05T13:00:00Z")), "completed"); | |
| 217 | assert.equal(maintenanceState({ ...m, state: "in_progress" }, new Date("2026-10-05T11:00:00Z")), "in_progress", "started early by hand"); | |
| 218 | assert.equal(maintenanceState({ ...m, state: "cancelled" }, new Date("2026-10-05T12:30:00Z")), "cancelled"); | |
| 219 | assert.equal(maintenanceState({ ...m, state: "completed" }, new Date("2026-10-05T12:30:00Z")), "completed", "finished early by hand"); | |
| 220 | }); | |
| 221 | ||
| 222 | test("buildPage: incident impacts and maintenance reach the parts and the banner", () => { | |
| 223 | const now = new Date("2026-10-05T12:30:00Z"); | |
| 224 | const parts = components(VARS, false); | |
| 225 | const current = new Map(parts.map((p) => [p.key, { state: "up" as const, detail: "Answered in 80 ms", latency_ms: 80 }])); | |
| 226 | current.set("docs", { state: "down", detail: "Failed: HTTP 502", latency_ms: null }); | |
| 227 | const maintenance = [ | |
| 228 | { 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: [] }, | |
| 229 | { 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: [] }, | |
| 230 | { 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: [] }, | |
| 231 | ]; | |
| 232 | const quiet = buildPage({ parts, current, checkedAt: "2026-10-05T12:29:30Z", days: [], incidents: [], maintenance, now }); | |
| 233 | const docs = quiet.report.components.find((c) => c.key === "docs")!; | |
| 234 | assert.equal(docs.state, "maintenance"); | |
| 235 | assert.equal(docs.check_state, "down"); | |
| 236 | assert.equal(quiet.report.overall.state, "maintenance"); | |
| 237 | assert.equal(quiet.report.overall.title, "Under maintenance"); | |
| 238 | assert.deepEqual(quiet.report.maintenance.map((m) => [m.id, m.state]), [["m1", "in_progress"], ["m2", "scheduled"]]); | |
| 239 | ||
| 240 | const incident = { | |
| 241 | id: "i1", | |
| 242 | title: "API errors", | |
| 243 | impact: "degraded" as const, | |
| 244 | status: "identified" as const, | |
| 245 | components: ["api"], | |
| 246 | component_impacts: [{ key: "api", impact: "partial_outage" as const }], | |
| 247 | started_at: "2026-10-05T12:10:00Z", | |
| 248 | resolved_at: null, | |
| 249 | url: "", | |
| 250 | postmortem_published_at: null, | |
| 251 | updates: [], | |
| 252 | }; | |
| 253 | const busy = buildPage({ parts, current, checkedAt: "2026-10-05T12:29:30Z", days: [], incidents: [incident], maintenance, now }); | |
| 254 | assert.equal(busy.report.components.find((c) => c.key === "api")!.state, "partial"); | |
| 255 | assert.equal(busy.report.overall.title, "Partial outage"); | |
| 256 | assert.equal(busy.report.overall.line, "Identified: API errors."); | |
| 257 | const resolved = buildPage({ parts, current, checkedAt: "2026-10-05T12:29:30Z", days: [], incidents: [{ ...incident, resolved_at: "2026-10-05T12:20:00Z" }], now }); | |
| 258 | assert.equal(resolved.report.components.find((c) => c.key === "api")!.state, "up", "a resolved incident no longer colours its part"); | |
| 259 | }); | |
| Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily | 260 | |
| 261 | test("git storage is listed only with the repos service, and degraded says why", () => { | |
| 262 | assert.equal(components(VARS, true).some((part) => part.key === "storage"), false); | |
| 263 | const storage = components(VARS, true, true).find((part) => part.key === "storage"); | |
| 264 | assert.equal(storage?.check.kind, "storage"); | |
| 265 | assert.equal(storage?.core, false); | |
| 266 | assert.deepEqual(classify({ ok: true, ms: 40, degraded: "Rate limited 2 times in 5 minutes" }), { | |
| 267 | state: "degraded", | |
| 268 | detail: "Rate limited 2 times in 5 minutes", | |
| 269 | }); | |
| 270 | }); |