1
0

refactor: 统一 expect 断言体系,引入共享 ValueMatcher/ContentRules/KeyValueExpect 模型

- 引入共享 ValueMatcher(equals/contains/regex/exists/empty/gt/gte/lt/lte)
- 引入共享 ContentRules 数组(direct/json/css/xpath 提取器)
- 引入共享 KeyValueExpect(动态键值断言,字面量等价 equals)
- maxDurationMs → durationMs: ValueMatcher(所有 checker)
- match → regex(固定无 flags)
- Ping max* → packetLossPercent/avgLatencyMs/maxLatencyMs(ValueMatcher)
- LLM finishReason/rawFinishReason → ValueMatcher
- DB 新增 result: ContentRules
- TCP banner → ContentRules 数组
- 删除旧模块:operator.ts、validate-operator.ts、duration.ts、body.ts、text.ts、output.ts
- 更新全部 checker schema/validate/expect/execute
- 更新 probe-config.schema.json、probes.example.yaml
- 更新 README.md、DEVELOPMENT.md(含 expect 字段选择规范)
- 同步 10 个 delta specs 到主 specs,归档 change
This commit is contained in:
2026-05-19 14:24:27 +08:00
parent 349896bd02
commit 7a635a0a9f
85 changed files with 4290 additions and 2028 deletions

View File

@@ -26,7 +26,7 @@ describe("checkHeaders", () => {
const headers = { "content-type": "application/json" };
expect(checkHeaders(headers, { "content-type": { contains: "json" } }).matched).toBe(true);
expect(checkHeaders(headers, { "content-type": { match: "^application/" } }).matched).toBe(true);
expect(checkHeaders(headers, { "content-type": { regex: "^application/" } }).matched).toBe(true);
expect(checkHeaders(headers, { "content-type": { contains: "xml" } }).matched).toBe(false);
});

View File

@@ -487,7 +487,7 @@ describe("HttpChecker", () => {
expect(result.durationMs!).toBeGreaterThanOrEqual(0);
});
test("expect.maxDurationMs 使用完整耗时", async () => {
test("expect.durationMs 使用完整耗时", async () => {
const slowServer = Bun.serve({
async fetch() {
await new Promise((resolve) => setTimeout(resolve, 50));
@@ -498,7 +498,7 @@ describe("HttpChecker", () => {
try {
const result = await checker.execute(
makeTarget({
expect: { body: [{ contains: "x" }], maxDurationMs: 10 },
expect: { body: [{ contains: "x" }], durationMs: { lte: 10 } },
url: `http://localhost:${slowServer.port}/`,
}),
makeCtx(),
@@ -521,7 +521,7 @@ describe("HttpChecker", () => {
test("body 失败优先于 duration 检查", async () => {
const result = await checker.execute(
makeTarget({
expect: { body: [{ contains: "nonexistent" }], maxDurationMs: 999999 },
expect: { body: [{ contains: "nonexistent" }], durationMs: { lte: 999999 } },
url: `${baseUrl}/ok`,
}),
makeCtx(),
@@ -689,7 +689,7 @@ describe("HttpChecker", () => {
name: "test",
type: "http",
});
expect(errors).toContain("缺少支持的规则类型");
expect(errors).toContain("match 是未知字段");
});
test("非法 regex 启动校验失败", () => {
@@ -722,19 +722,19 @@ describe("HttpChecker", () => {
expect(errors).toContain("json.path");
});
test("非法 operator match 启动校验失败", () => {
test("旧 match matcher 启动校验失败", () => {
const errors = validateHttpTarget({
expect: { headers: { "x-test": { match: "[invalid" } } },
http: { url: "https://example.com" },
name: "test",
type: "http",
});
expect(errors).toContain("match 正则不合法");
expect(errors).toContain("match 是未知 matcher");
});
test("ReDoS operator match 启动校验失败", () => {
test("ReDoS regex matcher 启动校验失败", () => {
const errors = validateHttpTarget({
expect: { headers: { "x-test": { match: "(\\d+)*x" } } },
expect: { headers: { "x-test": { regex: "(\\d+)*x" } } },
http: { url: "https://example.com" },
name: "test",
type: "http",
@@ -769,17 +769,17 @@ describe("HttpChecker", () => {
name: "test",
type: "http",
});
expect(errors).toContain("必须包含至少一个合法 operator");
expect(errors).toContain("必须包含至少一个合法 matcher");
});
test("body rule 多个支持字段启动失败", () => {
const errors = validateHttpTarget({
expect: { body: [{ contains: "ok", regex: "ok" }] },
expect: { body: [{ contains: "ok", json: { path: "$.status" } }] },
http: { url: "https://example.com" },
name: "test",
type: "http",
});
expect(errors).toContain("只能配置一种规则类型");
expect(errors).toContain("直接 matcher 不能与 extractor 混用");
});
test("body rule 缺少支持字段启动失败", () => {
@@ -789,7 +789,7 @@ describe("HttpChecker", () => {
name: "test",
type: "http",
});
expect(errors).toContain("缺少支持的规则类型");
expect(errors).toContain("foo 是未知字段");
});
test("css selector 为空启动失败", () => {