Files
Checkmate/server/test/integration/heartbeatJobMaintenance.test.ts
T
2026-09-25 11:39:16 -07:00

147 lines
5.3 KiB
TypeScript

import { describe, expect, it, beforeEach } from "@jest/globals";
import { createHeartbeatTestHarness, makeMonitor, type HeartbeatTestHarness } from "../helpers/heartbeatTestHarness.ts";
import type { MaintenanceWindow } from "../../src/domain/maintenance-windows/maintenance-window.type.ts";
const makeMaintenanceWindow = (overrides?: Partial<MaintenanceWindow>): MaintenanceWindow => {
const now = new Date();
const start = new Date(now.getTime() - 60 * 60 * 1000); // 1 hour ago
const end = new Date(now.getTime() + 60 * 60 * 1000); // 1 hour from now
return {
id: "mw-1",
monitorIds: ["mon-1"],
teamId: "team-1",
active: true,
name: "Test Maintenance",
duration: 2,
durationUnit: "hours",
repeat: 0,
start: start.toISOString(),
end: end.toISOString(),
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
...overrides,
};
};
describe("Heartbeat job: maintenance windows", () => {
let h: HeartbeatTestHarness;
beforeEach(() => {
h = createHeartbeatTestHarness();
});
it("skips checks and sets monitor to maintenance when window is active", async () => {
const monitor = makeMonitor();
h.monitorsRepo.seed(monitor);
// Configure an active maintenance window covering the current time
h.maintenanceWindowsRepo.findByMonitorId.mockResolvedValue([makeMaintenanceWindow()]);
await h.heartbeatJob(monitor);
// Monitor status should be set to maintenance
const storedMonitor = await h.monitorsRepo.findById("mon-1", "team-1");
expect(storedMonitor.status).toBe("maintenance");
// No network request should have been made
expect(h.providerRegistry.probe).not.toHaveBeenCalled();
// No check should have been buffered
expect(h.bufferStub.addToBuffer).not.toHaveBeenCalled();
// No incident should have been created
expect(h.incidentsRepo.getAll()).toHaveLength(0);
// No notification should have been sent
expect(h.notificationsService.handleNotifications).not.toHaveBeenCalled();
});
it("does not update status if monitor is already in maintenance", async () => {
const monitor = makeMonitor({ status: "maintenance" });
h.monitorsRepo.seed(monitor);
h.maintenanceWindowsRepo.findByMonitorId.mockResolvedValue([makeMaintenanceWindow()]);
await h.heartbeatJob(monitor);
// findById reads the stored monitor; updateById should not have been called
// since status was already "maintenance"
const storedMonitor = await h.monitorsRepo.findById("mon-1", "team-1");
expect(storedMonitor.status).toBe("maintenance");
expect(h.providerRegistry.probe).not.toHaveBeenCalled();
});
it("resumes normal checks when maintenance window expires", async () => {
const monitor = makeMonitor();
h.monitorsRepo.seed(monitor);
// First heartbeat: inside maintenance window
h.maintenanceWindowsRepo.findByMonitorId.mockResolvedValue([makeMaintenanceWindow()]);
await h.heartbeatJob(monitor);
let storedMonitor = await h.monitorsRepo.findById("mon-1", "team-1");
expect(storedMonitor.status).toBe("maintenance");
// Second heartbeat: maintenance window expired (no windows returned)
h.maintenanceWindowsRepo.findByMonitorId.mockResolvedValue([]);
h.setNextResponse(true, 200);
await h.heartbeatJob(monitor);
// Network request should have been made this time
expect(h.providerRegistry.probe).toHaveBeenCalledTimes(1);
// Check should have been buffered
expect(h.bufferStub.addToBuffer).toHaveBeenCalledTimes(1);
});
it("skips checks when window is inactive (active: false)", async () => {
const monitor = makeMonitor();
h.monitorsRepo.seed(monitor);
// Inactive maintenance window — should be ignored
h.maintenanceWindowsRepo.findByMonitorId.mockResolvedValue([makeMaintenanceWindow({ active: false })]);
h.setNextResponse(true, 200);
await h.heartbeatJob(monitor);
// Normal check should proceed since window is inactive
expect(h.providerRegistry.probe).toHaveBeenCalledTimes(1);
expect(h.bufferStub.addToBuffer).toHaveBeenCalledTimes(1);
});
it("skips checks for every monitor covered by a group window", async () => {
const monitorA = makeMonitor({ id: "mon-a", name: "Monitor A" });
const monitorB = makeMonitor({ id: "mon-b", name: "Monitor B" });
h.monitorsRepo.seed(monitorA);
h.monitorsRepo.seed(monitorB);
// A single group window covers both monitors
h.maintenanceWindowsRepo.findByMonitorId.mockResolvedValue([makeMaintenanceWindow({ monitorIds: ["mon-a", "mon-b"] })]);
await h.heartbeatJob(monitorA);
await h.heartbeatJob(monitorB);
expect((await h.monitorsRepo.findById("mon-a", "team-1")).status).toBe("maintenance");
expect((await h.monitorsRepo.findById("mon-b", "team-1")).status).toBe("maintenance");
expect(h.providerRegistry.probe).not.toHaveBeenCalled();
});
it("skips checks when window is in the past (expired)", async () => {
const monitor = makeMonitor();
h.monitorsRepo.seed(monitor);
// Window ended an hour ago
const pastEnd = new Date(Date.now() - 60 * 60 * 1000);
const pastStart = new Date(pastEnd.getTime() - 2 * 60 * 60 * 1000);
h.maintenanceWindowsRepo.findByMonitorId.mockResolvedValue([
makeMaintenanceWindow({ start: pastStart.toISOString(), end: pastEnd.toISOString() }),
]);
h.setNextResponse(true, 200);
await h.heartbeatJob(monitor);
// Window is in the past — normal check should proceed
expect(h.providerRegistry.probe).toHaveBeenCalledTimes(1);
});
});