udpate tests

This commit is contained in:
Alex Holliday committed 2026-09-23 13:54:55 -07:00
1 parent a1b10c7469
commit 7abc74ac98
2 files changed
+37 -27

No files matched your search

@@ -7,6 +7,7 @@ import type { IUsersRepository } from "../../src/domain/users/user.repository.in
import type { INotificationMessageBuilder } from "../../src/domain/notifications/notification.message-builder.ts";
import type { Monitor } from "../../src/domain/monitors/monitor.type.ts";
import type { MonitorActionDecision } from "../../src/worker/worker.interface.ts";
import type { Check } from "../../src/domain/checks/check.type.ts";
// ── Helpers ──────────────────────────────────────────────────────────────────
@@ -30,6 +31,17 @@ const makeDecision = (overrides?: Partial<MonitorActionDecision>): MonitorAction
...overrides,
});
const makeCheck = (statusCode: number): Check => ({
id: "check-1",
metadata: { monitorId: "mon-1", teamId: "team-1", type: "http" },
status: statusCode < 400,
statusCode,
responseTime: 100,
message: statusCode < 400 ? "OK" : "Service Unavailable",
createdAt: "2026-01-01T00:00:00Z",
updatedAt: "2026-01-01T00:00:00Z",
});
// ── Test suite ───────────────────────────────────────────────────────────────
describe("Incident lifecycle (integration)", () => {
@@ -53,7 +65,7 @@ describe("Incident lifecycle (integration)", () => {
const monitor = makeMonitor({ status: "down" });
const decision = makeDecision({ shouldCreateIncident: true, incidentReason: "status_down" });
const incident = await service.handleIncident(monitor, 503, decision);
const incident = await service.handleIncident(monitor, decision, makeCheck(503));
expect(incident).not.toBeNull();
expect(incident!.monitorId).toBe("mon-1");
@@ -74,8 +86,8 @@ describe("Incident lifecycle (integration)", () => {
const monitor = makeMonitor({ status: "down" });
const decision = makeDecision({ shouldCreateIncident: true, incidentReason: "status_down" });
const first = await service.handleIncident(monitor, 503, decision);
const second = await service.handleIncident(monitor, 503, decision);
const first = await service.handleIncident(monitor, decision, makeCheck(503));
const second = await service.handleIncident(monitor, decision, makeCheck(503));
expect(second!.id).toBe(first!.id);
expect(repo.getAll()).toHaveLength(1);
@@ -86,10 +98,10 @@ describe("Incident lifecycle (integration)", () => {
it("auto-resolves an incident when monitor recovers", async () => {
const monitor = makeMonitor({ status: "down" });
const createDecision = makeDecision({ shouldCreateIncident: true, incidentReason: "status_down" });
const created = await service.handleIncident(monitor, 503, createDecision);
const created = await service.handleIncident(monitor, createDecision, makeCheck(503));
const resolveDecision = makeDecision({ shouldResolveIncident: true });
const resolved = await service.handleIncident(monitor, 200, resolveDecision);
const resolved = await service.handleIncident(monitor, resolveDecision, makeCheck(200));
expect(resolved).not.toBeNull();
expect(resolved!.id).toBe(created!.id);
@@ -104,7 +116,7 @@ describe("Incident lifecycle (integration)", () => {
const monitor = makeMonitor({ status: "up" });
const decision = makeDecision({ shouldResolveIncident: true });
const result = await service.handleIncident(monitor, 200, decision);
const result = await service.handleIncident(monitor, decision, makeCheck(200));
expect(result).toBeNull();
});
@@ -114,7 +126,7 @@ describe("Incident lifecycle (integration)", () => {
it("manually resolves an active incident with comment", async () => {
const monitor = makeMonitor({ status: "down" });
const decision = makeDecision({ shouldCreateIncident: true, incidentReason: "status_down" });
const created = await service.handleIncident(monitor, 500, decision);
const created = await service.handleIncident(monitor, decision, makeCheck(500));
const resolved = await service.resolveIncident(created!.id, "user-1", "team-1", "Root cause identified", "user@test.com");
@@ -129,7 +141,7 @@ describe("Incident lifecycle (integration)", () => {
it("throws when manually resolving an already-resolved incident", async () => {
const monitor = makeMonitor({ status: "down" });
const decision = makeDecision({ shouldCreateIncident: true, incidentReason: "status_down" });
const created = await service.handleIncident(monitor, 500, decision);
const created = await service.handleIncident(monitor, decision, makeCheck(500));
await service.resolveIncident(created!.id, "user-1", "team-1");
@@ -146,7 +158,7 @@ describe("Incident lifecycle (integration)", () => {
]);
const decision = makeDecision({ shouldCreateIncident: true, incidentReason: "threshold_breach" });
const incident = await service.handleIncident(monitor, 200, decision, { monitorId: "mon-1" } as any);
const incident = await service.handleIncident(monitor, decision, makeCheck(200));
expect(incident!.statusCode).toBe(9999);
expect(incident!.message).toBe("CPU: 92% (threshold: 80%), MEMORY: 88% (threshold: 85%)");
@@ -158,7 +170,7 @@ describe("Incident lifecycle (integration)", () => {
const monitor = makeMonitor({ status: "up" });
const decision = makeDecision();
const result = await service.handleIncident(monitor, 200, decision);
const result = await service.handleIncident(monitor, decision, makeCheck(200));
expect(result).toBeNull();
expect(repo.getAll()).toHaveLength(0);
@@ -170,19 +182,19 @@ describe("Incident lifecycle (integration)", () => {
const monitor = makeMonitor({ status: "down" });
// First outage
const first = await service.handleIncident(monitor, 503, makeDecision({ shouldCreateIncident: true, incidentReason: "status_down" }));
const first = await service.handleIncident(monitor, makeDecision({ shouldCreateIncident: true, incidentReason: "status_down" }), makeCheck(503));
expect(first!.status).toBe(true);
// Manually resolved
await service.resolveIncident(first!.id, "user-1", "team-1", "Restarted server");
// Second outage — new incident since previous was resolved
const second = await service.handleIncident(monitor, 502, makeDecision({ shouldCreateIncident: true, incidentReason: "status_down" }));
const second = await service.handleIncident(monitor, makeDecision({ shouldCreateIncident: true, incidentReason: "status_down" }), makeCheck(502));
expect(second!.id).not.toBe(first!.id);
expect(second!.statusCode).toBe(502);
// Auto-resolve
const resolved = await service.handleIncident(monitor, 200, makeDecision({ shouldResolveIncident: true }));
const resolved = await service.handleIncident(monitor, makeDecision({ shouldResolveIncident: true }), makeCheck(200));
expect(resolved!.id).toBe(second!.id);
expect(resolved!.status).toBe(false);
expect(resolved!.resolutionType).toBe("automatic");
@@ -200,14 +212,14 @@ describe("Incident lifecycle (integration)", () => {
const monitorB = makeMonitor({ id: "mon-b", status: "down" });
const decision = makeDecision({ shouldCreateIncident: true, incidentReason: "status_down" });
const incidentA = await service.handleIncident(monitorA, 500, decision);
const incidentB = await service.handleIncident(monitorB, 502, decision);
const incidentA = await service.handleIncident(monitorA, decision, makeCheck(500));
const incidentB = await service.handleIncident(monitorB, decision, makeCheck(502));
expect(incidentA!.id).not.toBe(incidentB!.id);
expect(repo.getAll()).toHaveLength(2);
// Resolving monitor A does not affect monitor B
const resolved = await service.handleIncident(monitorA, 200, makeDecision({ shouldResolveIncident: true }));
const resolved = await service.handleIncident(monitorA, makeDecision({ shouldResolveIncident: true }), makeCheck(200));
expect(resolved!.id).toBe(incidentA!.id);
const bStillActive = await repo.findActiveByMonitorId("mon-b", "team-1");
@@ -110,7 +110,7 @@ describe("IncidentService", () => {
describe("handleIncident", () => {
it("returns null when neither create nor resolve is requested", async () => {
const { service } = createService();
const result = await service.handleIncident(makeMonitor(), 200, makeDecision(), makeCheck());
const result = await service.handleIncident(makeMonitor(), makeDecision(), makeCheck());
expect(result).toBeNull();
});
@@ -121,9 +121,8 @@ describe("IncidentService", () => {
const result = await service.handleIncident(
makeMonitor(),
500,
makeDecision({ shouldCreateIncident: true, incidentReason: "status_down" }),
makeCheck()
makeCheck({ statusCode: 500 })
);
expect(result).toBe(existing);
@@ -138,9 +137,8 @@ describe("IncidentService", () => {
const result = await service.handleIncident(
makeMonitor(),
500,
makeDecision({ shouldCreateIncident: true, incidentReason: "status_down" }),
makeCheck()
makeCheck({ statusCode: 500 })
);
expect(result).toBe(created);
@@ -166,7 +164,7 @@ describe("IncidentService", () => {
const monitor = makeMonitor({ type: "hardware" });
const check = makeCheck({ metadata: { monitorId: "mon-1", teamId: "team-1", type: "hardware" }, cpu: { usage_percent: 0.95 } });
const decision = makeDecision({ shouldCreateIncident: true, incidentReason: "threshold_breach" });
await service.handleIncident(monitor, 200, decision, check);
await service.handleIncident(monitor, decision, check);
expect(notificationMessageBuilder.extractThresholdBreaches).toHaveBeenCalledWith(monitor, check);
expect(incidentsRepository.create).toHaveBeenCalledWith(
@@ -185,7 +183,7 @@ describe("IncidentService", () => {
(notificationMessageBuilder.extractThresholdBreaches as jest.Mock).mockReturnValue([]);
const decision = makeDecision({ shouldCreateIncident: true, incidentReason: "threshold_breach" });
await service.handleIncident(makeMonitor(), 200, decision, makeCheck());
await service.handleIncident(makeMonitor(), decision, makeCheck());
expect(incidentsRepository.create).toHaveBeenCalledWith(expect.objectContaining({ message: "Threshold breach detected" }));
});
@@ -207,7 +205,7 @@ describe("IncidentService", () => {
containerSummary: { total: 3, running: 2, stopped: 1, unhealthy: 1 },
});
const decision = makeDecision({ shouldCreateIncident: true, incidentReason: "threshold_breach" });
await service.handleIncident(monitor, 200, decision, check);
await service.handleIncident(monitor, decision, check);
expect(incidentsRepository.create).toHaveBeenCalledWith(
expect.objectContaining({ statusCode: 9999, message: "db: stopped (exit code 1), api: unhealthy" })
@@ -226,7 +224,7 @@ describe("IncidentService", () => {
]);
const decision = makeDecision({ shouldCreateIncident: true, incidentReason: "threshold_breach" });
await service.handleIncident(makeMonitor(), 200, decision, makeCheck());
await service.handleIncident(makeMonitor(), decision, makeCheck());
expect(incidentsRepository.create).toHaveBeenCalledWith(
expect.objectContaining({ message: "CPU: 95% (threshold: 80%), MEMORY: 90% (threshold: 80%)" })
@@ -240,7 +238,7 @@ describe("IncidentService", () => {
(incidentsRepository.findActiveByMonitorId as jest.Mock).mockResolvedValue(active);
(incidentsRepository.updateById as jest.Mock).mockResolvedValue(resolved);
const result = await service.handleIncident(makeMonitor(), 200, makeDecision({ shouldResolveIncident: true }), makeCheck());
const result = await service.handleIncident(makeMonitor(), makeDecision({ shouldResolveIncident: true }), makeCheck());
expect(result).toBe(resolved);
expect(incidentsRepository.updateById).toHaveBeenCalledWith(
@@ -254,7 +252,7 @@ describe("IncidentService", () => {
const { service, incidentsRepository } = createService();
(incidentsRepository.findActiveByMonitorId as jest.Mock).mockResolvedValue(null);
const result = await service.handleIncident(makeMonitor(), 200, makeDecision({ shouldResolveIncident: true }), makeCheck());
const result = await service.handleIncident(makeMonitor(), makeDecision({ shouldResolveIncident: true }), makeCheck());
expect(result).toBeNull();
});