feat: 重构配置校验为 TypeBox + Ajv + semantic validator,严格禁止未知字段
- 新增 config-contract 模块(TypeBox fragments、Ajv 契约校验、ConfigValidationIssue) - CheckerDefinition 扩展为含 configKey、schemas、validate 的完整插件接口 - HTTP/Command 各自维护 contract.ts + validate.ts,校验从 resolve 中分离 - resolve 不再承担校验,只做默认值合并和路径/单位解析 - config-loader 流程: unknown → RawProbeConfig → ValidatedProbeConfig → ResolvedConfig - 导出 probe-config.schema.json,新增 schema/schema:check 脚本 - 更新 DEVELOPMENT.md 新增 1.7 开发新 Checker 完整指引 - 同步更新 4 个 main specs(probe-config、command-checker、expect-body-checkers、checker-runner-abstraction)
This commit is contained in:
@@ -47,7 +47,13 @@
|
||||
- **THEN** 系统 SHALL 以错误退出并提示文件不存在
|
||||
|
||||
### Requirement: 配置校验
|
||||
系统 SHALL 在启动时对 YAML 配置进行完整校验,校验失败时以非零状态退出并输出清晰的错误信息。HTTP checker SHALL 对已支持字段执行严格启动期校验;未知字段 SHALL 被忽略,不触发启动失败且不影响运行行为。
|
||||
系统 SHALL 在启动时对 YAML 配置进行完整校验,校验失败时以非零状态退出并输出清晰的错误信息。系统 SHALL 使用 TypeBox 定义配置契约和 raw config TypeScript 类型,由 Ajv 校验 TypeBox 生成的 JSON Schema,再执行启动期语义 validator。配置加载流程 SHALL 明确区分 `RawProbeConfig`、`ValidatedProbeConfig`、`ResolvedConfig` 三段生命周期。JSON Schema 契约 SHALL 覆盖业务无关的结构规则,包括字段类型、必填字段、枚举、数组与对象形状、数值范围和未知字段。语义 validator SHALL 覆盖契约不适合表达的业务规则,包括 target name 唯一性、checker type 注册状态、时长和大小解析、HTTP URL、正则可编译、JSONPath 子集和 XPath 可编译。
|
||||
|
||||
契约校验和语义 validator SHALL 统一产出 `ConfigValidationIssue`,最终由配置加载流程统一渲染为中文错误信息。
|
||||
|
||||
系统 SHALL 导出完整 `probe-config.schema.json`,该文件 SHALL 与运行期 TypeBox fragments 生成的 JSON Schema 保持一致,用于用户配置引用和编辑器提示。
|
||||
|
||||
除 `headers`、`env` 等明确声明为动态键值表的对象外,配置中的未知字段 SHALL 导致启动期配置错误。系统 MUST NOT 静默忽略未知字段。
|
||||
|
||||
#### Scenario: target 缺少必填字段
|
||||
- **WHEN** YAML 中某个 target 缺少 name 或 type 字段
|
||||
@@ -62,8 +68,8 @@
|
||||
- **THEN** 系统 SHALL 以错误退出,提示该 target 缺少 command.exec 字段
|
||||
|
||||
#### Scenario: target type 非法
|
||||
- **WHEN** YAML 中某个 target 的 type 不是 `http` 或 `command`
|
||||
- **THEN** 系统 SHALL 以错误退出,提示不支持的 target type
|
||||
- **WHEN** YAML 中某个 target 的 type 不是已注册 checker 类型
|
||||
- **THEN** 系统 SHALL 以错误退出,提示不支持的 target type 和当前支持的 type 列表
|
||||
|
||||
#### Scenario: target name 重复
|
||||
- **WHEN** YAML 中存在两个 name 相同的 target
|
||||
@@ -81,16 +87,28 @@
|
||||
- **WHEN** runtime.maxConcurrentChecks 不是正整数
|
||||
- **THEN** 系统 SHALL 以错误退出并提示 runtime.maxConcurrentChecks 格式错误
|
||||
|
||||
#### Scenario: interval 或 timeout 解析结果非法
|
||||
- **WHEN** interval 或 timeout 解析结果不是正整数毫秒(如 `0ms` 或 `1.5ms`)
|
||||
- **THEN** 系统 SHALL 以错误退出并提示必须为正整数毫秒
|
||||
|
||||
#### Scenario: size 格式非法
|
||||
- **WHEN** maxBodyBytes 或 maxOutputBytes 值不是有效的 size 格式
|
||||
- **THEN** 系统 SHALL 以错误退出并提示支持 B、KB、MB、GB 格式
|
||||
|
||||
#### Scenario: size 解析结果非法
|
||||
- **WHEN** maxBodyBytes 或 maxOutputBytes 解析结果不是非负安全整数字节数(如 `1.5B`)
|
||||
- **THEN** 系统 SHALL 以错误退出并提示必须为非负安全整数字节数
|
||||
|
||||
#### Scenario: HTTP method 非法
|
||||
- **WHEN** YAML 中某个 HTTP target 的 `http.method` 不是 GET、HEAD、POST、PUT、PATCH、DELETE、OPTIONS 之一
|
||||
- **THEN** 系统 SHALL 以错误退出,提示该 target 的 method 不合法
|
||||
|
||||
#### Scenario: HTTP method 小写非法
|
||||
- **WHEN** YAML 中某个 HTTP target 配置 `http.method: get`
|
||||
- **THEN** 系统 SHALL 以错误退出,提示该 target 的 method 必须为大写枚举值
|
||||
|
||||
#### Scenario: URL 格式非法
|
||||
- **WHEN** YAML 中某个 HTTP target 的 `http.url` 不以 `http://` 或 `https://` 开头
|
||||
- **WHEN** YAML 中某个 HTTP target 的 `http.url` 不是合法 URL,或协议不是 `http:` / `https:`
|
||||
- **THEN** 系统 SHALL 以错误退出,提示该 target 的 URL 格式不合法
|
||||
|
||||
#### Scenario: maxRedirects 非法
|
||||
@@ -106,7 +124,7 @@
|
||||
- **THEN** 系统 SHALL 以错误退出,提示该 target 的 ignoreSSL 必须为布尔值
|
||||
|
||||
#### Scenario: HTTP headers 类型非法
|
||||
- **WHEN** YAML 中某个 HTTP target 的 `http.headers` 不是对象,或任一 header 名和值不能作为字符串使用
|
||||
- **WHEN** YAML 中某个 HTTP target 的 `http.headers` 不是对象,或任一 header 值不是字符串
|
||||
- **THEN** 系统 SHALL 以错误退出,提示该 target 的 http.headers 格式错误
|
||||
|
||||
#### Scenario: HTTP body 类型非法
|
||||
@@ -165,11 +183,37 @@
|
||||
- **WHEN** YAML 中某个 HTTP expect operator 的 match 不是可编译正则字符串,empty/exists 不是布尔值,或 gt/gte/lt/lte 不是有限数字
|
||||
- **THEN** 系统 SHALL 以错误退出,提示对应 operator 配置不合法
|
||||
|
||||
#### Scenario: unknown 字段忽略
|
||||
- **WHEN** YAML 中某个 HTTP target、expect 或 rule 对象包含未知字段,且所有已支持字段均合法
|
||||
- **THEN** 系统 SHALL 忽略未知字段并正常启动
|
||||
#### Scenario: expect operator 类型非法
|
||||
- **WHEN** YAML 中某个 expect operator 的 match 不是可编译正则字符串,empty/exists 不是布尔值,或 gt/gte/lt/lte 不是有限数字
|
||||
- **THEN** 系统 SHALL 以错误退出,提示对应 operator 配置不合法
|
||||
|
||||
### Requirement: size 配置解析
|
||||
#### Scenario: unknown 字段失败
|
||||
- **WHEN** YAML 中任一结构化配置对象包含契约未声明的字段,且该对象不是明确允许动态键的对象
|
||||
- **THEN** 系统 SHALL 以错误退出,提示未知字段所在路径
|
||||
|
||||
#### Scenario: 动态 headers 字段允许
|
||||
- **WHEN** YAML 中 `http.headers`、`defaults.http.headers` 或 `expect.headers` 包含任意 header 名称,且对应值符合契约
|
||||
- **THEN** 系统 SHALL 接受这些动态 header 名称
|
||||
|
||||
#### Scenario: 动态 env 字段允许
|
||||
- **WHEN** YAML 中 `command.env` 包含任意环境变量名称,且对应值为字符串
|
||||
- **THEN** 系统 SHALL 接受这些动态 env 名称
|
||||
|
||||
#### Scenario: JSON Schema 不修改输入
|
||||
- **WHEN** 系统执行 JSON Schema 契约校验
|
||||
- **THEN** 系统 MUST NOT 通过契约校验器强制转换类型、注入默认值或删除未知字段
|
||||
|
||||
#### Scenario: 配置生命周期分离
|
||||
- **WHEN** 系统加载配置文件
|
||||
- **THEN** 系统 SHALL 按 `unknown -> RawProbeConfig -> ValidatedProbeConfig -> ResolvedConfig` 的顺序执行契约校验、语义校验和运行期配置解析
|
||||
|
||||
#### Scenario: 结构化校验 issue
|
||||
- **WHEN** 契约校验或语义 validator 发现非法配置
|
||||
- **THEN** 系统 SHALL 先生成包含 code、path、message 和可选 targetName 的结构化 `ConfigValidationIssue`,再统一渲染为中文错误
|
||||
|
||||
#### Scenario: 导出配置 JSON Schema
|
||||
- **WHEN** 仓库生成或检查配置契约
|
||||
- **THEN** 根目录 SHALL 存在 draft-07 `probe-config.schema.json`,且其内容 SHALL 与当前公共 fragments 和已注册 checker fragments 组装出的完整 schema 一致
|
||||
系统 SHALL 支持使用单位字符串配置读取上限,单位包括 `B`、`KB`、`MB` 和 `GB`。
|
||||
|
||||
#### Scenario: 解析 MB
|
||||
|
||||
Reference in New Issue
Block a user