1
0
Files
DiAL/tests/server/bootstrap.test.ts
lanyuanxiaoyao e983e5d75d refactor: 重命名 command checker 为 cmd checker 并适配跨平台测试
将 type/configKey 从 "command" 统一为 "cmd",源码目录 runner/command/ → runner/cmd/,
spec 目录 command-checker/ → cmd-checker/,测试全部改用 bun -e 替代 Unix 系统命令,
归档 cmd-checker-enhancement 变更并同步 delta spec 到主 spec。
2026-05-14 09:23:10 +08:00

133 lines
3.9 KiB
TypeScript

import { describe, expect, test } from "bun:test";
import { join } from "node:path";
import type { ResolvedConfig } from "../../src/server/checker/config-loader";
import type { ProbeEngine } from "../../src/server/checker/engine";
import type { ProbeStore } from "../../src/server/checker/store";
import type { ResolvedTargetBase } from "../../src/server/checker/types";
import { bootstrap, type BootstrapDependencies } from "../../src/server/bootstrap";
type ShutdownSignal = "SIGINT" | "SIGTERM";
const target: ResolvedTargetBase = {
group: "default",
intervalMs: 30000,
name: "test",
timeoutMs: 5000,
type: "cmd",
};
function createHarness(overrides: BootstrapDependencies = {}) {
const calls: string[] = [];
const shutdownHandlers = new Map<ShutdownSignal, () => void>();
const config: ResolvedConfig = {
configDir: "/tmp",
dataDir: "/tmp/dial-data",
host: "127.0.0.1",
maxConcurrentChecks: 3,
port: 3000,
retentionMs: 1000,
targets: [target],
};
const store = {
close() {
calls.push("store.close");
},
syncTargets(targets: ResolvedTargetBase[]) {
calls.push(`syncTargets:${targets.length}`);
},
} as unknown as ProbeStore;
const engine = {
start() {
calls.push("engine.start");
},
stop() {
calls.push("engine.stop");
},
} as unknown as ProbeEngine;
const dependencies: BootstrapDependencies = {
createEngine(actualStore, targets, maxConcurrentChecks, retentionMs) {
expect(actualStore).toBe(store);
calls.push(`createEngine:${targets.length}:${maxConcurrentChecks}:${retentionMs}`);
return engine;
},
createStore(dbPath) {
calls.push(`createStore:${dbPath}`);
return store;
},
exit(code) {
calls.push(`exit:${code}`);
throw new Error(`exit:${code}`);
},
loadConfig(configPath) {
calls.push(`loadConfig:${configPath}`);
return Promise.resolve(config);
},
logError(...data) {
calls.push(`logError:${String(data[1])}`);
},
onSignal(signal, handler) {
calls.push(`onSignal:${signal}`);
shutdownHandlers.set(signal, handler);
},
startServer(options) {
expect(options.config).toEqual({ host: config.host, port: config.port });
expect(options.store).toBe(store);
calls.push(`startServer:${options.mode}`);
},
...overrides,
};
return { calls, dependencies, shutdownHandlers };
}
describe("bootstrap", () => {
test("开发模式执行完整启动序列", async () => {
const { calls, dependencies } = createHarness();
await bootstrap({ configPath: "/tmp/probes.yaml", mode: "development" }, dependencies);
expect(calls).toEqual([
"loadConfig:/tmp/probes.yaml",
`createStore:${join("/tmp/dial-data", "probe.db")}`,
"syncTargets:1",
"createEngine:1:3:1000",
"engine.start",
"onSignal:SIGINT",
"onSignal:SIGTERM",
"startServer:development",
]);
});
test("收到退出信号时停止 engine 并关闭 store", async () => {
const { calls, dependencies, shutdownHandlers } = createHarness();
await bootstrap({ configPath: "/tmp/probes.yaml", mode: "development" }, dependencies);
expect(() => shutdownHandlers.get("SIGINT")!()).toThrow("exit:0");
expect(calls.slice(-3)).toEqual(["engine.stop", "store.close", "exit:0"]);
});
test("启动失败时输出错误并以非零退出", async () => {
const { calls, dependencies } = createHarness({
loadConfig() {
return Promise.reject(new Error("bad config"));
},
});
let error: unknown;
try {
await bootstrap({ configPath: "/tmp/probes.yaml", mode: "development" }, dependencies);
} catch (caught) {
error = caught;
}
expect(error).toBeInstanceOf(Error);
expect((error as Error).message).toBe("exit:1");
expect(calls).toEqual(["logError:bad config", "exit:1"]);
});
});