fix names

This commit is contained in:
Alex Holliday committed 2026-09-25 11:39:16 -07:00
1 parent 712350d669
commit 98a5fa9941
24 files changed
+178 -571

No files matched your search

-1
View File
@@ -1,5 +1,4 @@
import { Resolver } from "dns/promises";
import axios from "axios";
import got from "got";
import ping from "ping";
import Docker from "dockerode";
+2 -2
View File
@@ -54,7 +54,7 @@ export class EgressService implements IEgressService {
private settingsService: ISettingsService,
private egressStateRepository: IEgressStateRepository,
private jobsRepository: IJobsRepository,
private networkService: IProviderRegistry,
private providerRegistry: IProviderRegistry,
private proxyResolver: IProxyResolver,
private logger: ILogger
) {}
@@ -100,7 +100,7 @@ export class EgressService implements IEgressService {
const { response, responseTime, error } = await timeRequest(async () => {
const proxyUrl = await this.proxyResolver.resolve(monitor);
return Promise.race([
this.networkService.probe(monitor, { proxyUrl }),
this.providerRegistry.probe(monitor, { proxyUrl }),
new Promise<never>((_, reject) => {
timer = setTimeout(() => reject(new Error(`Egress probe timed out after ${PROBE_TIMEOUT_MS}ms`)), PROBE_TIMEOUT_MS);
}),
@@ -2,7 +2,7 @@ import type { Monitor, MonitorType } from "@/domain/monitors/monitor.type.js";
import type { CheckContext, MonitorPayloadMap, MonitorStatusResponse } from "@/types/network.js";
import { NETWORK_ERROR } from "@/types/network.js";
import { IStatusProvider } from "./IStatusProvider.js";
const SERVICE_NAME = "NetworkService";
const SERVICE_NAME = "ProviderRegistry";
export interface IProviderRegistry {
probe<T extends MonitorType>(monitor: Monitor & { type: T }, ctx?: CheckContext): Promise<MonitorStatusResponse<MonitorPayloadMap[T]>>;
+5 -5
View File
@@ -60,7 +60,7 @@ export interface HeartbeatTestHarness {
statusService: StatusService;
incidentService: IncidentService;
notificationsService: { handleNotifications: jest.Mock };
networkService: { requestStatus: jest.Mock };
providerRegistry: { probe: jest.Mock };
bufferStub: { addToBuffer: jest.Mock; addGeoCheckToBuffer: jest.Mock; scheduleNextFlush: jest.Mock };
maintenanceWindowsRepo: { findByMonitorId: jest.Mock };
messageBuilder: { buildThresholdBreachMessage: jest.Mock };
@@ -87,8 +87,8 @@ export function createHeartbeatTestHarness(): HeartbeatTestHarness {
let nextResponse: MonitorStatusResponse | null = null;
let nextStatus = true;
let nextCode = 200;
const networkService = {
requestStatus: jest.fn().mockImplementation(() => {
const providerRegistry = {
probe: jest.fn().mockImplementation(() => {
if (nextResponse) {
return Promise.resolve(nextResponse);
}
@@ -137,7 +137,7 @@ export function createHeartbeatTestHarness(): HeartbeatTestHarness {
checksRepository: { findUnevaluatedByMonitorId: jest.fn() } as any,
jobsRepository: { upsertEvaluate: jest.fn(), pullEvaluated: jest.fn() } as any,
checkService: checkService as any,
networkService: networkService as any,
providerRegistry: providerRegistry as any,
proxyResolver: proxyResolver as any,
bufferService: bufferStub as any,
dockerLogsService: dockerLogsService as any,
@@ -163,7 +163,7 @@ export function createHeartbeatTestHarness(): HeartbeatTestHarness {
statusService,
incidentService,
notificationsService,
networkService,
providerRegistry,
bufferStub,
maintenanceWindowsRepo,
messageBuilder,
@@ -1,5 +1,5 @@
import { describe, expect, it } from "@jest/globals";
import type { IStatusProvider } from "../../src/service/network/IStatusProvider.ts";
import type { IStatusProvider } from "../../src/service/networkProviders/IStatusProvider.ts";
import type { Monitor } from "../../src/domain/monitors/monitor.type.ts";
/**
@@ -126,7 +126,7 @@ describe("Evaluate cursor with more than one processing worker", () => {
checksRepository,
jobsRepository,
checkService,
networkService: { requestStatus: jest.fn<any>() } as any,
providerRegistry: { probe: jest.fn<any>() } as any,
proxyResolver: { resolve: jest.fn<any>() } as any,
bufferService,
dockerLogsService: { buildDockerLogs: jest.fn<any>() } as any,
@@ -44,7 +44,7 @@ describe("Heartbeat job: maintenance windows", () => {
expect(storedMonitor.status).toBe("maintenance");
// No network request should have been made
expect(h.networkService.requestStatus).not.toHaveBeenCalled();
expect(h.providerRegistry.probe).not.toHaveBeenCalled();
// No check should have been buffered
expect(h.bufferStub.addToBuffer).not.toHaveBeenCalled();
@@ -68,7 +68,7 @@ describe("Heartbeat job: maintenance windows", () => {
// since status was already "maintenance"
const storedMonitor = await h.monitorsRepo.findById("mon-1", "team-1");
expect(storedMonitor.status).toBe("maintenance");
expect(h.networkService.requestStatus).not.toHaveBeenCalled();
expect(h.providerRegistry.probe).not.toHaveBeenCalled();
});
it("resumes normal checks when maintenance window expires", async () => {
@@ -88,7 +88,7 @@ describe("Heartbeat job: maintenance windows", () => {
await h.heartbeatJob(monitor);
// Network request should have been made this time
expect(h.networkService.requestStatus).toHaveBeenCalledTimes(1);
expect(h.providerRegistry.probe).toHaveBeenCalledTimes(1);
// Check should have been buffered
expect(h.bufferStub.addToBuffer).toHaveBeenCalledTimes(1);
@@ -105,7 +105,7 @@ describe("Heartbeat job: maintenance windows", () => {
await h.heartbeatJob(monitor);
// Normal check should proceed since window is inactive
expect(h.networkService.requestStatus).toHaveBeenCalledTimes(1);
expect(h.providerRegistry.probe).toHaveBeenCalledTimes(1);
expect(h.bufferStub.addToBuffer).toHaveBeenCalledTimes(1);
});
@@ -123,7 +123,7 @@ describe("Heartbeat job: maintenance windows", () => {
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();
expect(h.providerRegistry.probe).not.toHaveBeenCalled();
});
it("skips checks when window is in the past (expired)", async () => {
@@ -141,6 +141,6 @@ describe("Heartbeat job: maintenance windows", () => {
await h.heartbeatJob(monitor);
// Window is in the past — normal check should proceed
expect(h.networkService.requestStatus).toHaveBeenCalledTimes(1);
expect(h.providerRegistry.probe).toHaveBeenCalledTimes(1);
});
});
@@ -1,5 +1,5 @@
import { describe, expect, it, jest } from "@jest/globals";
import { AdvancedMatcher } from "../../../../src/service/network/AdvancedMatcher.ts";
import { AdvancedMatcher } from "../../../../src/service/networkProviders/AdvancedMatcher.ts";
import type { Monitor } from "../../../../src/domain/monitors/monitor.type.ts";
// ── Helpers ──────────────────────────────────────────────────────────────────
@@ -2,7 +2,7 @@ import { beforeEach, describe, expect, it, jest } from "@jest/globals";
import { testStatusProviderContract } from "../../../helpers/statusProviderContract.ts";
import { NETWORK_ERROR } from "../../../../src/types/network.ts";
import type { Monitor } from "../../../../src/domain/monitors/monitor.type.ts";
import { DNSProvider } from "../../../../src/service/network/DNSProvider.ts";
import { DNSProvider } from "../../../../src/service/networkProviders/DNSProvider.ts";
// ── Mocks ────────────────────────────────────────────────────────────────────
@@ -1,6 +1,6 @@
import { describe, expect, it, jest } from "@jest/globals";
import https from "node:https";
import { DockerProvider } from "../../../../src/service/network/DockerProvider.ts";
import { DockerProvider } from "../../../../src/service/networkProviders/DockerProvider.ts";
import type { IEncryptionService } from "../../../../src/service/encryption/encryptionService.ts";
import { testStatusProviderContract } from "../../../helpers/statusProviderContract.ts";
import { createMockLogger } from "../../../helpers/createMockLogger.ts";
@@ -1,5 +1,5 @@
import { describe, expect, it, jest } from "@jest/globals";
import { GameProvider } from "../../../../src/service/network/GameProvider.ts";
import { GameProvider } from "../../../../src/service/networkProviders/GameProvider.ts";
import { testStatusProviderContract } from "../../../helpers/statusProviderContract.ts";
import { createMockLogger } from "../../../helpers/createMockLogger.ts";
import { NETWORK_ERROR } from "../../../../src/types/network.ts";
@@ -1,5 +1,5 @@
import { describe, expect, it, jest } from "@jest/globals";
import { GrpcProvider } from "../../../../src/service/network/GrpcProvider.ts";
import { GrpcProvider } from "../../../../src/service/networkProviders/GrpcProvider.ts";
import { testStatusProviderContract } from "../../../helpers/statusProviderContract.ts";
import type { Monitor } from "../../../../src/domain/monitors/monitor.type.ts";
@@ -1,7 +1,7 @@
import { describe, expect, it, jest } from "@jest/globals";
import { HardwareProvider } from "../../../../src/service/network/HardwareProvider.ts";
import { HardwareProvider } from "../../../../src/service/networkProviders/HardwareProvider.ts";
import { testStatusProviderContract } from "../../../helpers/statusProviderContract.ts";
import type { HttpProvider } from "../../../../src/service/network/HttpProvider.ts";
import type { HttpProvider } from "../../../../src/service/networkProviders/HttpProvider.ts";
import type { Monitor } from "../../../../src/domain/monitors/monitor.type.ts";
// ── Helpers ──────────────────────────────────────────────────────────────────
@@ -39,7 +39,7 @@ jest.unstable_mockModule("got", () => ({
},
}));
const { HttpProvider } = await import("../../../../src/service/network/HttpProvider.ts");
const { HttpProvider } = await import("../../../../src/service/networkProviders/HttpProvider.ts");
const gotModule = await import("got");
const { HTTPError, RequestError } = gotModule;
@@ -1,8 +1,8 @@
import { describe, expect, it, jest } from "@jest/globals";
import { PageSpeedProvider } from "../../../../src/service/network/PageSpeedProvider.ts";
import { PageSpeedProvider } from "../../../../src/service/networkProviders/PageSpeedProvider.ts";
import { testStatusProviderContract } from "../../../helpers/statusProviderContract.ts";
import { createMockLogger } from "../../../helpers/createMockLogger.ts";
import type { HttpProvider } from "../../../../src/service/network/HttpProvider.ts";
import type { HttpProvider } from "../../../../src/service/networkProviders/HttpProvider.ts";
import type { ISettingsService } from "../../../../src/domain/app-settings/app-settings.service.ts";
import type { Monitor } from "../../../../src/domain/monitors/monitor.type.ts";
@@ -1,5 +1,5 @@
import { describe, expect, it, jest } from "@jest/globals";
import { PingProvider } from "../../../../src/service/network/PingProvider.ts";
import { PingProvider } from "../../../../src/service/networkProviders/PingProvider.ts";
import { testStatusProviderContract } from "../../../helpers/statusProviderContract.ts";
import type { Monitor } from "../../../../src/domain/monitors/monitor.type.ts";
import * as net from "net";
@@ -1,5 +1,5 @@
import { describe, expect, it, jest } from "@jest/globals";
import { PortProvider } from "../../../../src/service/network/PortProvider.ts";
import { PortProvider } from "../../../../src/service/networkProviders/PortProvider.ts";
import { testStatusProviderContract } from "../../../helpers/statusProviderContract.ts";
import { NETWORK_ERROR } from "../../../../src/types/network.ts";
import type { Monitor } from "../../../../src/domain/monitors/monitor.type.ts";
@@ -1,5 +1,5 @@
import { describe, expect, it } from "@jest/globals";
import { timeRequest, isStatusUp, peerAnsweredFromError } from "../../../../src/service/network/utils.ts";
import { timeRequest, isStatusUp, peerAnsweredFromError } from "../../../../src/service/networkProviders/utils.ts";
import { NETWORK_ERROR } from "../../../../src/types/network.ts";
describe("network utils", () => {
@@ -1,5 +1,5 @@
import { describe, expect, it, jest, beforeEach, afterEach } from "@jest/globals";
import { WebSocketProvider } from "../../../../src/service/network/WebSocketProvider.ts";
import { WebSocketProvider } from "../../../../src/service/networkProviders/WebSocketProvider.ts";
import { testStatusProviderContract } from "../../../helpers/statusProviderContract.ts";
import { NETWORK_ERROR } from "../../../../src/types/network.ts";
import type { Monitor } from "../../../../src/domain/monitors/monitor.type.ts";
+29 -29
View File
@@ -42,7 +42,7 @@ const makeJob = (overrides?: Partial<Job>): Job => ({
...overrides,
});
// requestStatus mock that answers per target url
// probe mock that answers per target url
const statusFor = (reachableTargets: string[]) =>
jest.fn().mockImplementation(async (monitor: any) => ({
monitorId: monitor.id,
@@ -78,7 +78,7 @@ const createService = (overrides?: Record<string, any>) => {
upsertCleanupJob: jest.fn().mockResolvedValue(true),
deleteGlobalJobIfUnchanged: jest.fn().mockResolvedValue(true),
},
networkService: { requestStatus: statusFor(["1.1.1.1", "8.8.8.8"]) },
providerRegistry: { probe: statusFor(["1.1.1.1", "8.8.8.8"]) },
proxyResolver: { resolve: jest.fn().mockResolvedValue(undefined) },
logger: createMockLogger(),
...overrides,
@@ -87,15 +87,15 @@ const createService = (overrides?: Record<string, any>) => {
defaults.settingsService as any,
defaults.egressStateRepository as any,
defaults.jobsRepository as any,
defaults.networkService as any,
defaults.providerRegistry as any,
defaults.proxyResolver as any,
defaults.logger as any
);
return { service, defaults };
};
const probedUrls = (defaults: { networkService: { requestStatus: unknown } }) =>
(defaults.networkService.requestStatus as jest.Mock).mock.calls.map((call) => (call[0] as any).url);
const probedUrls = (defaults: { providerRegistry: { probe: unknown } }) =>
(defaults.providerRegistry.probe as jest.Mock).mock.calls.map((call) => (call[0] as any).url);
// ── Tests ────────────────────────────────────────────────────────────────────
@@ -112,7 +112,7 @@ describe("EgressService", () => {
await service.probeTargets(["1.1.1.1", "8.8.8.8:53", "https://example.com/health", "[2606:4700::1111]:443", "2606:4700::1111"]);
const monitors = (defaults.networkService.requestStatus as jest.Mock).mock.calls.map((call) => call[0] as any);
const monitors = (defaults.providerRegistry.probe as jest.Mock).mock.calls.map((call) => call[0] as any);
expect(monitors).toEqual([
expect.objectContaining({ type: "ping", url: "1.1.1.1" }),
expect.objectContaining({ type: "port", url: "8.8.8.8", port: 53 }),
@@ -129,13 +129,13 @@ describe("EgressService", () => {
await service.probeTargets(["https://example.com/health"]);
expect(resolve).toHaveBeenCalledWith(expect.objectContaining({ type: "http", proxyMode: "inherit" }));
expect(defaults.networkService.requestStatus).toHaveBeenCalledWith(expect.objectContaining({ type: "http" }), {
expect(defaults.providerRegistry.probe).toHaveBeenCalledWith(expect.objectContaining({ type: "http" }), {
proxyUrl: "http://proxy.internal:3128",
});
});
it("reports reachable only when the provider says status is true", async () => {
const { service } = createService({ networkService: { requestStatus: statusFor(["1.1.1.1"]) } });
const { service } = createService({ providerRegistry: { probe: statusFor(["1.1.1.1"]) } });
const results = await service.probeTargets(["1.1.1.1", "8.8.8.8"]);
@@ -146,7 +146,7 @@ describe("EgressService", () => {
});
it("treats any HTTP response, even 4xx/5xx, as reachable but a transport failure as unreachable", async () => {
const requestStatus = jest.fn().mockImplementation(async (monitor: any) => ({
const probe = jest.fn().mockImplementation(async (monitor: any) => ({
monitorId: monitor.id,
teamId: monitor.teamId,
type: monitor.type,
@@ -155,7 +155,7 @@ describe("EgressService", () => {
message: monitor.url.includes("maintenance") ? "Service Unavailable" : "ECONNREFUSED",
responseTime: 3,
}));
const { service } = createService({ networkService: { requestStatus } });
const { service } = createService({ providerRegistry: { probe } });
const results = await service.probeTargets(["https://example.com/maintenance", "https://example.com/refused", "8.8.8.8:53"]);
@@ -172,11 +172,11 @@ describe("EgressService", () => {
const results = await service.probeTargets(["[1.1.1.1"]);
expect(results).toEqual([expect.objectContaining({ target: "[1.1.1.1", reachable: false, message: "Invalid egress target" })]);
expect(defaults.networkService.requestStatus).not.toHaveBeenCalled();
expect(defaults.providerRegistry.probe).not.toHaveBeenCalled();
});
it("counts a thrown provider error as unreachable", async () => {
const { service } = createService({ networkService: { requestStatus: jest.fn().mockRejectedValue(new Error("ENETUNREACH")) } });
const { service } = createService({ providerRegistry: { probe: jest.fn().mockRejectedValue(new Error("ENETUNREACH")) } });
const results = await service.probeTargets(["1.1.1.1"]);
@@ -185,7 +185,7 @@ describe("EgressService", () => {
it("counts a hung provider as unreachable once the probe timeout elapses", async () => {
jest.useFakeTimers();
const { service } = createService({ networkService: { requestStatus: jest.fn().mockReturnValue(new Promise(() => {})) } });
const { service } = createService({ providerRegistry: { probe: jest.fn().mockReturnValue(new Promise(() => {})) } });
const pending = service.probeTargets(["1.1.1.1"]);
await jest.advanceTimersByTimeAsync(5000);
@@ -206,13 +206,13 @@ describe("EgressService", () => {
const result = await service.assessAfterFailure();
expect(result).toBeNull();
expect(defaults.networkService.requestStatus).not.toHaveBeenCalled();
expect(defaults.providerRegistry.probe).not.toHaveBeenCalled();
expect(defaults.egressStateRepository.findSingleton).not.toHaveBeenCalled();
expect(defaults.egressStateRepository.recordProbe).not.toHaveBeenCalled();
});
it("returns ok and records the probe when any target is reachable", async () => {
const { service, defaults } = createService({ networkService: { requestStatus: statusFor(["8.8.8.8"]) } });
const { service, defaults } = createService({ providerRegistry: { probe: statusFor(["8.8.8.8"]) } });
const result = await service.assessAfterFailure();
@@ -227,7 +227,7 @@ describe("EgressService", () => {
});
it("marks degraded, warns, schedules the recovery job and returns degraded when every target is unreachable", async () => {
const { service, defaults } = createService({ networkService: { requestStatus: statusFor([]) } });
const { service, defaults } = createService({ providerRegistry: { probe: statusFor([]) } });
const result = await service.assessAfterFailure();
@@ -255,7 +255,7 @@ describe("EgressService", () => {
it("still returns degraded and leaves the job scheduled when another worker performed the transition first", async () => {
const { service, defaults } = createService({
networkService: { requestStatus: statusFor([]) },
providerRegistry: { probe: statusFor([]) },
egressStateRepository: degradedRepository({
findSingleton: jest.fn().mockResolvedValue(makeState()),
markDegraded: jest.fn().mockResolvedValue(null),
@@ -271,7 +271,7 @@ describe("EgressService", () => {
it("does not mark degraded when the recovery job cannot be scheduled", async () => {
const { service, defaults } = createService({
networkService: { requestStatus: statusFor([]) },
providerRegistry: { probe: statusFor([]) },
jobsRepository: {
upsertJob: jest.fn(),
upsertCleanupJob: jest.fn().mockRejectedValue(new Error("db down")),
@@ -289,7 +289,7 @@ describe("EgressService", () => {
const result = await service.assessAfterFailure();
expect(result).toBe("degraded");
expect(defaults.networkService.requestStatus).not.toHaveBeenCalled();
expect(defaults.providerRegistry.probe).not.toHaveBeenCalled();
expect(defaults.egressStateRepository.markDegraded).not.toHaveBeenCalled();
expect(defaults.egressStateRepository.recordProbe).not.toHaveBeenCalled();
expect(defaults.jobsRepository.upsertJob).toHaveBeenCalledWith(expect.objectContaining({ id: "egress", intervalMs: 5_000 }));
@@ -322,19 +322,19 @@ describe("EgressService", () => {
describe("shared assessment", () => {
it("shares one probe between concurrent failures", async () => {
const { service, defaults } = createService({ networkService: { requestStatus: statusFor(["8.8.8.8"]) } });
const { service, defaults } = createService({ providerRegistry: { probe: statusFor(["8.8.8.8"]) } });
const results = await Promise.all([service.assessAfterFailure(), service.assessAfterFailure(), service.assessAfterFailure()]);
expect(results).toEqual(["ok", "ok", "ok"]);
expect(defaults.settingsService.getCachedDBSettings).toHaveBeenCalledTimes(1);
expect(defaults.networkService.requestStatus).toHaveBeenCalledTimes(2); // one call per target, once
expect(defaults.providerRegistry.probe).toHaveBeenCalledTimes(2); // one call per target, once
expect(defaults.egressStateRepository.recordProbe).toHaveBeenCalledTimes(1);
});
it("keeps sharing an in-flight assessment that outlives the reuse window", async () => {
jest.useFakeTimers();
const { service, defaults } = createService({ networkService: { requestStatus: jest.fn().mockReturnValue(new Promise(() => {})) } });
const { service, defaults } = createService({ providerRegistry: { probe: jest.fn().mockReturnValue(new Promise(() => {})) } });
const first = service.assessAfterFailure();
await jest.advanceTimersByTimeAsync(4_900); // probes still hanging, past the 5 s reuse window from the first call
@@ -342,12 +342,12 @@ describe("EgressService", () => {
await jest.advanceTimersByTimeAsync(200); // probe timeout fires, both settle
expect(await Promise.all([first, second])).toEqual(["degraded", "degraded"]);
expect(defaults.networkService.requestStatus).toHaveBeenCalledTimes(2); // one round of two targets, not two rounds
expect(defaults.providerRegistry.probe).toHaveBeenCalledTimes(2); // one round of two targets, not two rounds
});
it("reuses a settled assessment within the reuse window and probes again after it", async () => {
jest.useFakeTimers();
const { service, defaults } = createService({ networkService: { requestStatus: statusFor(["8.8.8.8"]) } });
const { service, defaults } = createService({ providerRegistry: { probe: statusFor(["8.8.8.8"]) } });
await service.assessAfterFailure();
await jest.advanceTimersByTimeAsync(1_000);
@@ -385,7 +385,7 @@ describe("EgressService", () => {
it("marks recovered, releases the job and logs when a target becomes reachable", async () => {
const { service, defaults } = createService({
egressStateRepository: degradedRepository(),
networkService: { requestStatus: statusFor(["1.1.1.1"]) },
providerRegistry: { probe: statusFor(["1.1.1.1"]) },
});
const job = makeJob({ nextScheduledAt: 123_456 });
@@ -403,7 +403,7 @@ describe("EgressService", () => {
it("releases the job but does not log a recovery when another process already recorded it", async () => {
const { service, defaults } = createService({
egressStateRepository: degradedRepository({ markRecovered: jest.fn().mockResolvedValue(null) }),
networkService: { requestStatus: statusFor(["1.1.1.1"]) },
providerRegistry: { probe: statusFor(["1.1.1.1"]) },
});
await service.checkRecovery(makeJob());
@@ -417,7 +417,7 @@ describe("EgressService", () => {
it("records the probe and keeps the job while every target stays unreachable", async () => {
const { service, defaults } = createService({
egressStateRepository: degradedRepository(),
networkService: { requestStatus: statusFor([]) },
providerRegistry: { probe: statusFor([]) },
});
await service.checkRecovery(makeJob());
@@ -432,7 +432,7 @@ describe("EgressService", () => {
await service.checkRecovery(makeJob());
expect(defaults.networkService.requestStatus).not.toHaveBeenCalled();
expect(defaults.providerRegistry.probe).not.toHaveBeenCalled();
expect(defaults.egressStateRepository.markRecovered).not.toHaveBeenCalled();
expect(defaults.jobsRepository.deleteGlobalJobIfUnchanged).toHaveBeenCalledWith("egress", 1_000);
});
@@ -445,7 +445,7 @@ describe("EgressService", () => {
await service.checkRecovery(makeJob());
expect(defaults.networkService.requestStatus).not.toHaveBeenCalled();
expect(defaults.providerRegistry.probe).not.toHaveBeenCalled();
expect(defaults.jobsRepository.deleteGlobalJobIfUnchanged).toHaveBeenCalledWith("egress", 1_000);
});
@@ -1,482 +0,0 @@
import { describe, expect, it, jest } from "@jest/globals";
import { NetworkService } from "../../../src/service/networkService.ts";
import { createMockLogger } from "../../helpers/createMockLogger.ts";
import { NETWORK_ERROR } from "../../../src/types/network.ts";
import type { IStatusProvider } from "../../../src/service/network/IStatusProvider.ts";
import type { Monitor, MonitorType } from "../../../src/domain/monitors/monitor.type.ts";
import type { AxiosStatic } from "axios";
// ── Helpers ──────────────────────────────────────────────────────────────────
const createMockAxios = () =>
({
post: jest.fn(),
}) as unknown as jest.Mocked<AxiosStatic>;
const createMockProvider = (type: string, result?: unknown) => {
const provider: jest.Mocked<IStatusProvider<unknown>> = {
type,
supports: jest.fn((t: MonitorType) => t === type) as any,
handle: jest.fn().mockResolvedValue(
result ?? {
monitorId: "mon-1",
teamId: "team-1",
type,
status: true,
code: 200,
message: "OK",
}
),
};
return provider;
};
const createService = (providers: IStatusProvider<unknown>[] = []) => {
const logger = createMockLogger();
const axios = createMockAxios();
const service = new NetworkService(axios, logger as any, providers);
return { service, logger, axios };
};
// ── Tests ────────────────────────────────────────────────────────────────────
describe("NetworkService", () => {
// ── Static / instance properties ─────────────────────────────────────────
// ── requestStatus ────────────────────────────────────────────────────────
describe("requestStatus", () => {
it("delegates to the matching provider", async () => {
const provider = createMockProvider("http");
const { service } = createService([provider]);
const monitor = { type: "http", _id: "mon-1" } as unknown as Monitor & { type: "http" };
const result = await service.requestStatus(monitor);
expect(provider.supports).toHaveBeenCalledWith("http");
expect(provider.handle).toHaveBeenCalledWith(monitor, undefined);
expect(result).toEqual(
expect.objectContaining({
monitorId: "mon-1",
status: true,
code: 200,
})
);
});
it("selects the correct provider when multiple are registered", async () => {
const httpProvider = createMockProvider("http");
const pingProvider = createMockProvider("ping");
const { service } = createService([httpProvider, pingProvider]);
const monitor = { type: "ping", _id: "mon-2" } as unknown as Monitor & { type: "ping" };
await service.requestStatus(monitor);
expect(httpProvider.handle).not.toHaveBeenCalled();
expect(pingProvider.handle).toHaveBeenCalledWith(monitor, undefined);
});
it("forwards the check context to the provider", async () => {
const provider = createMockProvider("http");
const { service } = createService([provider]);
const monitor = { type: "http", _id: "mon-1" } as unknown as Monitor & { type: "http" };
const ctx = { proxyUrl: "http://proxy.example.com:8080" };
await service.requestStatus(monitor, ctx);
expect(provider.handle).toHaveBeenCalledWith(monitor, ctx);
});
it("returns unsupported-type response when no provider matches", async () => {
const { service } = createService([]);
const monitor = { type: "unknown_type" as any, _id: "mon-1" } as unknown as Monitor & { type: any };
const result = await service.requestStatus(monitor);
expect(result).toEqual({
monitorId: "unknown",
teamId: "unknown",
type: "unknown",
status: false,
code: NETWORK_ERROR,
message: "Unsupported type: unknown_type",
});
});
});
// ── requestWebhook ───────────────────────────────────────────────────────
describe("requestWebhook", () => {
it("sends a POST and returns success response", async () => {
const { service, axios } = createService();
(axios.post as jest.Mock).mockResolvedValue({ status: 200, data: { ok: true } });
const result = await service.requestWebhook("slack", "https://hooks.example.com", { text: "hello" });
expect(axios.post).toHaveBeenCalledWith("https://hooks.example.com", { text: "hello" }, { headers: { "Content-Type": "application/json" } });
expect(result).toEqual({
type: "webhook",
status: true,
code: 200,
message: "Successfully sent slack notification",
payload: { ok: true },
});
});
it("returns failure with response data on axios error with response", async () => {
const { service, axios, logger } = createService();
const axiosError = Object.assign(new Error("Request failed"), {
response: { status: 403, data: { error: "forbidden" } },
});
(axios.post as jest.Mock).mockRejectedValue(axiosError);
const result = await service.requestWebhook("discord", "https://hooks.example.com", {});
expect(result).toEqual({
type: "webhook",
status: false,
code: 403,
message: "Failed to send discord notification",
payload: { error: "forbidden" },
});
expect(logger.warn).toHaveBeenCalledWith(
expect.objectContaining({
message: "Request failed",
service: "NetworkService",
method: "requestWebhook",
})
);
});
it("returns failure with NETWORK_ERROR when axios error has no response status", async () => {
const { service, axios } = createService();
const axiosError = Object.assign(new Error("timeout"), {
response: { status: undefined, data: undefined },
});
(axios.post as jest.Mock).mockRejectedValue(axiosError);
const result = await service.requestWebhook("slack", "https://hooks.example.com", {});
expect(result.code).toBe(NETWORK_ERROR);
});
it("returns failure with NETWORK_ERROR on non-axios error (no response property)", async () => {
const { service, axios } = createService();
(axios.post as jest.Mock).mockRejectedValue(new Error("DNS failure"));
const result = await service.requestWebhook("slack", "https://hooks.example.com", {});
expect(result).toEqual({
type: "webhook",
status: false,
code: NETWORK_ERROR,
message: "Failed to send slack notification",
});
});
it("handles non-Error thrown values", async () => {
const { service, axios, logger } = createService();
(axios.post as jest.Mock).mockRejectedValue("string error");
const result = await service.requestWebhook("slack", "https://hooks.example.com", {});
expect(logger.warn).toHaveBeenCalledWith(expect.objectContaining({ message: "string error" }));
expect(result).toEqual({
type: "webhook",
status: false,
code: NETWORK_ERROR,
message: "Failed to send slack notification",
});
});
it("returns NETWORK_ERROR when error has response key but response is undefined", async () => {
const { service, axios } = createService();
const axiosError = Object.assign(new Error("fail"), { response: undefined });
(axios.post as jest.Mock).mockRejectedValue(axiosError);
const result = await service.requestWebhook("slack", "https://hooks.example.com", {});
expect(result.code).toBe(NETWORK_ERROR);
expect(result.payload).toBeUndefined();
});
});
// ── requestPagerDuty ─────────────────────────────────────────────────────
describe("requestPagerDuty", () => {
const args = {
message: "Server down",
routingKey: "routing-key-123",
monitorUrl: "https://monitor.example.com",
};
it("sends correct payload and returns true on success", async () => {
const { service, axios } = createService();
(axios.post as jest.Mock).mockResolvedValue({ data: { status: "success" } });
const result = await service.requestPagerDuty(args);
expect(result).toBe(true);
expect(axios.post).toHaveBeenCalledWith(
"https://events.pagerduty.com/v2/enqueue",
expect.objectContaining({
routing_key: "routing-key-123",
event_action: "trigger",
payload: expect.objectContaining({
summary: "Server down",
severity: "critical",
source: "https://monitor.example.com",
}),
})
);
});
it("returns false when response status is not success", async () => {
const { service, axios } = createService();
(axios.post as jest.Mock).mockResolvedValue({ data: { status: "invalid event" } });
const result = await service.requestPagerDuty(args);
expect(result).toBe(false);
});
it("returns false when response data is undefined", async () => {
const { service, axios } = createService();
(axios.post as jest.Mock).mockResolvedValue({ data: undefined });
const result = await service.requestPagerDuty(args);
expect(result).toBe(false);
});
it("throws AppError on axios failure with response data", async () => {
const { service, axios } = createService();
const axiosError = Object.assign(new Error("Request failed"), {
response: { data: { message: "invalid routing key" } },
});
(axios.post as jest.Mock).mockRejectedValue(axiosError);
await expect(service.requestPagerDuty(args)).rejects.toThrow("Request failed");
});
it("throws AppError on network error without response", async () => {
const { service, axios } = createService();
(axios.post as jest.Mock).mockRejectedValue(new Error("ECONNREFUSED"));
await expect(service.requestPagerDuty(args)).rejects.toThrow("ECONNREFUSED");
});
it("throws AppError with default message for non-Error thrown value", async () => {
const { service, axios } = createService();
(axios.post as jest.Mock).mockRejectedValue(null);
await expect(service.requestPagerDuty(args)).rejects.toThrow("null");
});
it("uses fallback message when Error has empty string message", async () => {
const { service, axios } = createService();
(axios.post as jest.Mock).mockRejectedValue(new Error(""));
try {
await service.requestPagerDuty(args);
expect.unreachable("should have thrown");
} catch (err: any) {
expect(err.message).toBe("Error sending PagerDuty notification");
}
});
it("includes responseData when non-Error object has response property", async () => {
const { service, axios } = createService();
const errorObj = { response: { data: { detail: "bad key" } } };
(axios.post as jest.Mock).mockRejectedValue(errorObj);
try {
await service.requestPagerDuty(args);
expect.unreachable("should have thrown");
} catch (err: any) {
expect(err.details).toEqual({ responseData: { detail: "bad key" } });
}
});
it("sets responseData to undefined when error is a primitive (non-object)", async () => {
const { service, axios } = createService();
(axios.post as jest.Mock).mockRejectedValue(42);
try {
await service.requestPagerDuty(args);
expect.unreachable("should have thrown");
} catch (err: any) {
expect(err.details).toEqual({ responseData: undefined });
}
});
it("sets responseData to undefined when object has response key set to undefined", async () => {
const { service, axios } = createService();
(axios.post as jest.Mock).mockRejectedValue({ response: undefined });
try {
await service.requestPagerDuty(args);
expect.unreachable("should have thrown");
} catch (err: any) {
expect(err.details).toEqual({ responseData: undefined });
}
});
it("includes responseData in AppError details when available", async () => {
const { service, axios } = createService();
const axiosError = Object.assign(new Error("Bad request"), {
response: { data: { errors: ["invalid key"] } },
});
(axios.post as jest.Mock).mockRejectedValue(axiosError);
try {
await service.requestPagerDuty(args);
expect.unreachable("should have thrown");
} catch (err: any) {
expect(err.details).toEqual({
responseData: { errors: ["invalid key"] },
});
}
});
it("sets responseData to undefined in AppError when error has no response", async () => {
const { service, axios } = createService();
(axios.post as jest.Mock).mockRejectedValue(new Error("timeout"));
try {
await service.requestPagerDuty(args);
expect.unreachable("should have thrown");
} catch (err: any) {
expect(err.details).toEqual({ responseData: undefined });
}
});
});
// ── requestMatrix ────────────────────────────────────────────────────────
describe("requestMatrix", () => {
const args = {
homeserverUrl: "https://matrix.example.com",
accessToken: "token-abc",
roomId: "!room:example.com",
message: "<b>Alert</b>",
};
it("sends correct payload and returns success response", async () => {
const { service, axios } = createService();
(axios.post as jest.Mock).mockResolvedValue({ status: 200, data: { event_id: "$abc" } });
const result = await service.requestMatrix(args);
expect(result).toEqual({
status: true,
code: 200,
message: "Successfully sent Matrix notification",
});
expect(axios.post).toHaveBeenCalledWith(
"https://matrix.example.com/_matrix/client/v3/rooms/!room:example.com/send/m.room.message?access_token=token-abc",
{
msgtype: "m.text",
body: "<b>Alert</b>",
format: "org.matrix.custom.html",
formatted_body: "<b>Alert</b>",
},
{ headers: { "Content-Type": "application/json" } }
);
});
it("returns failure with response data on axios error with response (Error instance)", async () => {
const { service, axios, logger } = createService();
const axiosError = Object.assign(new Error("Forbidden"), {
response: { status: 403, data: { errcode: "M_FORBIDDEN" } },
});
(axios.post as jest.Mock).mockRejectedValue(axiosError);
const result = await service.requestMatrix(args);
expect(result).toEqual({
status: false,
code: 403,
message: "Failed to send Matrix notification",
payload: { errcode: "M_FORBIDDEN" },
});
expect(logger.warn).toHaveBeenCalledWith(
expect.objectContaining({
message: "Forbidden",
service: "NetworkService",
method: "requestMatrix",
})
);
});
it("returns NETWORK_ERROR when axios error response has no status", async () => {
const { service, axios } = createService();
const axiosError = Object.assign(new Error("timeout"), {
response: { status: 0, data: undefined },
});
(axios.post as jest.Mock).mockRejectedValue(axiosError);
const result = await service.requestMatrix(args);
expect(result.code).toBe(NETWORK_ERROR);
});
it("returns failure with NETWORK_ERROR on Error without response property", async () => {
const { service, axios, logger } = createService();
(axios.post as jest.Mock).mockRejectedValue(new Error("DNS failure"));
const result = await service.requestMatrix(args);
expect(result).toEqual({
status: false,
code: NETWORK_ERROR,
message: "Failed to send Matrix notification",
});
// First warn call is from the Error instanceof branch
expect(logger.warn).toHaveBeenCalledTimes(2);
});
it("returns failure with NETWORK_ERROR on non-Error thrown value", async () => {
const { service, axios, logger } = createService();
(axios.post as jest.Mock).mockRejectedValue("string error");
const result = await service.requestMatrix(args);
expect(result).toEqual({
status: false,
code: NETWORK_ERROR,
message: "Failed to send Matrix notification",
});
expect(logger.warn).toHaveBeenCalledWith(
expect.objectContaining({
message: "string error",
service: "NetworkService",
method: "requestMatrix",
})
);
});
it("returns NETWORK_ERROR when Error has response key but response is undefined", async () => {
const { service, axios } = createService();
const axiosError = Object.assign(new Error("fail"), { response: undefined });
(axios.post as jest.Mock).mockRejectedValue(axiosError);
const result = await service.requestMatrix(args);
expect(result.code).toBe(NETWORK_ERROR);
});
it("returns NETWORK_ERROR when Error response has undefined status", async () => {
const { service, axios } = createService();
const axiosError = Object.assign(new Error("fail"), {
response: { status: undefined, data: { error: "unknown" } },
});
(axios.post as jest.Mock).mockRejectedValue(axiosError);
const result = await service.requestMatrix(args);
expect(result.code).toBe(NETWORK_ERROR);
expect(result.payload).toEqual({ error: "unknown" });
});
});
});
@@ -0,0 +1,90 @@
import { describe, expect, it, jest } from "@jest/globals";
import { ProviderRegistry } from "../../../src/service/networkProviders/providerRegistry.ts";
import { NETWORK_ERROR } from "../../../src/types/network.ts";
import type { IStatusProvider } from "../../../src/service/networkProviders/IStatusProvider.ts";
import type { Monitor, MonitorType } from "../../../src/domain/monitors/monitor.type.ts";
// ── Helpers ──────────────────────────────────────────────────────────────────
const createMockProvider = (type: string, result?: unknown) => {
const provider: jest.Mocked<IStatusProvider<unknown>> = {
type,
supports: jest.fn((t: MonitorType) => t === type) as any,
handle: jest.fn().mockResolvedValue(
result ?? {
monitorId: "mon-1",
teamId: "team-1",
type,
status: true,
code: 200,
message: "OK",
}
),
};
return provider;
};
const createRegistry = (providers: IStatusProvider<unknown>[] = []) => new ProviderRegistry(providers);
// ── Tests ────────────────────────────────────────────────────────────────────
describe("ProviderRegistry", () => {
describe("probe", () => {
it("delegates to the matching provider", async () => {
const provider = createMockProvider("http");
const registry = createRegistry([provider]);
const monitor = { type: "http", _id: "mon-1" } as unknown as Monitor & { type: "http" };
const result = await registry.probe(monitor);
expect(provider.supports).toHaveBeenCalledWith("http");
expect(provider.handle).toHaveBeenCalledWith(monitor, undefined);
expect(result).toEqual(
expect.objectContaining({
monitorId: "mon-1",
status: true,
code: 200,
})
);
});
it("selects the correct provider when multiple are registered", async () => {
const httpProvider = createMockProvider("http");
const pingProvider = createMockProvider("ping");
const registry = createRegistry([httpProvider, pingProvider]);
const monitor = { type: "ping", _id: "mon-2" } as unknown as Monitor & { type: "ping" };
await registry.probe(monitor);
expect(httpProvider.handle).not.toHaveBeenCalled();
expect(pingProvider.handle).toHaveBeenCalledWith(monitor, undefined);
});
it("forwards the check context to the provider", async () => {
const provider = createMockProvider("http");
const registry = createRegistry([provider]);
const monitor = { type: "http", _id: "mon-1" } as unknown as Monitor & { type: "http" };
const ctx = { proxyUrl: "http://proxy.example.com:8080" };
await registry.probe(monitor, ctx);
expect(provider.handle).toHaveBeenCalledWith(monitor, ctx);
});
it("returns unsupported-type response when no provider matches", async () => {
const registry = createRegistry([]);
const monitor = { type: "unknown_type" as any, _id: "mon-1" } as unknown as Monitor & { type: any };
const result = await registry.probe(monitor);
expect(result).toEqual({
monitorId: "unknown",
teamId: "unknown",
type: "unknown",
status: false,
code: NETWORK_ERROR,
message: "Unsupported type: unknown_type",
});
});
});
});
@@ -1,5 +1,5 @@
import { describe, expect, it, jest } from "@jest/globals";
import { ProxyResolver, buildProxyUrl } from "../../../src/service/network/ProxyResolver.ts";
import { ProxyResolver, buildProxyUrl } from "../../../src/service/networkProviders/ProxyResolver.ts";
import type { Monitor } from "../../../src/domain/monitors/monitor.type.ts";
import type { Proxy } from "../../../src/domain/proxies/proxy.type.ts";
import { createMockLogger } from "../../helpers/createMockLogger.ts";
@@ -92,7 +92,7 @@ const createPipeline = (overrides?: Record<string, any>) => {
toCheck: jest.fn<any>().mockReturnValue({ id: "check-1" }),
createChecks: jest.fn<any>().mockResolvedValue([]),
},
networkService: { requestStatus: jest.fn<any>().mockResolvedValue({ monitorId: "m1", status: true, code: 200, message: "OK" }) },
providerRegistry: { probe: jest.fn<any>().mockResolvedValue({ monitorId: "m1", status: true, code: 200, message: "OK" }) },
proxyResolver: { resolve: jest.fn<any>().mockResolvedValue(undefined) },
bufferService: { addToBuffer: jest.fn<any>(), addDockerLogToBuffer: jest.fn<any>() },
dockerLogsService: { buildDockerLogs: jest.fn<any>().mockResolvedValue([]) },
@@ -115,7 +115,7 @@ describe("WorkerPipeline", () => {
await pipeline.handleCheck(makeJob());
expect(deps.monitorsRepository.findByIdLean).toHaveBeenCalledWith("m1");
expect(deps.networkService.requestStatus).toHaveBeenCalled();
expect(deps.providerRegistry.probe).toHaveBeenCalled();
expect(deps.bufferService.addToBuffer).toHaveBeenCalledWith({ id: "check-1" });
});
@@ -125,7 +125,7 @@ describe("WorkerPipeline", () => {
await pipeline.handleCheck(makeJob({ refId: null }));
expect(deps.monitorsRepository.findByIdLean).not.toHaveBeenCalled();
expect(deps.networkService.requestStatus).not.toHaveBeenCalled();
expect(deps.providerRegistry.probe).not.toHaveBeenCalled();
});
it("does nothing when the monitor no longer exists", async () => {
@@ -135,7 +135,7 @@ describe("WorkerPipeline", () => {
await pipeline.handleCheck(makeJob());
expect(deps.networkService.requestStatus).not.toHaveBeenCalled();
expect(deps.providerRegistry.probe).not.toHaveBeenCalled();
});
});
@@ -156,7 +156,7 @@ describe("WorkerPipeline", () => {
expect(result).toBeNull();
expect(deps.monitorsRepository.updateById).toHaveBeenCalledWith("m1", "team", { status: "maintenance", statusWindow: [] });
expect(deps.networkService.requestStatus).not.toHaveBeenCalled();
expect(deps.providerRegistry.probe).not.toHaveBeenCalled();
expect(deps.bufferService.addToBuffer).not.toHaveBeenCalled();
});
@@ -185,7 +185,7 @@ describe("WorkerPipeline", () => {
// The exit used to be a side effect of evaluating the resulting check, which the degraded-egress
// short-circuit skips. Left implicit, a monitor could sit in "maintenance" for a whole egress outage.
expect(deps.monitorsRepository.updateById).toHaveBeenCalledWith("m1", "team", { status: "initializing" });
expect(deps.networkService.requestStatus).toHaveBeenCalled();
expect(deps.providerRegistry.probe).toHaveBeenCalled();
});
it("does not touch the status of a monitor that was not in maintenance", async () => {
@@ -203,12 +203,12 @@ describe("WorkerPipeline", () => {
await pipeline.produce(makeMonitor());
expect(deps.networkService.requestStatus).toHaveBeenCalled();
expect(deps.providerRegistry.probe).toHaveBeenCalled();
});
// ── proxy resolution ──────────────────────────────────────────────────
it("passes the resolved proxy url into requestStatus", async () => {
it("passes the resolved proxy url into probe", async () => {
const { pipeline, deps } = createPipeline({
proxyResolver: { resolve: jest.fn<any>().mockResolvedValue("http://proxy.example.com:8080") },
});
@@ -216,7 +216,7 @@ describe("WorkerPipeline", () => {
await pipeline.produce(monitor);
expect(deps.networkService.requestStatus).toHaveBeenCalledWith(monitor, { proxyUrl: "http://proxy.example.com:8080" });
expect(deps.providerRegistry.probe).toHaveBeenCalledWith(monitor, { proxyUrl: "http://proxy.example.com:8080" });
});
it("still produces a check when the resolver returns undefined", async () => {
@@ -225,7 +225,7 @@ describe("WorkerPipeline", () => {
const result = await pipeline.produce(monitor);
expect(deps.networkService.requestStatus).toHaveBeenCalledWith(monitor, { proxyUrl: undefined });
expect(deps.providerRegistry.probe).toHaveBeenCalledWith(monitor, { proxyUrl: undefined });
expect(result).toEqual({ status: expect.objectContaining({ monitorId: "m1" }), check: { id: "check-1" } });
});
@@ -240,7 +240,7 @@ describe("WorkerPipeline", () => {
await pipeline.produce(monitor);
expect(deps.monitorsRepository.findDockerTlsKeyById).toHaveBeenCalledWith("m1");
expect(deps.networkService.requestStatus).toHaveBeenCalledWith(monitor, { proxyUrl: undefined, dockerTlsKey: "v1.abc123.iv.tag.data" });
expect(deps.providerRegistry.probe).toHaveBeenCalledWith(monitor, { proxyUrl: undefined, dockerTlsKey: "v1.abc123.iv.tag.data" });
});
it("does not fetch a key for a docker monitor without one set", async () => {
@@ -250,7 +250,7 @@ describe("WorkerPipeline", () => {
await pipeline.produce(monitor);
expect(deps.monitorsRepository.findDockerTlsKeyById).not.toHaveBeenCalled();
expect(deps.networkService.requestStatus).toHaveBeenCalledWith(monitor, { proxyUrl: undefined, dockerTlsKey: undefined });
expect(deps.providerRegistry.probe).toHaveBeenCalledWith(monitor, { proxyUrl: undefined, dockerTlsKey: undefined });
});
it("does not fetch a key for non-docker monitors even if the flag is set", async () => {
@@ -268,14 +268,14 @@ describe("WorkerPipeline", () => {
await pipeline.produce(monitor);
expect(deps.monitorsRepository.findDockerTlsKeyById).toHaveBeenCalledWith("m1");
expect(deps.networkService.requestStatus).toHaveBeenCalledWith(monitor, { proxyUrl: undefined, dockerTlsKey: undefined });
expect(deps.providerRegistry.probe).toHaveBeenCalledWith(monitor, { proxyUrl: undefined, dockerTlsKey: undefined });
});
// ── acquire / record ──────────────────────────────────────────────────
it("throws when the network response is null", async () => {
const { pipeline } = createPipeline({
networkService: { requestStatus: jest.fn<any>().mockResolvedValue(null) },
providerRegistry: { probe: jest.fn<any>().mockResolvedValue(null) },
});
await expect(pipeline.produce(makeMonitor())).rejects.toThrow("No network response");
@@ -297,7 +297,7 @@ describe("WorkerPipeline", () => {
const status = { monitorId: "m1", status: false, code: 500, message: "Error" };
const check = { id: "check-1" };
const { pipeline, deps } = createPipeline({
networkService: { requestStatus: jest.fn<any>().mockResolvedValue(status) },
providerRegistry: { probe: jest.fn<any>().mockResolvedValue(status) },
checkService: { toCheck: jest.fn<any>().mockReturnValue(check) },
});
@@ -311,7 +311,7 @@ describe("WorkerPipeline", () => {
const status = { type: "docker", monitorId: "m1", teamId: "team", status: true, code: 200, message: "OK", payload: {} };
const dockerLogs = [{ id: "docker-log-1" }, { id: "docker-log-2" }];
const { pipeline, deps } = createPipeline({
networkService: { requestStatus: jest.fn<any>().mockResolvedValue(status) },
providerRegistry: { probe: jest.fn<any>().mockResolvedValue(status) },
dockerLogsService: { buildDockerLogs: jest.fn<any>().mockResolvedValue(dockerLogs) },
});
@@ -354,7 +354,7 @@ describe("WorkerPipeline", () => {
]) {
const check: Record<string, unknown> = { id: "check-1" };
const { pipeline, deps } = createPipeline({
networkService: { requestStatus: jest.fn<any>().mockResolvedValue(status) },
providerRegistry: { probe: jest.fn<any>().mockResolvedValue(status) },
checkService: { toCheck: jest.fn<any>().mockReturnValue(check) },
egressService: { assessAfterFailure: jest.fn<any>().mockResolvedValue("degraded") },
});
@@ -370,7 +370,7 @@ describe("WorkerPipeline", () => {
it("consults egress for a timeout or network error", async () => {
const check: Record<string, unknown> = { id: "check-1" };
const { pipeline, deps } = createPipeline({
networkService: { requestStatus: jest.fn<any>().mockResolvedValue(failingStatus) },
providerRegistry: { probe: jest.fn<any>().mockResolvedValue(failingStatus) },
checkService: { toCheck: jest.fn<any>().mockReturnValue(check) },
egressService: { assessAfterFailure: jest.fn<any>().mockResolvedValue("degraded") },
});
@@ -385,7 +385,7 @@ describe("WorkerPipeline", () => {
for (const url of ["unix:///var/run/docker.sock", "/var/run/docker.sock"]) {
const check: Record<string, unknown> = { id: "check-1" };
const { pipeline, deps } = createPipeline({
networkService: { requestStatus: jest.fn<any>().mockResolvedValue({ ...failingStatus, type: "docker" }) },
providerRegistry: { probe: jest.fn<any>().mockResolvedValue({ ...failingStatus, type: "docker" }) },
checkService: { toCheck: jest.fn<any>().mockReturnValue(check) },
egressService: { assessAfterFailure: jest.fn<any>().mockResolvedValue("degraded") },
});
@@ -406,7 +406,7 @@ describe("WorkerPipeline", () => {
]) {
const check: Record<string, unknown> = { id: "check-1" };
const { pipeline, deps } = createPipeline({
networkService: { requestStatus: jest.fn<any>().mockResolvedValue({ ...failingStatus, type: monitor.type }) },
providerRegistry: { probe: jest.fn<any>().mockResolvedValue({ ...failingStatus, type: monitor.type }) },
checkService: { toCheck: jest.fn<any>().mockReturnValue(check) },
egressService: { assessAfterFailure: jest.fn<any>().mockResolvedValue("degraded") },
});
@@ -424,7 +424,7 @@ describe("WorkerPipeline", () => {
// them from a connection failure.
const check: Record<string, unknown> = { id: "check-1" };
const { pipeline, deps } = createPipeline({
networkService: { requestStatus: jest.fn<any>().mockResolvedValue({ ...failingStatus, peerResponded: true }) },
providerRegistry: { probe: jest.fn<any>().mockResolvedValue({ ...failingStatus, peerResponded: true }) },
checkService: { toCheck: jest.fn<any>().mockReturnValue(check) },
egressService: { assessAfterFailure: jest.fn<any>().mockResolvedValue("degraded") },
});
@@ -438,7 +438,7 @@ describe("WorkerPipeline", () => {
it("consults egress when the provider reports that nothing answered, despite an HTTP-shaped code", async () => {
const check: Record<string, unknown> = { id: "check-1" };
const { pipeline, deps } = createPipeline({
networkService: { requestStatus: jest.fn<any>().mockResolvedValue({ ...failingStatus, code: 503, peerResponded: false }) },
providerRegistry: { probe: jest.fn<any>().mockResolvedValue({ ...failingStatus, code: 503, peerResponded: false }) },
checkService: { toCheck: jest.fn<any>().mockReturnValue(check) },
egressService: { assessAfterFailure: jest.fn<any>().mockResolvedValue("degraded") },
});
@@ -453,7 +453,7 @@ describe("WorkerPipeline", () => {
it("flags a failing check as degraded when the egress service reports degraded egress", async () => {
const check: Record<string, unknown> = { id: "check-1" };
const { pipeline, deps } = createPipeline({
networkService: { requestStatus: jest.fn<any>().mockResolvedValue(failingStatus) },
providerRegistry: { probe: jest.fn<any>().mockResolvedValue(failingStatus) },
checkService: { toCheck: jest.fn<any>().mockReturnValue(check) },
egressService: { assessAfterFailure: jest.fn<any>().mockResolvedValue("degraded") },
});
@@ -472,7 +472,7 @@ describe("WorkerPipeline", () => {
it("clears the status window on the first degraded check of a spell", async () => {
const { pipeline, deps } = createPipeline({
networkService: { requestStatus: jest.fn<any>().mockResolvedValue(failingStatus) },
providerRegistry: { probe: jest.fn<any>().mockResolvedValue(failingStatus) },
egressService: { assessAfterFailure: jest.fn<any>().mockResolvedValue("degraded") },
});
@@ -483,7 +483,7 @@ describe("WorkerPipeline", () => {
it("does not re-write the status window for later degraded checks in the same spell", async () => {
const { pipeline, deps } = createPipeline({
networkService: { requestStatus: jest.fn<any>().mockResolvedValue(failingStatus) },
providerRegistry: { probe: jest.fn<any>().mockResolvedValue(failingStatus) },
egressService: { assessAfterFailure: jest.fn<any>().mockResolvedValue("degraded") },
});
@@ -494,7 +494,7 @@ describe("WorkerPipeline", () => {
it("leaves the status window alone when the probe found egress fine", async () => {
const { pipeline, deps } = createPipeline({
networkService: { requestStatus: jest.fn<any>().mockResolvedValue(failingStatus) },
providerRegistry: { probe: jest.fn<any>().mockResolvedValue(failingStatus) },
egressService: { assessAfterFailure: jest.fn<any>().mockResolvedValue("ok") },
});
@@ -506,7 +506,7 @@ describe("WorkerPipeline", () => {
it("flags a failing check as ok when the probe found egress fine", async () => {
const check: Record<string, unknown> = { id: "check-1" };
const { pipeline } = createPipeline({
networkService: { requestStatus: jest.fn<any>().mockResolvedValue(failingStatus) },
providerRegistry: { probe: jest.fn<any>().mockResolvedValue(failingStatus) },
checkService: { toCheck: jest.fn<any>().mockReturnValue(check) },
egressService: { assessAfterFailure: jest.fn<any>().mockResolvedValue("ok") },
});
@@ -519,7 +519,7 @@ describe("WorkerPipeline", () => {
it("leaves the field unset on a failing check when the egress check is disabled", async () => {
const check: Record<string, unknown> = { id: "check-1" };
const { pipeline, deps } = createPipeline({
networkService: { requestStatus: jest.fn<any>().mockResolvedValue(failingStatus) },
providerRegistry: { probe: jest.fn<any>().mockResolvedValue(failingStatus) },
checkService: { toCheck: jest.fn<any>().mockReturnValue(check) },
});