1
0

feat: 重构为多类型 checker 通用框架,支持 HTTP 与命令检查

- 引入 typed target 判别联合,支持 http 与 command 两种 checker
- expect 重构为有序规则数组,按配置顺序快速失败并生成结构化 failure
- 新增 command runner,支持 exec + args 本地命令执行
- 引入全局并发限制 maxConcurrentChecks 和 size 解析 (KB/MB/GB)
- HTTP/command 各自独立 expect pipeline,应用领域默认成功语义
- SQLite schema、API、Dashboard 全链路调整为 checker 通用契约
- 补充完整测试覆盖(192 tests),更新 README 与示例配置
This commit is contained in:
2026-05-10 22:25:21 +08:00
parent 599d973cbd
commit b8810f1182
46 changed files with 3562 additions and 1062 deletions

View File

@@ -0,0 +1,131 @@
import { describe, expect, test } from "bun:test";
import { runCommandCheck } from "../../../src/server/checker/command-runner";
import type { ResolvedCommandTarget } from "../../../src/server/checker/types";
function makeTarget(
command: Partial<ResolvedCommandTarget["command"]>,
overrides?: Partial<ResolvedCommandTarget>,
): ResolvedCommandTarget {
return {
type: "command",
name: "test-cmd",
command: {
exec: "echo",
args: ["hello"],
cwd: "/tmp",
env: {},
maxOutputBytes: 1024 * 1024,
...command,
},
intervalMs: 60000,
timeoutMs: 5000,
...overrides,
};
}
describe("runCommandCheck", () => {
test("exitCode=0 成功", async () => {
const result = await runCommandCheck(makeTarget({ exec: "true", args: [] }));
expect(result.success).toBe(true);
expect(result.matched).toBe(true);
expect(result.statusDetail).toBe("exitCode=0");
expect(result.failure).toBeNull();
});
test("exitCode=1 不匹配默认 [0]", async () => {
const result = await runCommandCheck(makeTarget({ exec: "false", args: [] }));
expect(result.success).toBe(false);
expect(result.matched).toBe(false);
expect(result.statusDetail).toBe("exitCode=1");
expect(result.failure).not.toBeNull();
expect(result.failure!.phase).toBe("exitCode");
});
test("exitCode=1 匹配自定义 [1]", async () => {
const result = await runCommandCheck(makeTarget({ exec: "false", args: [] }, { expect: { exitCode: [1] } }));
expect(result.success).toBe(true);
expect(result.matched).toBe(true);
expect(result.statusDetail).toBe("exitCode=1");
});
test("命令不存在返回 spawn 错误", async () => {
const result = await runCommandCheck(makeTarget({ exec: "/nonexistent/command/xyz" }));
expect(result.success).toBe(false);
expect(result.failure).not.toBeNull();
expect(result.failure!.phase).toBe("exitCode");
expect(result.failure!.message).toBeTruthy();
});
test("超时返回错误", async () => {
const result = await runCommandCheck(makeTarget({ exec: "sleep", args: ["10"] }, { timeoutMs: 100 }));
expect(result.success).toBe(false);
expect(result.failure).not.toBeNull();
expect(result.failure!.message).toContain("超时");
});
test("stdout 输出捕获", async () => {
const result = await runCommandCheck(makeTarget({ exec: "echo", args: ["hello world"] }));
expect(result.success).toBe(true);
});
test("stdout 匹配 expect", async () => {
const result = await runCommandCheck(
makeTarget({ exec: "echo", args: ["hello"] }, { expect: { stdout: [{ contains: "hello" }] } }),
);
expect(result.success).toBe(true);
expect(result.matched).toBe(true);
});
test("stdout 不匹配 expect", async () => {
const result = await runCommandCheck(
makeTarget({ exec: "echo", args: ["hello"] }, { expect: { stdout: [{ contains: "nonexistent" }] } }),
);
expect(result.success).toBe(false);
expect(result.failure!.phase).toBe("stdout");
});
test("stderr 匹配 expect", async () => {
const result = await runCommandCheck(
makeTarget({ exec: "bash", args: ["-c", "echo error >&2"] }, { expect: { stderr: [{ contains: "error" }] } }),
);
expect(result.success).toBe(true);
expect(result.matched).toBe(true);
});
test("输出超过 maxOutputBytes", async () => {
const result = await runCommandCheck(
makeTarget({
exec: "bash",
args: ["-c", "yes | head -1000"],
maxOutputBytes: 10,
}),
);
expect(result.success).toBe(false);
expect(result.failure).not.toBeNull();
expect(result.failure!.message).toContain("超过限制");
});
test("durationMs 非空", async () => {
const result = await runCommandCheck(makeTarget({ exec: "true", args: [] }));
expect(result.durationMs).not.toBeNull();
expect(result.durationMs!).toBeGreaterThanOrEqual(0);
});
test("ls 命令执行成功", async () => {
const result = await runCommandCheck(makeTarget({ exec: "ls", args: ["/tmp"] }));
expect(result.success).toBe(true);
expect(result.statusDetail).toBe("exitCode=0");
});
test("不使用 shell通配符不被展开", async () => {
const result = await runCommandCheck(
makeTarget({ exec: "echo", args: ["*"] }, { expect: { stdout: [{ contains: "*" }] } }),
);
expect(result.success).toBe(true);
});
test("不提供 stdin等待输入的命令会阻塞超时", async () => {
const result = await runCommandCheck(makeTarget({ exec: "bash", args: ["-c", "read line"] }, { timeoutMs: 500 }));
expect(result.success).toBe(false);
});
});