From f86ceae0c68ad7a21f0e0eff23035dbbc53a7faf Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Tue, 12 May 2026 10:41:30 -0700 Subject: [PATCH] update tests --- .../heartbeatJobMaintenance.test.ts | 19 +++++- .../services/maintenanceWindowService.test.ts | 66 +++++++++++++++++-- 2 files changed, 78 insertions(+), 7 deletions(-) diff --git a/server/test/integration/heartbeatJobMaintenance.test.ts b/server/test/integration/heartbeatJobMaintenance.test.ts index 663c2e61e..5df287242 100644 --- a/server/test/integration/heartbeatJobMaintenance.test.ts +++ b/server/test/integration/heartbeatJobMaintenance.test.ts @@ -8,7 +8,7 @@ const makeMaintenanceWindow = (overrides?: Partial): Maintena const end = new Date(now.getTime() + 60 * 60 * 1000); // 1 hour from now return { id: "mw-1", - monitorId: "mon-1", + monitorIds: ["mon-1"], teamId: "team-1", active: true, name: "Test Maintenance", @@ -109,6 +109,23 @@ describe("Heartbeat job: maintenance windows", () => { 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.networkService.requestStatus).not.toHaveBeenCalled(); + }); + it("skips checks when window is in the past (expired)", async () => { const monitor = makeMonitor(); h.monitorsRepo.seed(monitor); diff --git a/server/test/unit/services/maintenanceWindowService.test.ts b/server/test/unit/services/maintenanceWindowService.test.ts index 302ff128c..5ce64238a 100644 --- a/server/test/unit/services/maintenanceWindowService.test.ts +++ b/server/test/unit/services/maintenanceWindowService.test.ts @@ -37,7 +37,7 @@ const createService = (overrides?: { const makeWindow = (overrides?: Partial): MaintenanceWindow => ({ id: "mw-1", - monitorId: "mon-1", + monitorIds: ["mon-1"], teamId: "team-1", active: true, name: "Scheduled Maintenance", @@ -80,16 +80,16 @@ describe("MaintenanceWindowService", () => { // ── createMaintenanceWindow ────────────────────────────────────────────── describe("createMaintenanceWindow", () => { - it("creates a maintenance window for each monitor", async () => { + it("creates a single maintenance window covering all monitor IDs", async () => { const { service, maintenanceWindowsRepository } = createService(); await service.createMaintenanceWindow(defaultCreateParams); - expect(maintenanceWindowsRepository.create).toHaveBeenCalledTimes(2); + expect(maintenanceWindowsRepository.create).toHaveBeenCalledTimes(1); expect(maintenanceWindowsRepository.create).toHaveBeenCalledWith( expect.objectContaining({ teamId: "team-1", - monitorId: "mon-1", + monitorIds: ["mon-1", "mon-2"], name: "Scheduled Maintenance", active: true, duration: 60, @@ -99,7 +99,6 @@ describe("MaintenanceWindowService", () => { end: "2026-04-10T03:00:00Z", }) ); - expect(maintenanceWindowsRepository.create).toHaveBeenCalledWith(expect.objectContaining({ monitorId: "mon-2" })); }); it("verifies monitor ownership via findByIds", async () => { @@ -130,12 +129,13 @@ describe("MaintenanceWindowService", () => { await expect(service.createMaintenanceWindow(defaultCreateParams)).resolves.toBeUndefined(); }); - it("creates a single window when monitorIDs has one entry", async () => { + it("passes a single-element monitorIds array when only one monitor is provided", async () => { const { service, maintenanceWindowsRepository } = createService(); await service.createMaintenanceWindow({ ...defaultCreateParams, monitorIDs: ["mon-1"] }); expect(maintenanceWindowsRepository.create).toHaveBeenCalledTimes(1); + expect(maintenanceWindowsRepository.create).toHaveBeenCalledWith(expect.objectContaining({ monitorIds: ["mon-1"] })); }); }); @@ -248,5 +248,59 @@ describe("MaintenanceWindowService", () => { expect(maintenanceWindowsRepository.updateById).toHaveBeenCalledWith("mw-1", "team-1", { active: false, repeat: 3600000 }); }); + + it("maps the monitors body field to monitorIds when calling the repository", async () => { + const { service, maintenanceWindowsRepository } = createService(); + + await service.editMaintenanceWindow({ + id: "mw-1", + teamId: "team-1", + body: { monitors: ["mon-1", "mon-2"] }, + }); + + expect(maintenanceWindowsRepository.updateById).toHaveBeenCalledWith("mw-1", "team-1", { monitorIds: ["mon-1", "mon-2"] }); + }); + + it("verifies monitor ownership when monitors is in the body", async () => { + const { service, monitorsRepository } = createService(); + + await service.editMaintenanceWindow({ + id: "mw-1", + teamId: "team-1", + body: { monitors: ["mon-1", "mon-2"] }, + }); + + expect(monitorsRepository.findByIds).toHaveBeenCalledWith(["mon-1", "mon-2"]); + }); + + it("throws 403 when a new monitor belongs to a different team", async () => { + const monitorsRepository = createMonitorsRepo(); + (monitorsRepository.findByIds as jest.Mock).mockResolvedValue([ + { id: "mon-1", teamId: "team-1" }, + { id: "mon-2", teamId: "team-other" }, + ]); + const { service, maintenanceWindowsRepository } = createService({ monitorsRepository }); + + await expect( + service.editMaintenanceWindow({ + id: "mw-1", + teamId: "team-1", + body: { monitors: ["mon-1", "mon-2"] }, + }) + ).rejects.toThrow("Unauthorized to edit maintenance window for one or more monitors"); + expect(maintenanceWindowsRepository.updateById).not.toHaveBeenCalled(); + }); + + it("does not call findByIds when monitors is not in the body", async () => { + const { service, monitorsRepository } = createService(); + + await service.editMaintenanceWindow({ + id: "mw-1", + teamId: "team-1", + body: { name: "Updated Name" }, + }); + + expect(monitorsRepository.findByIds).not.toHaveBeenCalled(); + }); }); });