import { describe, expect, test } from "bun:test"; import { ProbeEngine } from "../../../src/server/checker/engine"; import type { ProbeStore } from "../../../src/server/checker/store"; import type { ResolvedCommandTarget, ResolvedHttpTarget, ResolvedTarget } from "../../../src/server/checker/types"; function createMockStore(targetNames: string[]) { let nextId = 1; const targets = targetNames.map((name) => ({ id: nextId++, name })); const results: Array> = []; return { getTargets() { return targets.map(({ id, name }) => ({ id, name, type: "command" as const, target: "", config: "", interval_ms: 60000, timeout_ms: 5000, expect: null, })); }, insertCheckResult(result: Record) { results.push(result); }, _results: results, }; } function makeCommandTarget(name: string, overrides?: Partial): ResolvedCommandTarget { return { type: "command", name, command: { exec: "echo", args: ["hello"], cwd: "/tmp", env: {}, maxOutputBytes: 1024 * 1024, }, intervalMs: 60000, timeoutMs: 5000, ...overrides, }; } describe("ProbeEngine", () => { test("start/stop 不抛错", () => { const mockStore = createMockStore(["test"]) as unknown as ProbeStore; const targets: ResolvedTarget[] = [makeCommandTarget("test")]; const engine = new ProbeEngine(mockStore, targets); engine.start(); engine.stop(); expect(true).toBe(true); }); test("单次 probeGroup 执行 command 检查", async () => { const target = makeCommandTarget("cmd-echo"); const mockStore = createMockStore(["cmd-echo"]) as unknown as ProbeStore; const engine = new ProbeEngine(mockStore, [target]); const probeGroup = (engine as unknown as { probeGroup: (t: ResolvedTarget[]) => Promise }).probeGroup.bind( engine, ); await probeGroup([target]); const results = (mockStore as unknown as { _results: Array> })._results; expect(results.length).toBe(1); expect(results[0]!.success).toBe(true); expect(results[0]!.matched).toBe(true); expect(results[0]!.statusDetail).toBe("exitCode=0"); }); test("多个目标并发执行", async () => { const targetA = makeCommandTarget("echo-a", { command: { exec: "echo", args: ["a"], cwd: "/tmp", env: {}, maxOutputBytes: 1024 * 1024 }, }); const targetB = makeCommandTarget("echo-b", { command: { exec: "echo", args: ["b"], cwd: "/tmp", env: {}, maxOutputBytes: 1024 * 1024 }, }); const mockStore = createMockStore(["echo-a", "echo-b"]) as unknown as ProbeStore; const engine = new ProbeEngine(mockStore, [targetA, targetB]); const probeGroup = (engine as unknown as { probeGroup: (t: ResolvedTarget[]) => Promise }).probeGroup.bind( engine, ); await probeGroup([targetA, targetB]); const results = (mockStore as unknown as { _results: Array> })._results; expect(results.length).toBe(2); }); test("失败目标不阻塞其他目标", async () => { const badTarget = makeCommandTarget("bad-cmd", { command: { exec: "false", args: [], cwd: "/tmp", env: {}, maxOutputBytes: 1024 * 1024 }, }); const goodTarget = makeCommandTarget("good-cmd"); const mockStore = createMockStore(["bad-cmd", "good-cmd"]) as unknown as ProbeStore; const engine = new ProbeEngine(mockStore, [badTarget, goodTarget]); const probeGroup = (engine as unknown as { probeGroup: (t: ResolvedTarget[]) => Promise }).probeGroup.bind( engine, ); await probeGroup([badTarget, goodTarget]); const results = (mockStore as unknown as { _results: Array> })._results; expect(results.length).toBe(2); const badResult = results.find((r) => r.success === false); const goodResult = results.find((r) => r.success === true); expect(badResult).toBeDefined(); expect(goodResult).toBeDefined(); }); test("并发限制 maxConcurrentChecks", async () => { const targets = Array.from({ length: 5 }, (_, i) => makeCommandTarget(`cmd-${i}`, { command: { exec: "echo", args: [String(i)], cwd: "/tmp", env: {}, maxOutputBytes: 1024 * 1024 }, }), ); const mockStore = createMockStore(targets.map((t) => t.name)) as unknown as ProbeStore; const engine = new ProbeEngine(mockStore, targets, 2); const probeGroup = (engine as unknown as { probeGroup: (t: ResolvedTarget[]) => Promise }).probeGroup.bind( engine, ); await probeGroup(targets); const results = (mockStore as unknown as { _results: Array> })._results; expect(results.length).toBe(5); for (const r of results) { expect(r.success).toBe(true); } }); test("groupByInterval 按间隔分组", () => { const targetA = makeCommandTarget("a", { intervalMs: 30000 }); const targetB = makeCommandTarget("b", { intervalMs: 30000 }); const targetC = makeCommandTarget("c", { intervalMs: 60000 }); const mockStore = createMockStore(["a", "b", "c"]) as unknown as ProbeStore; const engine = new ProbeEngine(mockStore, [targetA, targetB, targetC]); engine.start(); engine.stop(); expect(true).toBe(true); }); test("未注册的 targetName 不写入结果", async () => { const target = makeCommandTarget("unknown-target"); const mockStore = createMockStore(["other-name"]) as unknown as ProbeStore; const engine = new ProbeEngine(mockStore, [target]); const probeGroup = (engine as unknown as { probeGroup: (t: ResolvedTarget[]) => Promise }).probeGroup.bind( engine, ); await probeGroup([target]); const results = (mockStore as unknown as { _results: Array> })._results; expect(results.length).toBe(0); }); test("HTTP 目标运行", async () => { const httpServer = Bun.serve({ port: 0, fetch() { return new Response("ok"); }, }); try { const httpTarget: ResolvedHttpTarget = { type: "http", name: "http-test", http: { url: `http://localhost:${httpServer.port}/`, method: "GET", headers: {}, maxBodyBytes: 1024 * 1024, }, intervalMs: 60000, timeoutMs: 5000, }; const mockStore = createMockStore(["http-test"]) as unknown as ProbeStore; const engine = new ProbeEngine(mockStore, [httpTarget]); const probeGroup = (engine as unknown as { probeGroup: (t: ResolvedTarget[]) => Promise }).probeGroup.bind( engine, ); await probeGroup([httpTarget]); const results = (mockStore as unknown as { _results: Array> })._results; expect(results.length).toBe(1); expect(results[0]!.success).toBe(true); expect(results[0]!.statusDetail).toBe("HTTP 200"); } finally { httpServer.stop(); } }); });