1
0
Files
DiAL/tests/server/checker/config-loader.test.ts
lanyuanxiaoyao bce0f8e7a8 feat: 增强 HTTP checker 鲁棒性 — 严格配置校验、完整耗时、流式body、重定向与编码完善
启动期校验: 新增 validate.ts 对 HTTP config/expect/body rule/operator 全方位严格校验
执行语义: body 改为 Web Stream 流式超限中止,durationMs 覆盖完整执行
错误归属: status/header 失败不读 body,phase 分层 request/body,early duration skip body
重定向: 跟随前释放 body,POST/303 改 GET 清理 header,跨 origin 剥离敏感 header
编码: 支持 quoted charset,未知编码返回结构化解码错误
文档: README match→regex+durationMs,DEVELOPMENT 执行流程与错误归属
测试: +63 测试覆盖全部新增场景,325 pass 0 fail
规格: 同步 probe-config/probe-engine/expect-body-checkers 3 个 delta spec
2026-05-13 08:00:05 +08:00

995 lines
26 KiB
TypeScript

import { afterAll, beforeAll, describe, expect, test } from "bun:test";
import { mkdir, rm, writeFile } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { loadConfig, parseDuration } from "../../../src/server/checker/config-loader";
import { checkerRegistry } from "../../../src/server/checker/runner";
import { CommandChecker } from "../../../src/server/checker/runner/command/runner";
import { HttpChecker } from "../../../src/server/checker/runner/http/runner";
import { readRuntimeConfig } from "../../../src/server/config";
function ensureRegistered() {
if (!checkerRegistry.supportedTypes.includes("http")) {
checkerRegistry.register(new HttpChecker());
checkerRegistry.register(new CommandChecker());
}
}
beforeAll(() => {
ensureRegistered();
});
describe("parseDuration", () => {
test("解析秒", () => {
expect(parseDuration("30s")).toBe(30000);
expect(parseDuration("1s")).toBe(1000);
});
test("解析分钟", () => {
expect(parseDuration("5m")).toBe(300000);
expect(parseDuration("1m")).toBe(60000);
});
test("解析毫秒", () => {
expect(parseDuration("500ms")).toBe(500);
expect(parseDuration("100ms")).toBe(100);
});
test("解析小数", () => {
expect(parseDuration("1.5s")).toBe(1500);
});
test("无效格式抛出错误", () => {
expect(() => parseDuration("30")).toThrow("无效的时长格式");
expect(() => parseDuration("abc")).toThrow("无效的时长格式");
expect(() => parseDuration("30x")).toThrow("无效的时长格式");
expect(() => parseDuration("")).toThrow("无效的时长格式");
});
});
describe("readRuntimeConfig", () => {
test("返回配置文件路径", () => {
expect(readRuntimeConfig(["./probes.yaml"])).toEqual({ configPath: "./probes.yaml" });
});
test("未提供参数抛出错误", () => {
expect(() => readRuntimeConfig([])).toThrow("需要指定 YAML 配置文件路径");
});
});
describe("loadConfig", () => {
let tempDir: string;
beforeAll(async () => {
tempDir = join(tmpdir(), `gc-test-${Date.now()}`);
await mkdir(tempDir, { recursive: true });
});
afterAll(async () => {
await rm(tempDir, { force: true, recursive: true });
});
test("解析最简 HTTP 配置", async () => {
const configPath = join(tempDir, "minimal-http.yaml");
await writeFile(
configPath,
`targets:
- name: "test"
type: http
http:
url: "http://example.com"
`,
);
const config = await loadConfig(configPath);
expect(config.host).toBe("127.0.0.1");
expect(config.port).toBe(3000);
expect(config.dataDir).toBe("./data");
expect(config.maxConcurrentChecks).toBe(20);
expect(config.targets).toHaveLength(1);
const t = config.targets[0]!;
expect(t.type).toBe("http");
if (t.type === "http") {
expect(t.name).toBe("test");
expect(t.http.url).toBe("http://example.com");
expect(t.http.method).toBe("GET");
expect(t.http.headers).toEqual({});
expect(t.http.ignoreSSL).toBe(false);
expect(t.http.maxBodyBytes).toBe(104857600);
expect(t.http.maxRedirects).toBe(0);
expect(t.intervalMs).toBe(30000);
expect(t.timeoutMs).toBe(10000);
}
});
test("解析最简 command 配置", async () => {
const subdir = join(tempDir, "subdir");
await mkdir(subdir, { recursive: true });
const configPath = join(subdir, "cmd.yaml");
await writeFile(
configPath,
`targets:
- name: "check-nginx"
type: command
command:
exec: "pgrep"
args: ["nginx"]
`,
);
const config = await loadConfig(configPath);
expect(config.targets).toHaveLength(1);
const t = config.targets[0]!;
expect(t.type).toBe("command");
if (t.type === "command") {
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();
}
});
test("解析完整配置", async () => {
const configPath = join(tempDir, "full.yaml");
await writeFile(
configPath,
`server:
host: "0.0.0.0"
port: 8080
dataDir: "./my-data"
runtime:
maxConcurrentChecks: 5
defaults:
interval: "15s"
timeout: "5s"
http:
method: "POST"
headers:
Authorization: "Bearer token"
maxBodyBytes: "50MB"
command:
cwd: "/tmp"
maxOutputBytes: "10MB"
targets:
- name: "http-target"
type: http
interval: "1m"
http:
url: "http://example.com"
ignoreSSL: true
maxRedirects: 5
expect:
status: ["2xx", 301]
body:
- contains: "ok"
- name: "cmd-target"
type: command
command:
exec: "ls"
args: ["/tmp"]
expect:
exitCode: [0]
`,
);
const config = await loadConfig(configPath);
expect(config.host).toBe("0.0.0.0");
expect(config.port).toBe(8080);
expect(config.dataDir).toBe("./my-data");
expect(config.maxConcurrentChecks).toBe(5);
expect(config.targets).toHaveLength(2);
const http = config.targets[0]!;
expect(http.type).toBe("http");
if (http.type === "http") {
expect(http.http.url).toBe("http://example.com");
expect(http.http.method).toBe("POST");
expect(http.http.headers).toEqual({ Authorization: "Bearer token" });
expect(http.http.ignoreSSL).toBe(true);
expect(http.http.maxBodyBytes).toBe(52428800);
expect(http.http.maxRedirects).toBe(5);
expect(http.expect?.status).toEqual(["2xx", 301]);
expect(http.intervalMs).toBe(60000);
expect(http.timeoutMs).toBe(5000);
}
const cmd = config.targets[1]!;
expect(cmd.type).toBe("command");
if (cmd.type === "command") {
expect(cmd.command.exec).toBe("ls");
expect(cmd.command.args).toEqual(["/tmp"]);
expect(cmd.command.maxOutputBytes).toBe(10485760);
}
});
test("per-target 覆盖 defaults", async () => {
const configPath = join(tempDir, "override.yaml");
await writeFile(
configPath,
`defaults:
interval: "30s"
timeout: "10s"
http:
method: "GET"
maxBodyBytes: "10MB"
targets:
- name: "override-all"
type: http
interval: "5m"
timeout: "30s"
http:
url: "http://example.com"
method: "POST"
maxBodyBytes: "1MB"
`,
);
const config = await loadConfig(configPath);
const t = config.targets[0]!;
if (t.type === "http") {
expect(t.http.method).toBe("POST");
expect(t.intervalMs).toBe(300000);
expect(t.timeoutMs).toBe(30000);
expect(t.http.maxBodyBytes).toBe(1048576);
}
});
test("配置文件不存在抛出错误", async () => {
// eslint-disable-next-line @typescript-eslint/await-thenable
await expect(loadConfig("/nonexistent/file.yaml")).rejects.toThrow("配置文件不存在");
});
test("target 缺少 name 抛出错误", async () => {
const configPath = join(tempDir, "no-name.yaml");
await writeFile(
configPath,
`targets:
- type: http
http:
url: "http://example.com"
`,
);
// eslint-disable-next-line @typescript-eslint/await-thenable
await expect(loadConfig(configPath)).rejects.toThrow("缺少 name 字段");
});
test("target 缺少 type 抛出错误", async () => {
const configPath = join(tempDir, "no-type.yaml");
await writeFile(
configPath,
`targets:
- name: "test"
http:
url: "http://example.com"
`,
);
// eslint-disable-next-line @typescript-eslint/await-thenable
await expect(loadConfig(configPath)).rejects.toThrow("缺少 type 字段");
});
test("HTTP target 缺少 url 抛出错误", async () => {
const configPath = join(tempDir, "no-url.yaml");
await writeFile(
configPath,
`targets:
- name: "test"
type: http
http: {}
`,
);
// eslint-disable-next-line @typescript-eslint/await-thenable
await expect(loadConfig(configPath)).rejects.toThrow("缺少 http.url 字段");
});
test("HTTP target 缺少 http 分组抛出清晰错误", async () => {
const configPath = join(tempDir, "no-http-group.yaml");
await writeFile(
configPath,
`targets:
- name: "test"
type: http
`,
);
// eslint-disable-next-line @typescript-eslint/await-thenable
await expect(loadConfig(configPath)).rejects.toThrow("缺少 http.url 字段");
});
test("HTTP target ignoreSSL 非布尔值抛出错误", async () => {
const configPath = join(tempDir, "bad-ignore-ssl.yaml");
await writeFile(
configPath,
`targets:
- name: "test"
type: http
http:
url: "http://example.com"
ignoreSSL: "true"
`,
);
// eslint-disable-next-line @typescript-eslint/await-thenable
await expect(loadConfig(configPath)).rejects.toThrow("ignoreSSL 必须为布尔值");
});
test("HTTP target maxRedirects 非负整数校验", async () => {
const configPath = join(tempDir, "bad-max-redirects.yaml");
await writeFile(
configPath,
`targets:
- name: "test"
type: http
http:
url: "http://example.com"
maxRedirects: 1.5
`,
);
// eslint-disable-next-line @typescript-eslint/await-thenable
await expect(loadConfig(configPath)).rejects.toThrow("maxRedirects 必须为非负整数");
});
test("HTTP target status 模式非法抛出错误", async () => {
const configPath = join(tempDir, "bad-status-pattern.yaml");
await writeFile(
configPath,
`targets:
- name: "test"
type: http
http:
url: "http://example.com"
expect:
status: ["abc"]
`,
);
// eslint-disable-next-line @typescript-eslint/await-thenable
await expect(loadConfig(configPath)).rejects.toThrow("status 模式");
});
test("command target 缺少 exec 抛出错误", async () => {
const configPath = join(tempDir, "no-exec.yaml");
await writeFile(
configPath,
`targets:
- name: "test"
type: command
command: {}
`,
);
// eslint-disable-next-line @typescript-eslint/await-thenable
await expect(loadConfig(configPath)).rejects.toThrow("缺少 command.exec 字段");
});
test("非法 target type 抛出错误", async () => {
const configPath = join(tempDir, "bad-type.yaml");
await writeFile(
configPath,
`targets:
- name: "test"
type: dns
`,
);
// eslint-disable-next-line @typescript-eslint/await-thenable
await expect(loadConfig(configPath)).rejects.toThrow("不支持的 type");
});
test("target name 重复抛出错误", async () => {
const configPath = join(tempDir, "dup-name.yaml");
await writeFile(
configPath,
`targets:
- name: "dup"
type: http
http:
url: "http://a.com"
- name: "dup"
type: http
http:
url: "http://b.com"
`,
);
// eslint-disable-next-line @typescript-eslint/await-thenable
await expect(loadConfig(configPath)).rejects.toThrow("target name 重复");
});
test("targets 为空数组抛出错误", async () => {
const configPath = join(tempDir, "empty-targets.yaml");
await writeFile(configPath, `targets: []`);
// eslint-disable-next-line @typescript-eslint/await-thenable
await expect(loadConfig(configPath)).rejects.toThrow("至少一个 target");
});
test("无效端口号抛出错误", async () => {
const configPath = join(tempDir, "bad-port.yaml");
await writeFile(
configPath,
`server:
port: 99999
targets:
- name: "t"
type: http
http:
url: "http://a.com"
`,
);
// eslint-disable-next-line @typescript-eslint/await-thenable
await expect(loadConfig(configPath)).rejects.toThrow("无效端口号");
});
test("非法 maxConcurrentChecks 抛出错误", async () => {
const configPath = join(tempDir, "bad-concurrency.yaml");
await writeFile(
configPath,
`runtime:
maxConcurrentChecks: -1
targets:
- name: "t"
type: http
http:
url: "http://a.com"
`,
);
// eslint-disable-next-line @typescript-eslint/await-thenable
await expect(loadConfig(configPath)).rejects.toThrow("maxConcurrentChecks 必须为正整数");
});
test("非法 size 格式抛出错误", async () => {
const configPath = join(tempDir, "bad-size.yaml");
await writeFile(
configPath,
`defaults:
http:
maxBodyBytes: "100TB"
targets:
- name: "t"
type: http
http:
url: "http://a.com"
`,
);
// eslint-disable-next-line @typescript-eslint/await-thenable
await expect(loadConfig(configPath)).rejects.toThrow("无效的 size 格式");
});
test("非法 interval 格式抛出错误", async () => {
const configPath = join(tempDir, "bad-interval.yaml");
await writeFile(
configPath,
`targets:
- name: "t"
type: http
interval: "30x"
http:
url: "http://a.com"
`,
);
// eslint-disable-next-line @typescript-eslint/await-thenable
await expect(loadConfig(configPath)).rejects.toThrow("无效的时长格式");
});
test("解析 expect 配置", async () => {
const configPath = join(tempDir, "expect.yaml");
await writeFile(
configPath,
`targets:
- name: "with-expect"
type: http
http:
url: "http://example.com"
expect:
status: [200, 201]
body:
- contains: "ok"
- json:
path: "$.status"
equals: "ok"
maxDurationMs: 3000
`,
);
const config = await loadConfig(configPath);
const t = config.targets[0]!;
if (t.type === "http") {
expect(t.expect).toEqual({
body: [{ contains: "ok" }, { json: { equals: "ok", path: "$.status" } }],
maxDurationMs: 3000,
status: [200, 201],
});
}
});
test("解析 command expect 配置", async () => {
const configPath = join(tempDir, "cmd-expect.yaml");
await writeFile(
configPath,
`targets:
- name: "cmd-with-expect"
type: command
command:
exec: "mycheck"
expect:
exitCode: [0, 2]
stdout:
- contains: "ok"
- match: "done"
stderr:
- empty: true
maxDurationMs: 5000
`,
);
const config = await loadConfig(configPath);
const t = config.targets[0]!;
if (t.type === "command") {
expect(t.expect).toEqual({
exitCode: [0, 2],
maxDurationMs: 5000,
stderr: [{ empty: true }],
stdout: [{ contains: "ok" }, { match: "done" }],
});
}
});
test("command cwd 相对配置文件目录", async () => {
const subdir = join(tempDir, "cwd-test");
await mkdir(subdir, { recursive: true });
const configPath = join(subdir, "cwd.yaml");
await writeFile(
configPath,
`targets:
- name: "cwd-test"
type: command
command:
exec: "ls"
cwd: "scripts"
`,
);
const config = await loadConfig(configPath);
const t = config.targets[0]!;
if (t.type === "command") {
expect(t.command.cwd).toBe(join(subdir, "scripts"));
}
});
test("command env 覆盖", async () => {
const configPath = join(tempDir, "env.yaml");
await writeFile(
configPath,
`targets:
- name: "env-test"
type: command
command:
exec: "echo"
env:
LANG: "C"
CUSTOM_VAR: "test"
`,
);
const config = await loadConfig(configPath);
const t = config.targets[0]!;
if (t.type === "command") {
expect(t.command.env["LANG"]).toBe("C");
expect(t.command.env["CUSTOM_VAR"]).toBe("test");
expect(t.command.env["PATH"]).toBeDefined();
}
});
test("解析 group 字段", async () => {
const configPath = join(tempDir, "group.yaml");
await writeFile(
configPath,
`targets:
- name: "grouped"
type: http
group: "搜索引擎"
http:
url: "http://example.com"
`,
);
const config = await loadConfig(configPath);
expect(config.targets[0]!.group).toBe("搜索引擎");
});
test("group 字段默认为 default", async () => {
const configPath = join(tempDir, "no-group.yaml");
await writeFile(
configPath,
`targets:
- name: "no-group"
type: http
http:
url: "http://example.com"
`,
);
const config = await loadConfig(configPath);
expect(config.targets[0]!.group).toBe("default");
});
test("非法 group 类型抛出错误", async () => {
const configPath = join(tempDir, "bad-group.yaml");
await writeFile(
configPath,
`targets:
- name: "test"
type: http
group: 123
http:
url: "http://example.com"
`,
);
// eslint-disable-next-line @typescript-eslint/await-thenable
await expect(loadConfig(configPath)).rejects.toThrow("group 字段必须为字符串");
});
test("HTTP headers 非字符串值抛出错误", async () => {
const configPath = join(tempDir, "bad-headers-val.yaml");
await writeFile(
configPath,
`targets:
- name: "test"
type: http
http:
url: "http://example.com"
headers:
X-Custom: 123
`,
);
// eslint-disable-next-line @typescript-eslint/await-thenable
await expect(loadConfig(configPath)).rejects.toThrow("http.headers");
});
test("HTTP body 非字符串抛出错误", async () => {
const configPath = join(tempDir, "bad-body.yaml");
await writeFile(
configPath,
`targets:
- name: "test"
type: http
http:
url: "http://example.com"
body: 123
`,
);
// eslint-disable-next-line @typescript-eslint/await-thenable
await expect(loadConfig(configPath)).rejects.toThrow("http.body 必须为字符串");
});
test("maxBodyBytes 负数抛出错误", async () => {
const configPath = join(tempDir, "neg-bodybytes.yaml");
await writeFile(
configPath,
`targets:
- name: "test"
type: http
http:
url: "http://example.com"
maxBodyBytes: -1
`,
);
// eslint-disable-next-line @typescript-eslint/await-thenable
await expect(loadConfig(configPath)).rejects.toThrow("非负安全整数");
});
test("maxBodyBytes 非整数抛出错误", async () => {
const configPath = join(tempDir, "float-bodybytes.yaml");
await writeFile(
configPath,
`targets:
- name: "test"
type: http
http:
url: "http://example.com"
maxBodyBytes: 1.5
`,
);
// eslint-disable-next-line @typescript-eslint/await-thenable
await expect(loadConfig(configPath)).rejects.toThrow("非负安全整数");
});
test("expect.status 数字不在 100-599 范围抛出错误", async () => {
const configPath = join(tempDir, "bad-status-num.yaml");
await writeFile(
configPath,
`targets:
- name: "test"
type: http
http:
url: "http://example.com"
expect:
status: [999]
`,
);
// eslint-disable-next-line @typescript-eslint/await-thenable
await expect(loadConfig(configPath)).rejects.toThrow("100-599");
});
test("expect.status 范围 6xx 抛出错误", async () => {
const configPath = join(tempDir, "bad-status-6xx.yaml");
await writeFile(
configPath,
`targets:
- name: "test"
type: http
http:
url: "http://example.com"
expect:
status: ["6xx"]
`,
);
// eslint-disable-next-line @typescript-eslint/await-thenable
await expect(loadConfig(configPath)).rejects.toThrow("5xx");
});
test("expect.maxDurationMs 负数抛出错误", async () => {
const configPath = join(tempDir, "neg-duration.yaml");
await writeFile(
configPath,
`targets:
- name: "test"
type: http
http:
url: "http://example.com"
expect:
maxDurationMs: -100
`,
);
// eslint-disable-next-line @typescript-eslint/await-thenable
await expect(loadConfig(configPath)).rejects.toThrow("maxDurationMs 必须为非负有限数字");
});
test("expect.body 非数组抛出错误", async () => {
const configPath = join(tempDir, "bad-expect-body.yaml");
await writeFile(
configPath,
`targets:
- name: "test"
type: http
http:
url: "http://example.com"
expect:
body: "not-array"
`,
);
// eslint-disable-next-line @typescript-eslint/await-thenable
await expect(loadConfig(configPath)).rejects.toThrow("expect.body 必须为数组");
});
test("body rule 缺少支持字段抛出错误", async () => {
const configPath = join(tempDir, "bad-body-rule-nofield.yaml");
await writeFile(
configPath,
`targets:
- name: "test"
type: http
http:
url: "http://example.com"
expect:
body:
- foo: "bar"
`,
);
// eslint-disable-next-line @typescript-eslint/await-thenable
await expect(loadConfig(configPath)).rejects.toThrow("缺少支持的规则类型");
});
test("body rule 使用 match 字段(非支持)抛出错误", async () => {
const configPath = join(tempDir, "bad-body-rule-match.yaml");
await writeFile(
configPath,
`targets:
- name: "test"
type: http
http:
url: "http://example.com"
expect:
body:
- match: "ok"
`,
);
// eslint-disable-next-line @typescript-eslint/await-thenable
await expect(loadConfig(configPath)).rejects.toThrow("缺少支持的规则类型");
});
test("body rule 多个支持字段抛出错误", async () => {
const configPath = join(tempDir, "bad-body-rule-multi.yaml");
await writeFile(
configPath,
`targets:
- name: "test"
type: http
http:
url: "http://example.com"
expect:
body:
- contains: "ok"
regex: "ok"
`,
);
// eslint-disable-next-line @typescript-eslint/await-thenable
await expect(loadConfig(configPath)).rejects.toThrow("只能配置一种规则类型");
});
test("body regex 非法正则抛出错误", async () => {
const configPath = join(tempDir, "bad-body-regex.yaml");
await writeFile(
configPath,
`targets:
- name: "test"
type: http
http:
url: "http://example.com"
expect:
body:
- regex: "[invalid"
`,
);
// eslint-disable-next-line @typescript-eslint/await-thenable
await expect(loadConfig(configPath)).rejects.toThrow("regex 正则不合法");
});
test("body json path 不以 $. 开头抛出错误", async () => {
const configPath = join(tempDir, "bad-json-path.yaml");
await writeFile(
configPath,
`targets:
- name: "test"
type: http
http:
url: "http://example.com"
expect:
body:
- json:
path: "status"
equals: "ok"
`,
);
// eslint-disable-next-line @typescript-eslint/await-thenable
await expect(loadConfig(configPath)).rejects.toThrow("json.path");
});
test("body css selector 为空抛出错误", async () => {
const configPath = join(tempDir, "bad-css-sel.yaml");
await writeFile(
configPath,
`targets:
- name: "test"
type: http
http:
url: "http://example.com"
expect:
body:
- css:
selector: ""
`,
);
// eslint-disable-next-line @typescript-eslint/await-thenable
await expect(loadConfig(configPath)).rejects.toThrow("css.selector 必须为非空字符串");
});
test("operator match 非法正则抛出错误", async () => {
const configPath = join(tempDir, "bad-op-match.yaml");
await writeFile(
configPath,
`targets:
- name: "test"
type: http
http:
url: "http://example.com"
expect:
headers:
X-Test:
match: "[invalid"
`,
);
// eslint-disable-next-line @typescript-eslint/await-thenable
await expect(loadConfig(configPath)).rejects.toThrow("match 正则不合法");
});
test("operator gte 非数字抛出错误", async () => {
const configPath = join(tempDir, "bad-op-gte.yaml");
await writeFile(
configPath,
`targets:
- name: "test"
type: http
http:
url: "http://example.com"
expect:
body:
- json:
path: "$.count"
gte: "abc"
`,
);
// eslint-disable-next-line @typescript-eslint/await-thenable
await expect(loadConfig(configPath)).rejects.toThrow("gte 必须为有限数字");
});
test("operator exists 非布尔值抛出错误", async () => {
const configPath = join(tempDir, "bad-op-exists.yaml");
await writeFile(
configPath,
`targets:
- name: "test"
type: http
http:
url: "http://example.com"
expect:
body:
- json:
path: "$.status"
exists: "yes"
`,
);
// eslint-disable-next-line @typescript-eslint/await-thenable
await expect(loadConfig(configPath)).rejects.toThrow("exists 必须为布尔值");
});
test("未知字段忽略不影响启动", async () => {
const configPath = join(tempDir, "unknown-fields.yaml");
await writeFile(
configPath,
`targets:
- name: "test"
type: http
http:
url: "http://example.com"
unknownHttpField: "value"
expect:
status: [200]
unknownExpectField: "value"
body:
- contains: "ok"
note: "ignored"
`,
);
const config = await loadConfig(configPath);
expect(config.targets).toHaveLength(1);
const t = config.targets[0]!;
if (t.type === "http") {
expect(t.expect?.status).toEqual([200]);
}
});
test("xpath path 非空字符串校验", async () => {
const configPath = join(tempDir, "bad-xpath-path.yaml");
await writeFile(
configPath,
`targets:
- name: "test"
type: http
http:
url: "http://example.com"
expect:
body:
- xpath:
path: ""
`,
);
// eslint-disable-next-line @typescript-eslint/await-thenable
await expect(loadConfig(configPath)).rejects.toThrow("xpath.path 必须为非空字符串");
});
test("expect headers 非对象抛出错误", async () => {
const configPath = join(tempDir, "bad-expect-headers.yaml");
await writeFile(
configPath,
`targets:
- name: "test"
type: http
http:
url: "http://example.com"
expect:
headers: "invalid"
`,
);
// eslint-disable-next-line @typescript-eslint/await-thenable
await expect(loadConfig(configPath)).rejects.toThrow("expect.headers 必须为对象");
});
});