1
0

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:
2026-05-14 09:23:10 +08:00
parent 0fa2c0c811
commit e983e5d75d
40 changed files with 522 additions and 773 deletions

View File

@@ -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 () => {

View File

@@ -15,7 +15,7 @@ const target: ResolvedTargetBase = {
intervalMs: 30000,
name: "test",
timeoutMs: 5000,
type: "command",
type: "cmd",
};
function createHarness(overrides: BootstrapDependencies = {}) {

View File

@@ -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]

View File

@@ -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,
},
}),
);

View File

@@ -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 在允许列表中匹配成功", () => {

View File

@@ -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')"]);
});
});

View File

@@ -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,

View File

@@ -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("无规则返回匹配成功", () => {

View File

@@ -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");

View File

@@ -34,7 +34,7 @@ function makeTarget(overrides: Partial<TargetStatus> = {}): TargetStatus {
describe("createTargetTableColumns", () => {
test("生成 7 个目标表格列", () => {
const columns = createTargetTableColumns(["http", "command"]);
const columns = createTargetTableColumns(["http", "cmd"]);
expect(columns.map((column) => column.colKey)).toEqual([
"latestCheck.matched",
@@ -48,14 +48,14 @@ describe("createTargetTableColumns", () => {
});
test("根据 checkerTypes 生成类型筛选器", () => {
const typeColumn = getColumn(createTargetTableColumns(["http", "command", "tcp"]), "type");
const typeColumn = getColumn(createTargetTableColumns(["http", "cmd", "tcp"]), "type");
const filter = typeColumn.filter as TableFilter;
expect(filter.type).toBe("single");
expect(filter.list).toEqual([
{ label: "全部", value: "" },
{ label: "http", value: "http" },
{ label: "command", value: "command" },
{ label: "cmd", value: "cmd" },
{ label: "tcp", value: "tcp" },
]);
});