1
0

refactor: 将 checker normalize 职责下沉到各 runner 目录

- 新增 CheckerDefinition.normalize 必需方法,typecheck 兜底遗漏实现
- 新增 expect/normalize.ts 共享 helper(compactExpect、normalizeValue、
  normalizeContent、normalizeKeyed)
- 为 HTTP、Cmd、DB、TCP、UDP、ICMP、LLM、WS、DNS 各新增独立 normalize.ts
- 简化 normalizer.ts:删除所有 checker type switch,改为 registry 委托
- 修复 DNS authoring 简写 bug:durationMs、valueCount、result 等字段
  现可通过完整加载链路
- 新增 DNS 回归测试和 registry 级合同测试
- 更新 docs/development/checker.md:补充 normalize 规范、文件结构、
  测试要求和 checklist
This commit is contained in:
2026-05-25 16:16:41 +08:00
parent c1db793073
commit 77c6015b3a
26 changed files with 565 additions and 194 deletions

View File

@@ -1,6 +1,7 @@
import Ajv from "ajv";
import { describe, expect, test } from "bun:test";
import { normalizeAuthoringConfig } from "../../../../src/server/checker/normalizer";
import { createDefaultCheckerRegistry } from "../../../../src/server/checker/runner";
import { createProbeConfigJsonSchema } from "../../../../src/server/checker/schema/export";
import { formatConfigIssues, issue } from "../../../../src/server/checker/schema/issues";
@@ -274,4 +275,115 @@ describe("config contract", () => {
}),
).toBe(false);
});
test("所有 checker 的 authoring ValueMatcher 简写经 normalize 后通过 normalized contract 校验", () => {
const authoringShorthandExamples: Record<string, object> = {
cmd: {
targets: [
{
cmd: { exec: "echo hello" },
expect: { durationMs: 1000 },
id: "cmd-test",
type: "cmd",
},
],
},
db: {
targets: [
{
db: { url: "sqlite://:memory:" },
expect: { durationMs: 2000 },
id: "db-test",
type: "db",
},
],
},
dns: {
targets: [
{
dns: { name: "example.com", resolver: "system" },
expect: { durationMs: 500 },
id: "dns-test",
type: "dns",
},
],
},
http: {
targets: [
{
expect: { durationMs: 5000 },
http: { url: "https://example.com" },
id: "http-test",
type: "http",
},
],
},
icmp: {
targets: [
{
expect: { packetLossPercent: 0 },
icmp: { host: "example.com" },
id: "icmp-test",
type: "icmp",
},
],
},
llm: {
targets: [
{
expect: { durationMs: 10000 },
id: "llm-test",
llm: {
model: "gpt-4o-mini",
prompt: "ping",
provider: "openai",
url: "https://example.com/v1/chat/completions",
},
type: "llm",
},
],
},
tcp: {
targets: [
{
expect: { durationMs: 3000 },
id: "tcp-test",
tcp: { host: "example.com", port: 80 },
type: "tcp",
},
],
},
udp: {
targets: [
{
expect: { durationMs: 1000 },
id: "udp-test",
type: "udp",
udp: { host: "example.com", port: 53 },
},
],
},
ws: {
targets: [
{
expect: { durationMs: 5000 },
id: "ws-test",
type: "ws",
ws: { url: "wss://example.com/ws" },
},
],
},
};
for (const [type, config] of Object.entries(authoringShorthandExamples)) {
const normalizeResult = normalizeAuthoringConfig(config, createDefaultCheckerRegistry());
expect(normalizeResult.issues).toHaveLength(0);
const contract = validateProbeConfigContract(normalizeResult.config, createDefaultCheckerRegistry());
expect(contract.config).not.toBeNull();
expect(
contract.issues,
`Checker "${type}" authoring shorthand should pass normalized contract, got issues: ${JSON.stringify(contract.issues.map((i) => `${i.path}: ${i.message}`))}`,
).toHaveLength(0);
}
});
});