Files
2026-09-23 11:17:01 -07:00

145 lines
6.0 KiB
TypeScript

import { describe, expect, it, jest, beforeEach } from "@jest/globals";
import { createMockLogger } from "../../../helpers/createMockLogger.ts";
import { makeNotification, makeMessage, makeMessageWithThresholds } from "../../../helpers/notificationMessage.ts";
import { testNotificationProviderContract } from "../../../helpers/notificationProviderContract.ts";
const mockGotPost = jest.fn().mockResolvedValue({});
jest.unstable_mockModule("got", () => ({ default: { post: mockGotPost } }));
const { PagerDutyProvider } = await import("../../../../src/domain/notifications/providers/pagerduty.ts");
const createProvider = () => {
const logger = createMockLogger();
return { provider: new PagerDutyProvider(logger as any), logger };
};
testNotificationProviderContract("PagerDutyProvider", {
create: () => {
mockGotPost.mockResolvedValue({});
return createProvider().provider;
},
makeNotification: () => makeNotification(),
});
describe("PagerDutyProvider", () => {
beforeEach(() => mockGotPost.mockReset().mockResolvedValue({}));
describe("sendTestAlert", () => {
it("sends to PagerDuty API and returns true", async () => {
expect(await createProvider().provider.sendTestAlert(makeNotification())).toBe(true);
expect(mockGotPost).toHaveBeenCalledWith(
"https://events.pagerduty.com/v2/enqueue",
expect.objectContaining({
json: expect.objectContaining({ routing_key: "https://hooks.example.com/webhook", event_action: "trigger" }),
})
);
});
it("returns false and logs on error", async () => {
mockGotPost.mockRejectedValue(new Error("fail"));
const { provider, logger } = createProvider();
expect(await provider.sendTestAlert(makeNotification())).toBe(false);
expect(logger.warn).toHaveBeenCalled();
});
it("handles non-Error thrown values", async () => {
mockGotPost.mockRejectedValue(null);
const { provider } = createProvider();
expect(await provider.sendTestAlert(makeNotification())).toBe(false);
});
});
describe("sendMessage", () => {
it("sends payload and returns true", async () => {
const { provider } = createProvider();
expect(await provider.sendMessage(makeNotification() as any, makeMessage())).toBe(true);
const payload = mockGotPost.mock.calls[0][1].json;
expect(payload.routing_key).toBe("https://hooks.example.com/webhook");
expect(payload.event_action).toBe("trigger");
expect(payload.dedup_key).toBe("checkmate-mon-1");
});
it("returns false when address is missing", async () => {
const { provider } = createProvider();
expect(await provider.sendMessage(makeNotification({ address: "" }) as any, makeMessage())).toBe(false);
});
it("returns false and logs on error", async () => {
mockGotPost.mockRejectedValue(new Error("fail"));
const { provider, logger } = createProvider();
expect(await provider.sendMessage(makeNotification() as any, makeMessage())).toBe(false);
expect(logger.warn).toHaveBeenCalled();
});
it("handles non-Error thrown values in sendMessage", async () => {
mockGotPost.mockRejectedValue(null);
const { provider } = createProvider();
expect(await provider.sendMessage(makeNotification() as any, makeMessage())).toBe(false);
});
it("uses 'resolve' event_action for monitor_up", async () => {
const { provider } = createProvider();
await provider.sendMessage(makeNotification() as any, makeMessage({ type: "monitor_up" }));
expect(mockGotPost.mock.calls[0][1].json.event_action).toBe("resolve");
});
it("uses 'resolve' event_action for threshold_resolved", async () => {
const { provider } = createProvider();
await provider.sendMessage(makeNotification() as any, makeMessage({ type: "threshold_resolved" }));
expect(mockGotPost.mock.calls[0][1].json.event_action).toBe("resolve");
});
it("uses 'resolve' event_action for container_resolved", async () => {
const { provider } = createProvider();
await provider.sendMessage(makeNotification() as any, makeMessage({ type: "container_resolved" }));
expect(mockGotPost.mock.calls[0][1].json.event_action).toBe("resolve");
});
it("uses 'trigger' event_action for container_breach", async () => {
const { provider } = createProvider();
await provider.sendMessage(makeNotification() as any, makeMessage({ type: "container_breach" }));
expect(mockGotPost.mock.calls[0][1].json.event_action).toBe("trigger");
});
it("includes threshold info in summary and custom_details", async () => {
const { provider } = createProvider();
await provider.sendMessage(makeNotification() as any, makeMessageWithThresholds());
const payload = mockGotPost.mock.calls[0][1].json;
expect(payload.payload.summary).toContain("CPU");
expect(payload.payload.custom_details.threshold_breaches).toBeDefined();
});
it("includes details in custom_details", async () => {
const { provider } = createProvider();
await provider.sendMessage(makeNotification() as any, makeMessage());
expect(mockGotPost.mock.calls[0][1].json.payload.custom_details.details).toBeDefined();
});
it("maps all severity levels", async () => {
const { provider } = createProvider();
for (const severity of ["critical", "warning", "info", "success"] as const) {
mockGotPost.mockClear();
await provider.sendMessage(makeNotification() as any, makeMessage({ severity }));
expect(mockGotPost.mock.calls[0][1].json.payload.severity).toBeTruthy();
}
});
it("uses 'error' for unknown severity", async () => {
const { provider } = createProvider();
await provider.sendMessage(makeNotification() as any, makeMessage({ severity: "unknown" as any }));
expect(mockGotPost.mock.calls[0][1].json.payload.severity).toBe("error");
});
it("omits threshold and details from custom_details when not present", async () => {
const { provider } = createProvider();
const msg = makeMessage();
msg.content.thresholds = undefined;
msg.content.details = undefined;
await provider.sendMessage(makeNotification() as any, msg);
const cd = mockGotPost.mock.calls[0][1].json.payload.custom_details;
expect(cd.threshold_breaches).toBeUndefined();
expect(cd.details).toBeUndefined();
});
});
});