1
0
Files
DiAL/docs/development/checker.md
lanyuanxiaoyao 77c6015b3a 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
2026-05-25 16:16:41 +08:00

222 lines
10 KiB
Markdown
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.
# Checker 开发
Checker 是 DiAL 的核心扩展单元。每个 checker 是 `src/server/checker/runner/<type>/` 下的自包含目录包含类型、schema、语义校验、执行逻辑、序列化和断言。
适用场景:新增 checker、修改 checker 配置或 expect、调整 checker 注册机制、改动 checker 测试或用户文档同步规则。
新增或修改 checker 前必须阅读 [开发入口](README.md)、[配置文件](../user/configuration.md)、[校验规则](../user/expectations.md) 和 [Checker 用户文档](../user/checkers/README.md)。还应阅读现有同类 checker 的实现和测试,例如 `src/server/checker/runner/http/``tests/server/checker/runner/http/`
## 设计原则
- 每个 checker 必须自包含在 `src/server/checker/runner/<type>/`
- checker 专属类型、schema、validate、execute、expect、normalize 和协议辅助逻辑放在同一目录。
- 注册只修改 `src/server/checker/runner/index.ts`,中间层不新增 type switch。
- schema 层只描述契约,语义规则放入 `validate.ts`
- `resolve()` 只做默认值填充、路径解析和单位转换,不执行校验。
- `execute()` 必须支持 `CheckerContext.signal` 超时取消。
- expect 字段必须选择合适断言模型,不为了统一而滥用 ValueMatcher。
- failure phase 命名遵循去单位后缀规则,例如 `durationMs` 对应 `duration`
## 架构目标
```text
checkerRegistry
├── runner/index.ts
├── schema/builder.ts
├── schema/validate.ts
├── config-loader.ts
├── engine.ts
└── store.ts
```
注册后,中间层通过 registry 自动委托 schema 生成、契约校验、配置 normalize、配置 resolve、执行和序列化。新增 checker 不应在中间层新增 `switch/case` 或类型分支。
## 标准文件结构
| 文件 | 职责 |
| -------------- | ------------------------------------------------------- |
| `index.ts` | 模块入口re-export Checker 类 |
| `types.ts` | Checker 专属类型 |
| `schema.ts` | TypeBox 契约 schema包含 config 和 expect |
| `validate.ts` | 启动期语义校验 |
| `normalize.ts` | Checker 专属 authoring expect 归一化 |
| `execute.ts` | Checker 类,实现 normalize、resolve、execute、serialize |
| `expect.ts` | Checker 专用断言函数 |
| 其他文件 | 协议解析、编码、provider 适配、平台命令封装等专属逻辑 |
## 类型定义
`types.ts` 中定义:
- `RawXxxTargetConfig`
- `RawXxxExpectConfig`
- `ResolvedXxxExpectConfig`
- `ResolvedXxxTarget extends ResolvedTargetBase`
不需要修改顶层 `checker/types.ts`。base interface 使用 index signature 支持扩展。
## Schema
checker 必须提供 `CheckerSchemas`,包含 Authoring 和 Normalized 两套 config/expect 片段。Authoring 描述用户 YAML 可写 DSLNormalized 描述 normalizer 输出。
常用 fragments
| Fragment | 用途 |
| ----------------------------------- | ------------------------- |
| `durationSchema` | 时长字符串 |
| `sizeSchema` | 大小单位 |
| `statusCodePatternSchema` | HTTP 状态码或范围 |
| `stringMapSchema` | headers、env 等字符串映射 |
| `createValueMatcherSchema()` | ValueMatcher |
| `createContentExpectationsSchema()` | ContentExpectations |
| `createKeyedExpectationsSchema()` | KeyedExpectations |
默认对象策略为 `additionalProperties: false`。只有明确的动态键值表可以开放任意键名。
## 语义校验
`validate.ts` 中实现 JSON Schema 无法表达的规则,统一返回 `ConfigValidationIssue[]`,不要直接拼接最终错误字符串。
共享校验工具包括:
| 函数 | 用途 |
| -------------------------------- | ---------------------------- |
| `validateRawValueExpectation` | 校验 Raw ValueExpectation |
| `validateRawContentExpectations` | 校验 ContentExpectations |
| `validateRawKeyedExpectations` | 校验 KeyedExpectations |
| `validateJsonPath` | 校验项目支持的 JSONPath 子集 |
| `isJsonValue` | 判断合法 JSON value |
## normalize 规范
`normalize()``CheckerDefinition` 中定义为必需方法,负责将 authoring expect DSL 转换为 normalized 形态。输入为变量已解析后的 target输出为适配 normalized schema 的 target。该方法在 `resolve()` 和 normalized contract 校验之前执行。
`normalize.ts` 中实现 `normalizeTargetExpect` 函数,`execute.ts` 中的 `normalize` 方法委托到该函数。
共享 normalize helper 位于 `src/server/checker/expect/normalize.ts`
| 函数 | 用途 |
| ------------------ | -------------------------------------------------------- |
| `compactExpect` | 合并两个 expect record过滤 undefined 字段 |
| `normalizeValue` | ValueMatcher 原始值简写展开为 `{equals: value}` |
| `normalizeContent` | ContentExpectations 简写展开为 normalized 形态 |
| `normalizeKeyed` | KeyedExpectations 对象形态展开为 `[{key, matcher}]` 数组 |
```typescript
import { compactExpect, normalizeContent, normalizeKeyed, normalizeValue } from "../../expect/normalize";
export function normalizeTargetExpect(target: RawTargetConfig): RawTargetConfig {
if (target.expect === undefined || !isPlainObject(target.expect)) return target;
const raw = target.expect as Record<string, unknown>;
return {
...target,
expect: compactExpect(raw, {
/* checker 专属字段映射 */
}),
};
}
```
expect 字段的归一化规则ValueMatcher 字段调用 `normalizeValue()`ContentExpectations 字段调用 `normalizeContent()`KeyedExpectations 字段调用 `normalizeKeyed()`boolean/enum/array 等非断言模型字段直接透传。
## resolve 规范
`resolve()` 只做内置默认值填充、路径解析、单位转换,不执行校验。输入已经通过 Normalized schema 和语义校验expect 已是 normalized 形态。
```typescript
const expect = target.expect as ResolvedXxxExpectConfig | undefined;
const resolvedExpect: ResolvedXxxExpectConfig = expect
? { ...expect, status: expect.status ?? [200] }
: { status: [200] };
```
返回值使用 `satisfies ResolvedXxxTarget` 确保类型正确。
## execute 规范
- 始终记录 `timestamp``start = performance.now()`
- 通过 `ctx.signal` 支持超时取消。
- 首个 expect 失败即停止,返回带 `failure` 的结果。
- 成功时 `failure: null, matched: true`
- 异常时使用 `errorFailure()`
- 不匹配时使用 `mismatchFailure()`
- `expected` 参数应传用户可读值,必要时使用 `displayValueExpectation()`
## expect 字段选择
| 场景 | 模型 |
| ------------------------------------ | ------------------- |
| 状态类结果且集合小而稳定 | enum 或 boolean |
| 单值数字指标或字符串元数据 | ValueMatcher |
| 文本、JSON、HTML、XML 或半结构化内容 | ContentExpectations |
| 动态键值表 | KeyedExpectations |
不要为了统一而把状态类字段改成 ValueMatcher。一个 expect 字段只能对应一种断言模型。
## 注册
1. 创建 `src/server/checker/runner/<type>/index.ts`
2.`src/server/checker/runner/index.ts` 添加导入。
3. 在 registry 初始化数组中添加 checker 实例。
注册后schema builder、validate、config-loader、engine、store 会自动按 registry 分发。
## 测试要求
测试文件放在 `tests/server/checker/runner/<type>/`,结构镜像源文件。
| 测试类别 | 覆盖内容 |
| -------------- | ---------------------------------------------------- |
| 契约测试 | TypeBox schema 与 JSON Schema 导出一致性 |
| 语义校验测试 | 合法和非法配置 |
| normalize 测试 | authoring expect 简写展开和 normalized contract 通过 |
| resolve 测试 | 默认值合并、路径解析、单位转换 |
| execute 测试 | 成功、失败、超时、expect 组合 |
| 注册测试 | registry 注册行为 |
| 配置加载测试 | 含新 checker 的 YAML 完整加载流程 |
## 文档和 schema 更新
新增或修改 checker 时通常需要更新:
- `probes.example.yaml`
- `probe-config.schema.json`,通过 `bun run schema` 生成
- `docs/user/checkers/<type>.md`
- `docs/user/checkers/README.md`
- `docs/user/expectations.md`,仅当断言模型、状态模型或通用规则变化
- `docs/user/configuration.md`,仅当 target 通用字段或配置加载形态变化
- `docs/development/checker.md`,仅当 checker 开发机制、测试要求或 checklist 变化
- `docs/README.md``openspec/config.yaml`,仅当文档同步规则变化
## 验证命令
新增或修改 checker 后通常需要运行:
```bash
bun run schema
bun run schema:check
bun run check
```
影响构建、Docker 或发布包时追加运行 `bun run verify`
## 完成检查清单
```text
□ checker 类型、schema、validate、normalize、resolve、execute、serialize 已实现
□ checker 已在 runner/index.ts 注册
□ 配置契约、语义校验和 JSON Schema 导出已同步
□ probes.example.yaml 已添加或更新示例
□ tests/server/checker/runner/<type>/ 已覆盖契约、校验、normalize、resolve、execute、注册和配置加载
□ docs/user/checkers/<type>.md 已添加或更新
□ docs/user/checkers/README.md 已添加或更新
□ 文档影响分析已完成,必要文档已同步
□ bun run schema 和 bun run schema:check 已通过
□ bun run check 已通过
□ bun run verify 已通过或记录未执行原因
```
## 更新触发条件
修改 checker 开发机制、目录结构、schema/validate/normalize/resolve/execute/expect 约定、测试要求、验证命令或文档同步 checklist 时,必须更新本文档。