1
0

feat: 新增 UDP checker,支持自定义 payload 请求-响应探测与断言

基于 Bun connected UDP socket 实现通用 UDP 拨测能力:
- 支持 text/hex/base64 payload 编码与独立 responseEncoding 响应视图
- 支持 responded、response、responseSize、sourceHost、sourcePort、maxDurationMs 专属 expect
- 单 datagram 发送,仅断言首个 UDP 响应 datagram
- 通过 maxResponseBytes 和 flags.truncated 进行响应大小限制与截断保护
- payload 可选,省略时发送空 datagram
- 自包含模块结构(types/schema/validate/expect/encoding/execute)
- 新增 741 tests(含 unit、execute 集成、expect 和编码 roundtrip),全部通过
This commit is contained in:
2026-05-18 17:23:17 +08:00
parent 550c427814
commit 52262a31f6
19 changed files with 2328 additions and 8 deletions

View File

@@ -0,0 +1,120 @@
import { describe, expect, it } from "bun:test";
import {
checkResponded,
checkResponseSize,
checkResponseText,
checkSourceHost,
checkSourcePort,
} from "../../../../../src/server/checker/runner/udp/expect";
describe("checkResponded", () => {
it("responded=true 期望 true → 匹配", () => {
const result = checkResponded(true, true);
expect(result.matched).toBe(true);
expect(result.failure).toBeNull();
});
it("responded=false 期望 true → 不匹配", () => {
const result = checkResponded(false, true);
expect(result.matched).toBe(false);
expect(result.failure!.kind).toBe("mismatch");
expect(result.failure!.phase).toBe("responded");
});
it("responded=false 期望 false → 匹配", () => {
const result = checkResponded(false, false);
expect(result.matched).toBe(true);
expect(result.failure).toBeNull();
});
it("responded=true 期望 false → 不匹配", () => {
const result = checkResponded(true, false);
expect(result.matched).toBe(false);
expect(result.failure!.kind).toBe("mismatch");
expect(result.failure!.phase).toBe("responded");
});
});
describe("checkResponseSize", () => {
it("size=4 gte=4 → 匹配", () => {
const result = checkResponseSize(4, { gte: 4 });
expect(result.matched).toBe(true);
expect(result.failure).toBeNull();
});
it("size=2 gte=4 → 不匹配phase=responseSize", () => {
const result = checkResponseSize(2, { gte: 4 });
expect(result.matched).toBe(false);
expect(result.failure!.kind).toBe("mismatch");
expect(result.failure!.phase).toBe("responseSize");
});
it("size=10 lt=20 → 匹配", () => {
const result = checkResponseSize(10, { lt: 20 });
expect(result.matched).toBe(true);
});
it("size=5 equals=5 → 匹配", () => {
const result = checkResponseSize(5, { equals: 5 });
expect(result.matched).toBe(true);
});
});
describe("checkResponseText", () => {
it("单条 contains 匹配", () => {
const result = checkResponseText("PONG", [{ contains: "PONG" }]);
expect(result.matched).toBe(true);
expect(result.failure).toBeNull();
});
it("单条 contains 不匹配phase=response", () => {
const result = checkResponseText("PING", [{ contains: "PONG" }]);
expect(result.matched).toBe(false);
expect(result.failure!.kind).toBe("mismatch");
expect(result.failure!.phase).toBe("response");
});
it("多条规则全部匹配", () => {
const result = checkResponseText("hello world", [{ contains: "hello" }, { match: "^hello" }]);
expect(result.matched).toBe(true);
expect(result.failure).toBeNull();
});
it("多条规则第二条失败 → 不匹配", () => {
const result = checkResponseText("hello world", [{ contains: "hello" }, { match: "^world" }]);
expect(result.matched).toBe(false);
expect(result.failure!.phase).toBe("response");
expect(result.failure!.path).toBe("response[1]");
});
});
describe("checkSourceHost", () => {
it("equals 匹配", () => {
const result = checkSourceHost("127.0.0.1", { equals: "127.0.0.1" });
expect(result.matched).toBe(true);
expect(result.failure).toBeNull();
});
it("equals 不匹配", () => {
const result = checkSourceHost("10.0.0.1", { equals: "127.0.0.1" });
expect(result.matched).toBe(false);
expect(result.failure!.kind).toBe("mismatch");
expect(result.failure!.phase).toBe("sourceHost");
});
});
describe("checkSourcePort", () => {
it("equals 匹配", () => {
const result = checkSourcePort(9000, { equals: 9000 });
expect(result.matched).toBe(true);
expect(result.failure).toBeNull();
});
it("equals 不匹配", () => {
const result = checkSourcePort(8080, { equals: 9000 });
expect(result.matched).toBe(false);
expect(result.failure!.kind).toBe("mismatch");
expect(result.failure!.phase).toBe("sourcePort");
});
});