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

71 lines3,725 bytesCodeBlame
1import assert from "node:assert/strict";
2import { test } from "node:test";
3
4import { DETECT_AFTER, type Streak, detect, detectedImpact, draftTitle } from "./detect.ts";
5
6const at = (minute: number) => new Date(Date.UTC(2026, 9, 5, 12, minute));
7
8/** Runs rounds of checks through detection, carrying the streaks along. */
9function run(rounds: Record<string, "up" | "degraded" | "down">[], open: { id: string; components: string[] }[] = [], maintenance = new Set<string>()) {
10 let streaks = new Map<string, Streak>();
11 const results = rounds.map((round, minute) => {
12 const found = detect(streaks, Object.entries(round).map(([component, state]) => ({ component, state })), open, maintenance, at(minute));
13 streaks = new Map(found.streaks.map((s) => [s.component, s]));
14 return found;
15 });
16 return results;
17}
18
19test(`a draft after ${DETECT_AFTER} failed checks in a row, once`, () => {
20 assert.equal(DETECT_AFTER, 3);
21 const rounds = run([{ api: "down" }, { api: "down" }, { api: "down" }, { api: "down" }]);
22 assert.deepEqual(rounds.map((r) => r.draft.length), [0, 0, 1, 0]);
23 assert.deepEqual(rounds[2]!.draft[0], { key: "api", state: "down", since: at(0).toISOString() });
24});
25
26test("a blip is not an incident: a good check resets the run", () => {
27 const rounds = run([{ api: "down" }, { api: "down" }, { api: "up" }, { api: "down" }, { api: "down" }]);
28 assert.ok(rounds.every((r) => r.draft.length === 0));
29 assert.equal(rounds[4]!.streaks[0]!.count, 2);
30});
31
32test("slow then failing is one run, at its worst", () => {
33 const rounds = run([{ docs: "degraded" }, { docs: "down" }, { docs: "degraded" }]);
34 assert.deepEqual(rounds[2]!.draft, [{ key: "docs", state: "down", since: at(0).toISOString() }]);
35});
36
37test("parts failing together share one draft", () => {
38 const rounds = run([{ api: "down", git: "down" }, { api: "down", git: "down" }, { api: "down", git: "down" }]);
39 assert.deepEqual(rounds[2]!.draft.map((d) => d.key), ["api", "git"]);
40});
41
42test("with an incident already open on the part: a line on it, no draft; recovery is noted", () => {
43 const open = [{ id: "inc1", components: ["api"] }];
44 const rounds = run([{ api: "down" }, { api: "down" }, { api: "down" }, { api: "up" }], open);
45 assert.equal(rounds[2]!.draft.length, 0);
46 assert.deepEqual(rounds[2]!.failing, [{ incident: "inc1", key: "api", state: "down", since: at(0).toISOString() }]);
47 assert.deepEqual(rounds[3]!.recovered, [{ incident: "inc1", key: "api", since: at(0).toISOString(), checks: 3 }]);
48 assert.equal(rounds[3]!.streaks.length, 0);
49});
50
51test("a short blip that never crossed the line is not noted as a recovery", () => {
52 const rounds = run([{ api: "down" }, { api: "up" }], [{ id: "inc1", components: ["api"] }]);
53 assert.equal(rounds[1]!.recovered.length, 0);
54});
55
56test("parts under maintenance and unmonitored parts are left alone", () => {
57 const rounds = run([{ api: "down" }, { api: "down" }, { api: "down" }], [], new Set(["api"]));
58 assert.ok(rounds.every((r) => r.draft.length === 0 && r.streaks.length === 0));
59 const none = detect(new Map(), [{ component: "sandboxes", state: "unmonitored" }], [], new Set(), at(0));
60 assert.equal(none.streaks.length, 0);
61});
62
63test("the draft says what failed", () => {
64 assert.equal(detectedImpact("down"), "major_outage");
65 assert.equal(detectedImpact("degraded"), "degraded");
66 assert.equal(draftTitle([{ name: "API", state: "down" }, { name: "Git", state: "down" }]), "Detected: API and Git not answering");
67 assert.equal(
68 draftTitle([{ name: "API", state: "down" }, { name: "Git", state: "down" }, { name: "MCP", state: "down" }, { name: "Docs", state: "degraded" }]),
69 "Detected: API, Git and MCP not answering; Docs slow",
70 );
71});