- 引入 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 与示例配置
132 lines
4.6 KiB
TypeScript
132 lines
4.6 KiB
TypeScript
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);
|
||
});
|
||
});
|