1
0
Files
DiAL/tests/server/checker/fetcher.test.ts
lanyuanxiaoyao b8810f1182 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 与示例配置
2026-05-10 22:25:21 +08:00

259 lines
7.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { afterAll, beforeAll, describe, expect, test } from "bun:test";
import { runHttpCheck } from "../../../src/server/checker/fetcher";
describe("runHttpCheck", () => {
test("checkExpect 已移除", async () => {
const mod = await import("../../../src/server/checker/fetcher");
expect((mod as Record<string, unknown>).checkExpect).toBeUndefined();
expect((mod as Record<string, unknown>).fetchTarget).toBeUndefined();
});
});
describe("runHttpCheck 集成", () => {
let server: ReturnType<typeof Bun.serve>;
let baseUrl: string;
beforeAll(() => {
server = Bun.serve({
port: 0,
fetch(req) {
const url = new URL(req.url);
switch (url.pathname) {
case "/ok":
return new Response("hello world", {
headers: { "content-type": "text/plain", "x-custom": "test-value" },
});
case "/json":
return new Response(JSON.stringify({ status: "ok" }), {
headers: { "content-type": "application/json" },
});
case "/echo":
return new Response(JSON.stringify({ method: req.method, body: req.body ? "present" : "empty" }), {
headers: { "content-type": "application/json" },
});
case "/large":
return new Response("x".repeat(2000));
case "/notfound":
return new Response("not found", { status: 404 });
case "/slow":
return new Response("slow", { status: 200 });
default:
return new Response("ok");
}
},
});
baseUrl = `http://localhost:${server.port}`;
});
afterAll(() => {
server.stop();
});
function makeTarget(overrides: {
url?: string;
method?: string;
body?: string;
headers?: Record<string, string>;
expect?: Record<string, unknown>;
maxBodyBytes?: number;
timeoutMs?: number;
}) {
return {
type: "http" as const,
name: "test-http",
http: {
url: overrides.url ?? `${baseUrl}/ok`,
method: overrides.method ?? "GET",
headers: overrides.headers ?? ({} as Record<string, string>),
body: overrides.body,
maxBodyBytes: overrides.maxBodyBytes ?? 1024 * 1024,
},
intervalMs: 60000,
timeoutMs: overrides.timeoutMs ?? 5000,
expect: overrides.expect as import("../../../src/server/checker/types").HttpExpectConfig | undefined,
};
}
test("成功请求 200", async () => {
const result = await runHttpCheck(makeTarget({ url: `${baseUrl}/ok` }));
expect(result.success).toBe(true);
expect(result.matched).toBe(true);
expect(result.statusDetail).toBe("HTTP 200");
expect(result.durationMs).not.toBeNull();
expect(result.failure).toBeNull();
});
test("404 不匹配默认 status [200]", async () => {
const result = await runHttpCheck(makeTarget({ url: `${baseUrl}/notfound` }));
expect(result.success).toBe(false);
expect(result.matched).toBe(false);
expect(result.statusDetail).toBe("HTTP 404");
expect(result.failure).not.toBeNull();
expect(result.failure!.phase).toBe("status");
});
test("404 匹配自定义 status [404]", async () => {
const result = await runHttpCheck(
makeTarget({
url: `${baseUrl}/notfound`,
expect: { status: [404] },
}),
);
expect(result.success).toBe(true);
expect(result.matched).toBe(true);
});
test("headers 检查通过", async () => {
const result = await runHttpCheck(
makeTarget({
url: `${baseUrl}/ok`,
expect: { headers: { "x-custom": "test-value" } },
}),
);
expect(result.success).toBe(true);
expect(result.matched).toBe(true);
});
test("headers 检查失败", async () => {
const result = await runHttpCheck(
makeTarget({
url: `${baseUrl}/ok`,
expect: { headers: { "x-custom": "wrong-value" } },
}),
);
expect(result.success).toBe(false);
expect(result.failure!.phase).toBe("headers");
});
test("body contains 检查", async () => {
const result = await runHttpCheck(
makeTarget({
url: `${baseUrl}/ok`,
expect: { body: [{ contains: "hello" }] },
}),
);
expect(result.success).toBe(true);
expect(result.matched).toBe(true);
});
test("body contains 失败", async () => {
const result = await runHttpCheck(
makeTarget({
url: `${baseUrl}/ok`,
expect: { body: [{ contains: "nonexistent" }] },
}),
);
expect(result.success).toBe(false);
expect(result.failure!.phase).toBe("body");
});
test("body json 检查", async () => {
const result = await runHttpCheck(
makeTarget({
url: `${baseUrl}/json`,
expect: { body: [{ json: { path: "$.status", equals: "ok" } }] },
}),
);
expect(result.success).toBe(true);
});
test("响应体超过 maxBodyBytes", async () => {
const result = await runHttpCheck(
makeTarget({
url: `${baseUrl}/large`,
maxBodyBytes: 100,
expect: { body: [{ contains: "x" }] },
}),
);
expect(result.success).toBe(false);
expect(result.failure).not.toBeNull();
expect(result.failure!.phase).toBe("body");
expect(result.failure!.message).toContain("超过限制");
});
test("请求超时", async () => {
const timeoutServer = Bun.serve({
port: 0,
async fetch() {
await new Promise((resolve) => setTimeout(resolve, 10000));
return new Response("late");
},
});
try {
const result = await runHttpCheck(
makeTarget({
url: `http://localhost:${timeoutServer.port}/`,
timeoutMs: 100,
}),
);
expect(result.success).toBe(false);
expect(result.failure).not.toBeNull();
expect(result.failure!.message).toContain("超时");
} finally {
timeoutServer.stop();
}
});
test("快速失败status 失败时不读取 body", async () => {
const result = await runHttpCheck(
makeTarget({
url: `${baseUrl}/notfound`,
expect: { status: [200], body: [{ contains: "something" }] },
}),
);
expect(result.success).toBe(false);
expect(result.failure!.phase).toBe("status");
});
test("快速失败headers 失败时不读取 body", async () => {
const result = await runHttpCheck(
makeTarget({
url: `${baseUrl}/ok`,
expect: { headers: { "x-missing": "value" }, body: [{ contains: "hello" }] },
}),
);
expect(result.success).toBe(false);
expect(result.failure!.phase).toBe("headers");
});
test("status 通过但 body 失败", async () => {
const result = await runHttpCheck(
makeTarget({
url: `${baseUrl}/ok`,
expect: { status: [200], body: [{ contains: "not-in-body" }] },
}),
);
expect(result.success).toBe(false);
expect(result.failure!.phase).toBe("body");
});
test("无 expect 时默认检查 status 200", async () => {
const result = await runHttpCheck(makeTarget({ url: `${baseUrl}/ok`, expect: undefined }));
expect(result.success).toBe(true);
});
test("POST 请求携带 body", async () => {
const result = await runHttpCheck(
makeTarget({
url: `${baseUrl}/echo`,
method: "POST",
body: "test-body",
headers: { "content-type": "text/plain" },
expect: { status: [200], body: [{ json: { path: "$.body", equals: "present" } }] },
}),
);
expect(result.success).toBe(true);
});
test("仅 contains 规则时不解析 JSON", async () => {
const result = await runHttpCheck(
makeTarget({
url: `${baseUrl}/ok`,
expect: { body: [{ contains: "hello world" }] },
}),
);
expect(result.success).toBe(true);
});
});