Skip to content

Commit 581cfab

Browse files
committed
test(status): cover the remaining branches in the alert fold
codecov/patch flagged 94% against the 99% gate. Branch coverage on service-status.ts was 92.3%, with three arms of the per-component fold unexercised -- all of them in the paths that decide what a malformed or partial alert does: • a `service` label that is not a string. Alertmanager's contract says Record<string, string>, but this reads a parsed JSON body from a source that can ship anything, and a non-string must take the same ignore path an unmapped label takes rather than throwing on .toLowerCase(); • a firing alert with no usable `startsAt`. Still degraded -- an alert with no timestamp is still an alert -- but no invented `since`; • the KEEP arms of the earliest-wins comparison. The existing test put the earlier alert second, which only exercises the replace arm; the mirror case (earlier first, and a later untimestamped one) leaves half the comparison free to invert unnoticed. Branch coverage is now 100% (39/39), statements and lines already were. No production code changed.
1 parent ac6c046 commit 581cfab

1 file changed

Lines changed: 31 additions & 0 deletions

File tree

test/unit/service-status.test.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,37 @@ describe("buildServiceStatus", () => {
120120
expect(payload.components.map((c) => c.component)).toEqual([...SERVICE_STATUS_COMPONENTS]);
121121
});
122122

123+
it("ignores an alert whose service label is not a string at all", () => {
124+
// Alertmanager labels are `Record<string, string>` by contract, but this reads a parsed JSON body from a
125+
// source that could ship anything. A non-string label must take the same ignore path an unmapped one
126+
// takes, not throw on `.toLowerCase()`.
127+
const payload = buildServiceStatus([{ labels: { service: 42 }, startsAt: "x", status: { state: "active" } }, { labels: {} }], NOW.toISOString());
128+
expect(payload.overall).toBe("operational");
129+
});
130+
131+
it("records no `since` when the firing alert carries no usable start time", () => {
132+
// `startsAt` absent or non-string: the component is still degraded — an alert with no timestamp is still
133+
// an alert — but inventing a start would imply a transition that was never reported.
134+
const payload = buildServiceStatus([alert({ startsAt: undefined })], NOW.toISOString());
135+
const review = payload.components.find((c) => c.component === "review");
136+
expect(review?.status).toBe("degraded");
137+
expect(review?.since).toBeNull();
138+
});
139+
140+
it("keeps an established `since` when a later alert for the same component has no start time", () => {
141+
// The timestamped alert established the incident start; a subsequent untimestamped one must not erase it.
142+
const payload = buildServiceStatus([alert({ startsAt: "2026-07-31T09:00:00.000Z" }), alert({ startsAt: null })], NOW.toISOString());
143+
expect(payload.components.find((c) => c.component === "review")?.since).toBe("2026-07-31T09:00:00.000Z");
144+
});
145+
146+
it("keeps the earlier `since` when the alerts arrive in chronological order too", () => {
147+
// The mirror of the earliest-wins test above. That one has the earlier alert SECOND, so it only exercises
148+
// the replace arm; this one has it first and exercises the keep arm. Without both, half the comparison is
149+
// free to invert unnoticed.
150+
const payload = buildServiceStatus([alert({ startsAt: "2026-07-31T09:00:00.000Z" }), alert({ startsAt: "2026-07-31T11:30:00.000Z" })], NOW.toISOString());
151+
expect(payload.components.find((c) => c.component === "review")?.since).toBe("2026-07-31T09:00:00.000Z");
152+
});
153+
123154
it("clears `since` for a component that is operational", () => {
124155
expect(buildServiceStatus([], NOW.toISOString()).components.every((c) => c.since === null)).toBe(true);
125156
});

0 commit comments

Comments
 (0)