udpate tests
This commit is contained in:
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();
|
||||
});
|
||||
|
||||
Reference in new issue
Block a user