1
0
Files
DiAL/tests/server/checker/runner/http/runner.test.ts
lanyuanxiaoyao a5cf6065c2 chore: 强化代码质量与风格检查体系
ESLint 升级到 recommended-type-checked + stylistic-type-checked,
引入 perfectionist 导入排序和 import 插件导入验证。

Prettier 显式声明全部格式化参数,消除跨环境差异。
TypeScript 启用 noUnusedLocals 和 noPropertyAccessFromIndexSignature。
完善 ignore 列表,排除 .agents/、bun.lock、data/ 等。
引入 husky + lint-staged(pre-commit)+ commitlint(commit-msg)。
更新 DEVELOPMENT.md 代码质量章节。
修复所有新增规则检测到的类型和风格违规。
2026-05-12 18:44:59 +08:00

222 lines
7.1 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 type { CheckerContext } from "../../../../../src/server/checker/runner/types";
import type { ResolvedHttpTarget } from "../../../../../src/server/checker/types";
import { HttpChecker } from "../../../../../src/server/checker/runner/http/runner";
const checker = new HttpChecker();
describe("HttpChecker", () => {
let server: ReturnType<typeof Bun.serve>;
let baseUrl: string;
beforeAll(() => {
server = Bun.serve({
fetch(req) {
const url = new URL(req.url);
switch (url.pathname) {
case "/echo":
return new Response(JSON.stringify({ body: req.body ? "present" : "empty", method: req.method }), {
headers: { "content-type": "application/json" },
});
case "/json":
return new Response(JSON.stringify({ status: "ok" }), {
headers: { "content-type": "application/json" },
});
case "/large":
return new Response("x".repeat(2000));
case "/notfound":
return new Response("not found", { status: 404 });
case "/ok":
return new Response("hello world", {
headers: { "content-type": "text/plain", "x-custom": "test-value" },
});
default:
return new Response("ok");
}
},
port: 0,
});
baseUrl = `http://localhost:${server.port}`;
});
afterAll(() => {
void server.stop();
});
function makeTarget(overrides: {
body?: string;
expect?: Record<string, unknown>;
headers?: Record<string, string>;
maxBodyBytes?: number;
method?: string;
timeoutMs?: number;
url?: string;
}): ResolvedHttpTarget {
return {
expect: overrides.expect,
group: "default",
http: {
body: overrides.body,
headers: overrides.headers ?? {},
maxBodyBytes: overrides.maxBodyBytes ?? 1024 * 1024,
method: overrides.method ?? "GET",
url: overrides.url ?? `${baseUrl}/ok`,
},
intervalMs: 60000,
name: "test-http",
timeoutMs: overrides.timeoutMs ?? 5000,
type: "http",
};
}
function makeCtx(timeoutMs = 5000): CheckerContext {
const controller = new AbortController();
setTimeout(() => controller.abort(), timeoutMs);
return { signal: controller.signal };
}
test("成功请求 200", async () => {
const result = await checker.execute(makeTarget({ url: `${baseUrl}/ok` }), makeCtx());
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 checker.execute(makeTarget({ url: `${baseUrl}/notfound` }), makeCtx());
expect(result.matched).toBe(false);
expect(result.statusDetail).toBe("HTTP 404");
expect(result.failure!.phase).toBe("status");
});
test("404 匹配自定义 status [404]", async () => {
const result = await checker.execute(
makeTarget({ expect: { status: [404] }, url: `${baseUrl}/notfound` }),
makeCtx(),
);
expect(result.matched).toBe(true);
});
test("headers 检查通过", async () => {
const result = await checker.execute(
makeTarget({ expect: { headers: { "x-custom": "test-value" } }, url: `${baseUrl}/ok` }),
makeCtx(),
);
expect(result.matched).toBe(true);
});
test("headers 检查失败", async () => {
const result = await checker.execute(
makeTarget({ expect: { headers: { "x-custom": "wrong-value" } }, url: `${baseUrl}/ok` }),
makeCtx(),
);
expect(result.matched).toBe(false);
expect(result.failure!.phase).toBe("headers");
});
test("body contains 检查", async () => {
const result = await checker.execute(
makeTarget({ expect: { body: [{ contains: "hello" }] }, url: `${baseUrl}/ok` }),
makeCtx(),
);
expect(result.matched).toBe(true);
});
test("body contains 失败", async () => {
const result = await checker.execute(
makeTarget({ expect: { body: [{ contains: "nonexistent" }] }, url: `${baseUrl}/ok` }),
makeCtx(),
);
expect(result.matched).toBe(false);
expect(result.failure!.phase).toBe("body");
});
test("body json 检查", async () => {
const result = await checker.execute(
makeTarget({ expect: { body: [{ json: { equals: "ok", path: "$.status" } }] }, url: `${baseUrl}/json` }),
makeCtx(),
);
expect(result.matched).toBe(true);
});
test("响应体超过 maxBodyBytes", async () => {
const result = await checker.execute(
makeTarget({ expect: { body: [{ contains: "x" }] }, maxBodyBytes: 100, url: `${baseUrl}/large` }),
makeCtx(),
);
expect(result.matched).toBe(false);
expect(result.failure!.phase).toBe("body");
expect(result.failure!.message).toContain("超过限制");
});
test("请求超时", async () => {
const timeoutServer = Bun.serve({
async fetch() {
await new Promise((resolve) => setTimeout(resolve, 10000));
return new Response("late");
},
port: 0,
});
try {
const result = await checker.execute(
makeTarget({ timeoutMs: 100, url: `http://localhost:${timeoutServer.port}/` }),
makeCtx(100),
);
expect(result.matched).toBe(false);
expect(result.failure!.message).toContain("超时");
} finally {
void timeoutServer.stop();
}
});
test("快速失败status 失败时不读取 body", async () => {
const result = await checker.execute(
makeTarget({ expect: { body: [{ contains: "something" }], status: [200] }, url: `${baseUrl}/notfound` }),
makeCtx(),
);
expect(result.matched).toBe(false);
expect(result.failure!.phase).toBe("status");
});
test("status 通过但 body 失败", async () => {
const result = await checker.execute(
makeTarget({ expect: { body: [{ contains: "not-in-body" }], status: [200] }, url: `${baseUrl}/ok` }),
makeCtx(),
);
expect(result.matched).toBe(false);
expect(result.failure!.phase).toBe("body");
});
test("无 expect 时默认检查 status 200", async () => {
const result = await checker.execute(makeTarget({ expect: undefined, url: `${baseUrl}/ok` }), makeCtx());
expect(result.matched).toBe(true);
});
test("POST 请求携带 body", async () => {
const result = await checker.execute(
makeTarget({
body: "test-body",
expect: { body: [{ json: { equals: "present", path: "$.body" } }], status: [200] },
headers: { "content-type": "text/plain" },
method: "POST",
url: `${baseUrl}/echo`,
}),
makeCtx(),
);
expect(result.matched).toBe(true);
});
test("serialize 返回 URL 和 config JSON", () => {
const target = makeTarget({});
const s = checker.serialize(target);
expect(s.target).toBe(target.http.url);
const config = JSON.parse(s.config) as { method: string; url: string };
expect(config.url).toBe(target.http.url);
expect(config.method).toBe("GET");
});
});