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。
This commit is contained in:
@@ -12,7 +12,7 @@ import type {
|
||||
} from "../../src/shared/api";
|
||||
|
||||
import { checkerRegistry } from "../../src/server/checker/runner";
|
||||
import { CommandChecker } from "../../src/server/checker/runner/command/execute";
|
||||
import { CommandChecker } from "../../src/server/checker/runner/cmd/execute";
|
||||
import { HttpChecker } from "../../src/server/checker/runner/http/execute";
|
||||
import { ProbeStore } from "../../src/server/checker/store";
|
||||
import { startServer } from "../../src/server/server";
|
||||
@@ -56,7 +56,7 @@ describe("API 路由", () => {
|
||||
type: "http",
|
||||
},
|
||||
{
|
||||
command: {
|
||||
cmd: {
|
||||
args: ["hello"],
|
||||
cwd: "/tmp",
|
||||
env: {},
|
||||
@@ -67,7 +67,7 @@ describe("API 路由", () => {
|
||||
intervalMs: 60000,
|
||||
name: "test-b",
|
||||
timeoutMs: 5000,
|
||||
type: "command",
|
||||
type: "cmd",
|
||||
},
|
||||
]);
|
||||
|
||||
@@ -150,7 +150,7 @@ describe("API 路由", () => {
|
||||
expect(tA.stats.availability).toBeDefined();
|
||||
|
||||
const tB = body.find((t) => t.name === "test-b")!;
|
||||
expect(tB.type).toBe("command");
|
||||
expect(tB.type).toBe("cmd");
|
||||
expect(tB.target).toBe("exec echo hello");
|
||||
expect(tB.latestCheck).toBeNull();
|
||||
});
|
||||
@@ -162,7 +162,7 @@ describe("API 路由", () => {
|
||||
expect(response.status).toBe(200);
|
||||
expect(body.checkerTypes).toEqual(checkerRegistry.supportedTypes);
|
||||
expect(body.checkerTypes).toContain("http");
|
||||
expect(body.checkerTypes).toContain("command");
|
||||
expect(body.checkerTypes).toContain("cmd");
|
||||
});
|
||||
|
||||
test("不支持的 method 在有 API 通配符时返回 404", async () => {
|
||||
|
||||
@@ -15,7 +15,7 @@ const target: ResolvedTargetBase = {
|
||||
intervalMs: 30000,
|
||||
name: "test",
|
||||
timeoutMs: 5000,
|
||||
type: "command",
|
||||
type: "cmd",
|
||||
};
|
||||
|
||||
function createHarness(overrides: BootstrapDependencies = {}) {
|
||||
|
||||
@@ -3,12 +3,12 @@ import { mkdir, rm, writeFile } from "node:fs/promises";
|
||||
import { tmpdir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
|
||||
import type { ResolvedCommandTarget } from "../../../src/server/checker/runner/command/types";
|
||||
import type { ResolvedCommandTarget } from "../../../src/server/checker/runner/cmd/types";
|
||||
import type { ResolvedHttpTarget } from "../../../src/server/checker/runner/http/types";
|
||||
|
||||
import { loadConfig, parseDuration } from "../../../src/server/checker/config-loader";
|
||||
import { checkerRegistry } from "../../../src/server/checker/runner";
|
||||
import { CommandChecker } from "../../../src/server/checker/runner/command/execute";
|
||||
import { CommandChecker } from "../../../src/server/checker/runner/cmd/execute";
|
||||
import { HttpChecker } from "../../../src/server/checker/runner/http/execute";
|
||||
import { readRuntimeConfig } from "../../../src/server/config";
|
||||
|
||||
@@ -132,7 +132,7 @@ describe("loadConfig", () => {
|
||||
expect(t.timeoutMs).toBe(10000);
|
||||
});
|
||||
|
||||
test("解析最简 command 配置", async () => {
|
||||
test("解析最简 cmd 配置", async () => {
|
||||
const subdir = join(tempDir, "subdir");
|
||||
await mkdir(subdir, { recursive: true });
|
||||
const configPath = join(subdir, "cmd.yaml");
|
||||
@@ -140,8 +140,8 @@ describe("loadConfig", () => {
|
||||
configPath,
|
||||
`targets:
|
||||
- name: "check-nginx"
|
||||
type: command
|
||||
command:
|
||||
type: cmd
|
||||
cmd:
|
||||
exec: "pgrep"
|
||||
args: ["nginx"]
|
||||
`,
|
||||
@@ -150,13 +150,13 @@ describe("loadConfig", () => {
|
||||
const config = await loadConfig(configPath);
|
||||
expect(config.targets).toHaveLength(1);
|
||||
const t = config.targets[0]! as ResolvedCommandTarget;
|
||||
expect(t.type).toBe("command");
|
||||
expect(t.type).toBe("cmd");
|
||||
expect(t.name).toBe("check-nginx");
|
||||
expect(t.command.exec).toBe("pgrep");
|
||||
expect(t.command.args).toEqual(["nginx"]);
|
||||
expect(t.command.cwd).toBe(subdir);
|
||||
expect(t.command.maxOutputBytes).toBe(104857600);
|
||||
expect(t.command.env["PATH"]).toBeDefined();
|
||||
expect(t.cmd.exec).toBe("pgrep");
|
||||
expect(t.cmd.args).toEqual(["nginx"]);
|
||||
expect(t.cmd.cwd).toBe(subdir);
|
||||
expect(t.cmd.maxOutputBytes).toBe(104857600);
|
||||
expect(t.cmd.env["PATH"]).toBeDefined();
|
||||
});
|
||||
|
||||
test("解析完整配置", async () => {
|
||||
@@ -177,7 +177,7 @@ defaults:
|
||||
headers:
|
||||
Authorization: "Bearer token"
|
||||
maxBodyBytes: "50MB"
|
||||
command:
|
||||
cmd:
|
||||
cwd: "/tmp"
|
||||
maxOutputBytes: "10MB"
|
||||
targets:
|
||||
@@ -193,8 +193,8 @@ targets:
|
||||
body:
|
||||
- contains: "ok"
|
||||
- name: "cmd-target"
|
||||
type: command
|
||||
command:
|
||||
type: cmd
|
||||
cmd:
|
||||
exec: "ls"
|
||||
args: ["/tmp"]
|
||||
expect:
|
||||
@@ -222,10 +222,10 @@ targets:
|
||||
expect(http.timeoutMs).toBe(5000);
|
||||
|
||||
const cmd = config.targets[1]! as ResolvedCommandTarget;
|
||||
expect(cmd.type).toBe("command");
|
||||
expect(cmd.command.exec).toBe("ls");
|
||||
expect(cmd.command.args).toEqual(["/tmp"]);
|
||||
expect(cmd.command.maxOutputBytes).toBe(10485760);
|
||||
expect(cmd.type).toBe("cmd");
|
||||
expect(cmd.cmd.exec).toBe("ls");
|
||||
expect(cmd.cmd.args).toEqual(["/tmp"]);
|
||||
expect(cmd.cmd.maxOutputBytes).toBe(10485760);
|
||||
});
|
||||
|
||||
test("绝对 dataDir 保持不变", async () => {
|
||||
@@ -386,18 +386,18 @@ targets:
|
||||
await expect(loadConfig(configPath)).rejects.toThrow("status 模式");
|
||||
});
|
||||
|
||||
test("command target 缺少 exec 抛出错误", async () => {
|
||||
test("cmd target 缺少 exec 抛出错误", async () => {
|
||||
const configPath = join(tempDir, "no-exec.yaml");
|
||||
await writeFile(
|
||||
configPath,
|
||||
`targets:
|
||||
- name: "test"
|
||||
type: command
|
||||
command: {}
|
||||
type: cmd
|
||||
cmd: {}
|
||||
`,
|
||||
);
|
||||
// eslint-disable-next-line @typescript-eslint/await-thenable
|
||||
await expect(loadConfig(configPath)).rejects.toThrow("缺少 command.exec 字段");
|
||||
await expect(loadConfig(configPath)).rejects.toThrow("缺少 cmd.exec 字段");
|
||||
});
|
||||
|
||||
test("非法 target type 抛出错误", async () => {
|
||||
@@ -538,14 +538,14 @@ targets:
|
||||
}
|
||||
});
|
||||
|
||||
test("解析 command expect 配置", async () => {
|
||||
test("解析 cmd expect 配置", async () => {
|
||||
const configPath = join(tempDir, "cmd-expect.yaml");
|
||||
await writeFile(
|
||||
configPath,
|
||||
`targets:
|
||||
- name: "cmd-with-expect"
|
||||
type: command
|
||||
command:
|
||||
type: cmd
|
||||
cmd:
|
||||
exec: "mycheck"
|
||||
expect:
|
||||
exitCode: [0, 2]
|
||||
@@ -560,7 +560,7 @@ targets:
|
||||
|
||||
const config = await loadConfig(configPath);
|
||||
const t = config.targets[0]!;
|
||||
if (t.type === "command") {
|
||||
if (t.type === "cmd") {
|
||||
expect(t.expect).toEqual({
|
||||
exitCode: [0, 2],
|
||||
maxDurationMs: 5000,
|
||||
@@ -570,7 +570,7 @@ targets:
|
||||
}
|
||||
});
|
||||
|
||||
test("command cwd 相对配置文件目录", async () => {
|
||||
test("cmd cwd 相对配置文件目录", async () => {
|
||||
const subdir = join(tempDir, "cwd-test");
|
||||
await mkdir(subdir, { recursive: true });
|
||||
const configPath = join(subdir, "cwd.yaml");
|
||||
@@ -578,8 +578,8 @@ targets:
|
||||
configPath,
|
||||
`targets:
|
||||
- name: "cwd-test"
|
||||
type: command
|
||||
command:
|
||||
type: cmd
|
||||
cmd:
|
||||
exec: "ls"
|
||||
cwd: "scripts"
|
||||
`,
|
||||
@@ -587,17 +587,17 @@ targets:
|
||||
|
||||
const config = await loadConfig(configPath);
|
||||
const t = config.targets[0] as ResolvedCommandTarget;
|
||||
expect(t.command.cwd).toBe(join(subdir, "scripts"));
|
||||
expect(t.cmd.cwd).toBe(join(subdir, "scripts"));
|
||||
});
|
||||
|
||||
test("command env 覆盖", async () => {
|
||||
test("cmd env 覆盖", async () => {
|
||||
const configPath = join(tempDir, "env.yaml");
|
||||
await writeFile(
|
||||
configPath,
|
||||
`targets:
|
||||
- name: "env-test"
|
||||
type: command
|
||||
command:
|
||||
type: cmd
|
||||
cmd:
|
||||
exec: "echo"
|
||||
env:
|
||||
LANG: "C"
|
||||
@@ -607,9 +607,9 @@ targets:
|
||||
|
||||
const config = await loadConfig(configPath);
|
||||
const t = config.targets[0] as ResolvedCommandTarget;
|
||||
expect(t.command.env["LANG"]).toBe("C");
|
||||
expect(t.command.env["CUSTOM_VAR"]).toBe("test");
|
||||
expect(t.command.env["PATH"]).toBeDefined();
|
||||
expect(t.cmd.env["LANG"]).toBe("C");
|
||||
expect(t.cmd.env["CUSTOM_VAR"]).toBe("test");
|
||||
expect(t.cmd.env["PATH"]).toBeDefined();
|
||||
});
|
||||
|
||||
test("解析 group 字段", async () => {
|
||||
@@ -1092,8 +1092,8 @@ targets:
|
||||
X-Response-Header:
|
||||
contains: "ok"
|
||||
- name: "cmd-test"
|
||||
type: command
|
||||
command:
|
||||
type: cmd
|
||||
cmd:
|
||||
exec: "true"
|
||||
env:
|
||||
CUSTOM_ENV_NAME: "custom"
|
||||
@@ -1101,64 +1101,64 @@ targets:
|
||||
);
|
||||
const config = await loadConfig(configPath);
|
||||
const http = config.targets[0] as ResolvedHttpTarget;
|
||||
const command = config.targets[1] as ResolvedCommandTarget;
|
||||
const cmdTarget = config.targets[1] as ResolvedCommandTarget;
|
||||
expect(http.type).toBe("http");
|
||||
expect(command.type).toBe("command");
|
||||
expect(cmdTarget.type).toBe("cmd");
|
||||
expect(http.http.headers["X-Default-Header"]).toBe("default");
|
||||
expect(http.http.headers["X-Custom-Header"]).toBe("custom");
|
||||
expect(command.command.env["CUSTOM_ENV_NAME"]).toBe("custom");
|
||||
expect(cmdTarget.cmd.env["CUSTOM_ENV_NAME"]).toBe("custom");
|
||||
});
|
||||
|
||||
test("command args 类型非法", async () => {
|
||||
test("cmd args 类型非法", async () => {
|
||||
await expectConfigError(
|
||||
"bad-command-args.yaml",
|
||||
"bad-cmd-args.yaml",
|
||||
`targets:
|
||||
- name: "cmd"
|
||||
type: command
|
||||
command:
|
||||
type: cmd
|
||||
cmd:
|
||||
exec: "echo"
|
||||
args: "hello"
|
||||
`,
|
||||
"command.args 类型不合法",
|
||||
"cmd.args 类型不合法",
|
||||
);
|
||||
});
|
||||
|
||||
test("command cwd 类型非法", async () => {
|
||||
test("cmd cwd 类型非法", async () => {
|
||||
await expectConfigError(
|
||||
"bad-command-cwd.yaml",
|
||||
"bad-cmd-cwd.yaml",
|
||||
`targets:
|
||||
- name: "cmd"
|
||||
type: command
|
||||
command:
|
||||
type: cmd
|
||||
cmd:
|
||||
exec: "echo"
|
||||
cwd: 123
|
||||
`,
|
||||
"command.cwd 类型不合法",
|
||||
"cmd.cwd 类型不合法",
|
||||
);
|
||||
});
|
||||
|
||||
test("command env 值类型非法", async () => {
|
||||
test("cmd env 值类型非法", async () => {
|
||||
await expectConfigError(
|
||||
"bad-command-env.yaml",
|
||||
"bad-cmd-env.yaml",
|
||||
`targets:
|
||||
- name: "cmd"
|
||||
type: command
|
||||
command:
|
||||
type: cmd
|
||||
cmd:
|
||||
exec: "echo"
|
||||
env:
|
||||
COUNT: 123
|
||||
`,
|
||||
"command.env.COUNT 类型不合法",
|
||||
"cmd.env.COUNT 类型不合法",
|
||||
);
|
||||
});
|
||||
|
||||
test("command maxOutputBytes 非法", async () => {
|
||||
test("cmd maxOutputBytes 非法", async () => {
|
||||
await expectConfigError(
|
||||
"bad-command-max-output.yaml",
|
||||
"bad-cmd-max-output.yaml",
|
||||
`targets:
|
||||
- name: "cmd"
|
||||
type: command
|
||||
command:
|
||||
type: cmd
|
||||
cmd:
|
||||
exec: "echo"
|
||||
maxOutputBytes: "1TB"
|
||||
`,
|
||||
@@ -1166,13 +1166,13 @@ targets:
|
||||
);
|
||||
});
|
||||
|
||||
test("command expect exitCode 类型非法", async () => {
|
||||
test("cmd expect exitCode 类型非法", async () => {
|
||||
await expectConfigError(
|
||||
"bad-command-exit-code.yaml",
|
||||
"bad-cmd-exit-code.yaml",
|
||||
`targets:
|
||||
- name: "cmd"
|
||||
type: command
|
||||
command:
|
||||
type: cmd
|
||||
cmd:
|
||||
exec: "echo"
|
||||
expect:
|
||||
exitCode: [1.5]
|
||||
@@ -1181,13 +1181,13 @@ targets:
|
||||
);
|
||||
});
|
||||
|
||||
test("command stdout 空 text rule 非法", async () => {
|
||||
test("cmd stdout 空 text rule 非法", async () => {
|
||||
await expectConfigError(
|
||||
"bad-command-stdout-empty.yaml",
|
||||
"bad-cmd-stdout-empty.yaml",
|
||||
`targets:
|
||||
- name: "cmd"
|
||||
type: command
|
||||
command:
|
||||
type: cmd
|
||||
cmd:
|
||||
exec: "echo"
|
||||
expect:
|
||||
stdout:
|
||||
@@ -1197,13 +1197,13 @@ targets:
|
||||
);
|
||||
});
|
||||
|
||||
test("command stderr 未知 operator 非法", async () => {
|
||||
test("cmd stderr 未知 operator 非法", async () => {
|
||||
await expectConfigError(
|
||||
"bad-command-stderr-operator.yaml",
|
||||
"bad-cmd-stderr-operator.yaml",
|
||||
`targets:
|
||||
- name: "cmd"
|
||||
type: command
|
||||
command:
|
||||
type: cmd
|
||||
cmd:
|
||||
exec: "echo"
|
||||
expect:
|
||||
stderr:
|
||||
@@ -1213,13 +1213,13 @@ targets:
|
||||
);
|
||||
});
|
||||
|
||||
test("command stdout match 正则非法", async () => {
|
||||
test("cmd stdout match 正则非法", async () => {
|
||||
await expectConfigError(
|
||||
"bad-command-stdout-regex.yaml",
|
||||
"bad-cmd-stdout-regex.yaml",
|
||||
`targets:
|
||||
- name: "cmd"
|
||||
type: command
|
||||
command:
|
||||
type: cmd
|
||||
cmd:
|
||||
exec: "echo"
|
||||
expect:
|
||||
stdout:
|
||||
@@ -1229,13 +1229,13 @@ targets:
|
||||
);
|
||||
});
|
||||
|
||||
test("command expect 未知字段失败", async () => {
|
||||
test("cmd expect 未知字段失败", async () => {
|
||||
await expectConfigError(
|
||||
"bad-command-expect-unknown.yaml",
|
||||
"bad-cmd-expect-unknown.yaml",
|
||||
`targets:
|
||||
- name: "cmd"
|
||||
type: command
|
||||
command:
|
||||
type: cmd
|
||||
cmd:
|
||||
exec: "echo"
|
||||
expect:
|
||||
status: [200]
|
||||
|
||||
@@ -1,15 +1,19 @@
|
||||
import { describe, expect, test } from "bun:test";
|
||||
|
||||
import type { ResolvedCommandTarget } from "../../../src/server/checker/runner/command/types";
|
||||
import type { ResolvedCommandTarget } from "../../../src/server/checker/runner/cmd/types";
|
||||
import type { ResolvedHttpTarget } from "../../../src/server/checker/runner/http/types";
|
||||
import type { ProbeStore } from "../../../src/server/checker/store";
|
||||
import type { ResolvedTargetBase } from "../../../src/server/checker/types";
|
||||
|
||||
import { ProbeEngine } from "../../../src/server/checker/engine";
|
||||
import { checkerRegistry } from "../../../src/server/checker/runner";
|
||||
import { CommandChecker } from "../../../src/server/checker/runner/command/execute";
|
||||
import { CommandChecker } from "../../../src/server/checker/runner/cmd/execute";
|
||||
import { HttpChecker } from "../../../src/server/checker/runner/http/execute";
|
||||
|
||||
const processEnv = Object.fromEntries(
|
||||
Object.entries(process.env).filter((entry): entry is [string, string] => entry[1] !== undefined),
|
||||
);
|
||||
|
||||
function createMockStore(targetNames: string[]) {
|
||||
let nextId = 1;
|
||||
const targets = targetNames.map((name) => ({ id: nextId++, name }));
|
||||
@@ -27,7 +31,7 @@ function createMockStore(targetNames: string[]) {
|
||||
name,
|
||||
target: "",
|
||||
timeout_ms: 5000,
|
||||
type: "command" as const,
|
||||
type: "cmd" as const,
|
||||
}));
|
||||
},
|
||||
insertCheckResult(result: Record<string, unknown>) {
|
||||
@@ -45,18 +49,18 @@ function ensureRegistered() {
|
||||
|
||||
function makeCommandTarget(name: string, overrides?: Partial<ResolvedCommandTarget>): ResolvedCommandTarget {
|
||||
return {
|
||||
command: {
|
||||
args: ["hello"],
|
||||
cwd: "/tmp",
|
||||
env: {},
|
||||
exec: "echo",
|
||||
cmd: {
|
||||
args: ["-e", "console.log('hello')"],
|
||||
cwd: process.cwd(),
|
||||
env: processEnv,
|
||||
exec: "bun",
|
||||
maxOutputBytes: 1024 * 1024,
|
||||
},
|
||||
group: "default",
|
||||
intervalMs: 60000,
|
||||
name,
|
||||
timeoutMs: 5000,
|
||||
type: "command",
|
||||
type: "cmd",
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
@@ -72,7 +76,7 @@ describe("ProbeEngine", () => {
|
||||
expect(true).toBe(true);
|
||||
});
|
||||
|
||||
test("单次 probeGroup 执行 command 检查", async () => {
|
||||
test("单次 probeGroup 执行 cmd 检查", async () => {
|
||||
const target = makeCommandTarget("cmd-echo");
|
||||
const mockStore = createMockStore(["cmd-echo"]) as unknown as ProbeStore;
|
||||
const engine = new ProbeEngine(mockStore, [target]);
|
||||
@@ -90,10 +94,22 @@ describe("ProbeEngine", () => {
|
||||
|
||||
test("多个目标并发执行", async () => {
|
||||
const targetA = makeCommandTarget("echo-a", {
|
||||
command: { args: ["a"], cwd: "/tmp", env: {}, exec: "echo", maxOutputBytes: 1024 * 1024 },
|
||||
cmd: {
|
||||
args: ["-e", "console.log('a')"],
|
||||
cwd: process.cwd(),
|
||||
env: processEnv,
|
||||
exec: "bun",
|
||||
maxOutputBytes: 1024 * 1024,
|
||||
},
|
||||
});
|
||||
const targetB = makeCommandTarget("echo-b", {
|
||||
command: { args: ["b"], cwd: "/tmp", env: {}, exec: "echo", maxOutputBytes: 1024 * 1024 },
|
||||
cmd: {
|
||||
args: ["-e", "console.log('b')"],
|
||||
cwd: process.cwd(),
|
||||
env: processEnv,
|
||||
exec: "bun",
|
||||
maxOutputBytes: 1024 * 1024,
|
||||
},
|
||||
});
|
||||
|
||||
const mockStore = createMockStore(["echo-a", "echo-b"]) as unknown as ProbeStore;
|
||||
@@ -110,7 +126,13 @@ describe("ProbeEngine", () => {
|
||||
|
||||
test("失败目标不阻塞其他目标", async () => {
|
||||
const badTarget = makeCommandTarget("bad-cmd", {
|
||||
command: { args: [], cwd: "/tmp", env: {}, exec: "false", maxOutputBytes: 1024 * 1024 },
|
||||
cmd: {
|
||||
args: ["-e", "process.exit(1)"],
|
||||
cwd: process.cwd(),
|
||||
env: processEnv,
|
||||
exec: "bun",
|
||||
maxOutputBytes: 1024 * 1024,
|
||||
},
|
||||
});
|
||||
const goodTarget = makeCommandTarget("good-cmd");
|
||||
|
||||
@@ -133,7 +155,7 @@ describe("ProbeEngine", () => {
|
||||
|
||||
test("checker rejected 时写入 internal error 结果", async () => {
|
||||
ensureRegistered();
|
||||
const checker = checkerRegistry.get("command");
|
||||
const checker = checkerRegistry.get("cmd");
|
||||
const originalExecute = checker.execute.bind(checker);
|
||||
checker.execute = async (target, ctx) => {
|
||||
if (target.name === "reject-cmd") {
|
||||
@@ -176,7 +198,13 @@ describe("ProbeEngine", () => {
|
||||
test("并发限制 maxConcurrentChecks", async () => {
|
||||
const targets = Array.from({ length: 5 }, (_, i) =>
|
||||
makeCommandTarget(`cmd-${i}`, {
|
||||
command: { args: [String(i)], cwd: "/tmp", env: {}, exec: "echo", maxOutputBytes: 1024 * 1024 },
|
||||
cmd: {
|
||||
args: ["-e", `console.log('${i}')`],
|
||||
cwd: process.cwd(),
|
||||
env: processEnv,
|
||||
exec: "bun",
|
||||
maxOutputBytes: 1024 * 1024,
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { describe, expect, test } from "bun:test";
|
||||
|
||||
import { checkExitCode } from "../../../../../src/server/checker/runner/command/expect";
|
||||
import { checkExitCode } from "../../../../../src/server/checker/runner/cmd/expect";
|
||||
|
||||
describe("checkExitCode", () => {
|
||||
test("exitCode 在允许列表中匹配成功", () => {
|
||||
@@ -1,9 +1,9 @@
|
||||
import { describe, expect, test } from "bun:test";
|
||||
|
||||
import type { ResolvedCommandTarget } from "../../../../../src/server/checker/runner/command/types";
|
||||
import type { ResolvedCommandTarget } from "../../../../../src/server/checker/runner/cmd/types";
|
||||
import type { CheckerContext } from "../../../../../src/server/checker/runner/types";
|
||||
|
||||
import { CommandChecker } from "../../../../../src/server/checker/runner/command/execute";
|
||||
import { CommandChecker } from "../../../../../src/server/checker/runner/cmd/execute";
|
||||
|
||||
const checker = new CommandChecker();
|
||||
|
||||
@@ -18,37 +18,37 @@ function makeCtx(timeoutMs = 5000): CheckerContext {
|
||||
}
|
||||
|
||||
function makeTarget(
|
||||
command: Partial<ResolvedCommandTarget["command"]>,
|
||||
cmd: Partial<ResolvedCommandTarget["cmd"]>,
|
||||
overrides?: Partial<ResolvedCommandTarget>,
|
||||
): ResolvedCommandTarget {
|
||||
return {
|
||||
command: {
|
||||
args: ["hello"],
|
||||
cwd: "/tmp",
|
||||
cmd: {
|
||||
args: ["-e", "console.log('hello')"],
|
||||
cwd: process.cwd(),
|
||||
env: processEnv,
|
||||
exec: "echo",
|
||||
exec: "bun",
|
||||
maxOutputBytes: 1024 * 1024,
|
||||
...command,
|
||||
...cmd,
|
||||
},
|
||||
group: "default",
|
||||
intervalMs: 60000,
|
||||
name: "test-cmd",
|
||||
timeoutMs: 5000,
|
||||
type: "command",
|
||||
type: "cmd",
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
describe("CommandChecker", () => {
|
||||
test("exitCode=0 成功", async () => {
|
||||
const result = await checker.execute(makeTarget({ args: [], exec: "true" }), makeCtx());
|
||||
const result = await checker.execute(makeTarget({ args: ["-e", "process.exit(0)"], exec: "bun" }), makeCtx());
|
||||
expect(result.matched).toBe(true);
|
||||
expect(result.statusDetail).toBe("exitCode=0");
|
||||
expect(result.failure).toBeNull();
|
||||
});
|
||||
|
||||
test("exitCode=1 不匹配默认 [0]", async () => {
|
||||
const result = await checker.execute(makeTarget({ args: [], exec: "false" }), makeCtx());
|
||||
const result = await checker.execute(makeTarget({ args: ["-e", "process.exit(1)"], exec: "bun" }), makeCtx());
|
||||
expect(result.matched).toBe(false);
|
||||
expect(result.statusDetail).toBe("exitCode=1");
|
||||
expect(result.failure!.phase).toBe("exitCode");
|
||||
@@ -56,7 +56,7 @@ describe("CommandChecker", () => {
|
||||
|
||||
test("exitCode=1 匹配自定义 [1]", async () => {
|
||||
const result = await checker.execute(
|
||||
makeTarget({ args: [], exec: "false" }, { expect: { exitCode: [1] } }),
|
||||
makeTarget({ args: ["-e", "process.exit(1)"], exec: "bun" }, { expect: { exitCode: [1] } }),
|
||||
makeCtx(),
|
||||
);
|
||||
expect(result.matched).toBe(true);
|
||||
@@ -64,26 +64,35 @@ describe("CommandChecker", () => {
|
||||
});
|
||||
|
||||
test("命令不存在返回 spawn 错误", async () => {
|
||||
const result = await checker.execute(makeTarget({ exec: "/nonexistent/command/xyz" }), makeCtx());
|
||||
const result = await checker.execute(makeTarget({ exec: "dial-command-not-found-xyz" }), makeCtx());
|
||||
expect(result.matched).toBe(false);
|
||||
expect(result.failure!.phase).toBe("exitCode");
|
||||
expect(result.failure!.message).toBeTruthy();
|
||||
});
|
||||
|
||||
test("超时返回错误", async () => {
|
||||
const result = await checker.execute(makeTarget({ args: ["10"], exec: "sleep" }, { timeoutMs: 100 }), makeCtx(100));
|
||||
const result = await checker.execute(
|
||||
makeTarget({ args: ["-e", "await Bun.sleep(10000)"], exec: "bun" }, { timeoutMs: 100 }),
|
||||
makeCtx(100),
|
||||
);
|
||||
expect(result.matched).toBe(false);
|
||||
expect(result.failure!.message).toContain("超时");
|
||||
});
|
||||
|
||||
test("stdout 输出捕获", async () => {
|
||||
const result = await checker.execute(makeTarget({ args: ["hello world"], exec: "echo" }), makeCtx());
|
||||
const result = await checker.execute(
|
||||
makeTarget({ args: ["-e", "console.log('hello world')"], exec: "bun" }),
|
||||
makeCtx(),
|
||||
);
|
||||
expect(result.matched).toBe(true);
|
||||
});
|
||||
|
||||
test("stdout 匹配 expect", async () => {
|
||||
const result = await checker.execute(
|
||||
makeTarget({ args: ["hello"], exec: "echo" }, { expect: { stdout: [{ contains: "hello" }] } }),
|
||||
makeTarget(
|
||||
{ args: ["-e", "console.log('hello')"], exec: "bun" },
|
||||
{ expect: { stdout: [{ contains: "hello" }] } },
|
||||
),
|
||||
makeCtx(),
|
||||
);
|
||||
expect(result.matched).toBe(true);
|
||||
@@ -91,7 +100,10 @@ describe("CommandChecker", () => {
|
||||
|
||||
test("stdout 不匹配 expect", async () => {
|
||||
const result = await checker.execute(
|
||||
makeTarget({ args: ["hello"], exec: "echo" }, { expect: { stdout: [{ contains: "nonexistent" }] } }),
|
||||
makeTarget(
|
||||
{ args: ["-e", "console.log('hello')"], exec: "bun" },
|
||||
{ expect: { stdout: [{ contains: "nonexistent" }] } },
|
||||
),
|
||||
makeCtx(),
|
||||
);
|
||||
expect(result.matched).toBe(false);
|
||||
@@ -100,7 +112,10 @@ describe("CommandChecker", () => {
|
||||
|
||||
test("stderr 匹配 expect", async () => {
|
||||
const result = await checker.execute(
|
||||
makeTarget({ args: ["-c", "echo error >&2"], exec: "bash" }, { expect: { stderr: [{ contains: "error" }] } }),
|
||||
makeTarget(
|
||||
{ args: ["-e", "process.stderr.write('error\\n')"], exec: "bun" },
|
||||
{ expect: { stderr: [{ contains: "error" }] } },
|
||||
),
|
||||
makeCtx(),
|
||||
);
|
||||
expect(result.matched).toBe(true);
|
||||
@@ -108,7 +123,7 @@ describe("CommandChecker", () => {
|
||||
|
||||
test("输出超过 maxOutputBytes", async () => {
|
||||
const result = await checker.execute(
|
||||
makeTarget({ args: ["-c", "yes | head -1000"], exec: "bash", maxOutputBytes: 10 }),
|
||||
makeTarget({ args: ["-e", "process.stdout.write('y\\n'.repeat(1000))"], exec: "bun", maxOutputBytes: 10 }),
|
||||
makeCtx(),
|
||||
);
|
||||
expect(result.matched).toBe(false);
|
||||
@@ -116,7 +131,7 @@ describe("CommandChecker", () => {
|
||||
});
|
||||
|
||||
test("durationMs 非空", async () => {
|
||||
const result = await checker.execute(makeTarget({ args: [], exec: "true" }), makeCtx());
|
||||
const result = await checker.execute(makeTarget({ args: ["-e", "process.exit(0)"], exec: "bun" }), makeCtx());
|
||||
expect(result.durationMs).not.toBeNull();
|
||||
expect(result.durationMs!).toBeGreaterThanOrEqual(0);
|
||||
});
|
||||
@@ -134,8 +149,8 @@ describe("CommandChecker", () => {
|
||||
makeTarget(
|
||||
{
|
||||
args: ["-e", "console.log(process.env.DIAL_TEST_ENV ?? '')"],
|
||||
env: { DIAL_TEST_ENV: "resolved-env" },
|
||||
exec: process.execPath,
|
||||
env: { ...processEnv, DIAL_TEST_ENV: "resolved-env" },
|
||||
exec: "bun",
|
||||
},
|
||||
{ expect: { stdout: [{ contains: "resolved-env" }] } },
|
||||
),
|
||||
@@ -146,11 +161,11 @@ describe("CommandChecker", () => {
|
||||
});
|
||||
|
||||
test("serialize 返回命令摘要和 config JSON", () => {
|
||||
const target = makeTarget({ args: ["hello"], exec: "echo" });
|
||||
const target = makeTarget({ args: ["-e", "console.log('hello')"], exec: "bun" });
|
||||
const s = checker.serialize(target);
|
||||
expect(s.target).toBe("exec echo hello");
|
||||
expect(s.target).toBe("exec bun -e console.log('hello')");
|
||||
const config = JSON.parse(s.config) as { args: string[]; exec: string };
|
||||
expect(config.exec).toBe("echo");
|
||||
expect(config.args).toEqual(["hello"]);
|
||||
expect(config.exec).toBe("bun");
|
||||
expect(config.args).toEqual(["-e", "console.log('hello')"]);
|
||||
});
|
||||
});
|
||||
@@ -45,8 +45,8 @@ describe("CheckerRegistry", () => {
|
||||
test("查询支持的 type 列表", () => {
|
||||
const registry = new CheckerRegistry();
|
||||
registry.register(createChecker("http"));
|
||||
registry.register(createChecker("command"));
|
||||
expect(registry.supportedTypes).toEqual(["http", "command"]);
|
||||
registry.register(createChecker("cmd"));
|
||||
expect(registry.supportedTypes).toEqual(["http", "cmd"]);
|
||||
});
|
||||
|
||||
test("definitions 返回注册定义", () => {
|
||||
@@ -66,8 +66,8 @@ describe("CheckerRegistry", () => {
|
||||
const second = createDefaultCheckerRegistry();
|
||||
first.register(createChecker("custom"));
|
||||
|
||||
expect(first.supportedTypes).toEqual(["http", "command", "custom"]);
|
||||
expect(second.supportedTypes).toEqual(["http", "command"]);
|
||||
expect(first.supportedTypes).toEqual(["http", "cmd", "custom"]);
|
||||
expect(second.supportedTypes).toEqual(["http", "cmd"]);
|
||||
expect(
|
||||
first.definitions.every(
|
||||
(checker) => checker.schemas.config && checker.schemas.defaults && checker.schemas.expect,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { describe, expect, test } from "bun:test";
|
||||
|
||||
import { checkTextRules } from "../../../../../src/server/checker/runner/command/text";
|
||||
import { checkTextRules } from "../../../../../src/server/checker/runner/cmd/text";
|
||||
|
||||
describe("checkTextRules", () => {
|
||||
test("无规则返回匹配成功", () => {
|
||||
|
||||
@@ -3,12 +3,12 @@ import { mkdir } from "node:fs/promises";
|
||||
import { tmpdir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
|
||||
import type { ResolvedCommandTarget } from "../../../src/server/checker/runner/command/types";
|
||||
import type { ResolvedCommandTarget } from "../../../src/server/checker/runner/cmd/types";
|
||||
import type { ResolvedHttpTarget } from "../../../src/server/checker/runner/http/types";
|
||||
import type { CheckFailure } from "../../../src/server/checker/types";
|
||||
|
||||
import { checkerRegistry } from "../../../src/server/checker/runner";
|
||||
import { CommandChecker } from "../../../src/server/checker/runner/command/execute";
|
||||
import { CommandChecker } from "../../../src/server/checker/runner/cmd/execute";
|
||||
import { HttpChecker } from "../../../src/server/checker/runner/http/execute";
|
||||
import { ProbeStore } from "../../../src/server/checker/store";
|
||||
import { rmRetry } from "../../helpers";
|
||||
@@ -42,7 +42,7 @@ const httpTarget: ResolvedHttpTarget = {
|
||||
};
|
||||
|
||||
const commandTarget: ResolvedCommandTarget = {
|
||||
command: {
|
||||
cmd: {
|
||||
args: ["-c", "1", "localhost"],
|
||||
cwd: "/tmp",
|
||||
env: {},
|
||||
@@ -53,7 +53,7 @@ const commandTarget: ResolvedCommandTarget = {
|
||||
intervalMs: 60000,
|
||||
name: "test-cmd",
|
||||
timeoutMs: 5000,
|
||||
type: "command",
|
||||
type: "cmd",
|
||||
};
|
||||
|
||||
describe("ProbeStore", () => {
|
||||
@@ -75,7 +75,7 @@ describe("ProbeStore", () => {
|
||||
expect(store.getTargets()).toHaveLength(0);
|
||||
});
|
||||
|
||||
test("同步 http 和 command targets", () => {
|
||||
test("同步 http 和 cmd targets", () => {
|
||||
store.syncTargets([httpTarget, commandTarget]);
|
||||
const targets = store.getTargets();
|
||||
expect(targets).toHaveLength(2);
|
||||
@@ -106,9 +106,9 @@ describe("ProbeStore", () => {
|
||||
expect(JSON.parse(t.expect!)).toEqual({ maxDurationMs: 3000, status: [200] });
|
||||
});
|
||||
|
||||
test("command target 字段正确", () => {
|
||||
test("cmd target 字段正确", () => {
|
||||
const t = store.getTargets().find((t) => t.name === "test-cmd")!;
|
||||
expect(t.type).toBe("command");
|
||||
expect(t.type).toBe("cmd");
|
||||
expect(t.target).toBe("exec ping -c 1 localhost");
|
||||
const config = JSON.parse(t.config) as { args: string[]; cwd: string; exec: string; maxOutputBytes: number };
|
||||
expect(config.exec).toBe("ping");
|
||||
|
||||
Reference in New Issue
Block a user