1
0
Files
DiAL/openspec/specs/expect-body-checkers/spec.md
lanyuanxiaoyao 7a635a0a9f 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
2026-05-19 14:24:27 +08:00

210 lines
12 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.
## Purpose
定义 HTTP 拨测中响应体校验方法集contains/regex/json/css/xpath、操作符系统和响应头校验的行为规范。
## Requirements
### Requirement: 响应体多种校验方法
系统 SHALL 支持通过共享 `ContentRules` 对 HTTP 响应体进行有序内容校验。`expect.body` MUST 为规则数组。每个规则 SHALL 为直接 `ValueMatcher`,或 `json``css``xpath` extractor 规则之一。直接 matcher SHALL 作用于完整响应体文本。`json` SHALL 解析响应体为 JSON 并用 JSONPath 子集提取值。`css` SHALL 使用 CSS selector 从 HTML 中提取元素文本或属性。`xpath` SHALL 使用 XPath 从 XML/HTML 中提取节点值。Extractor 未配置 matcher 时 SHALL 等价于 `exists: true`
#### Scenario: contains 子串匹配
- **WHEN** HTTP target 配置 `expect.body: [{contains: "healthy"}]`,且响应体包含 `"healthy"`
- **THEN** 系统 SHALL 判定该 body 规则通过
#### Scenario: regex 正则匹配
- **WHEN** HTTP target 配置 `expect.body: [{regex: '"status"\\s*:\\s*"ok"'}]`,且响应体匹配该正则
- **THEN** 系统 SHALL 判定该 body 规则通过
#### Scenario: json JSONPath 等值匹配
- **WHEN** HTTP target 配置 `expect.body: [{json: {path: "$.status", equals: "ok"}}]`,且响应 JSON 中 `$.status` 值为 `"ok"`
- **THEN** 系统 SHALL 判定该 body 规则通过
#### Scenario: json JSONPath 存在性匹配
- **WHEN** HTTP target 配置 `expect.body: [{json: {path: "$.status"}}]`,且响应 JSON 中存在 `$.status`
- **THEN** 系统 SHALL 将该规则按 `exists: true` 语义判定通过
#### Scenario: css 选择器匹配
- **WHEN** HTTP target 配置 `expect.body: [{css: {selector: "div#health", equals: "OK"}}]`,且 HTML 中存在 `div#health` 元素文本为 `"OK"`
- **THEN** 系统 SHALL 判定该 body 规则通过
#### Scenario: css 选择器匹配属性值
- **WHEN** HTTP target 配置 css 规则带 `attr: "content"` 用于提取属性,且属性值匹配期望
- **THEN** 系统 SHALL 判定该 body 规则通过
#### Scenario: xpath 表达式匹配
- **WHEN** HTTP target 配置 `expect.body: [{xpath: {path: "/root/status/text()", equals: "ok"}}]`,且 XML 中 `/root/status` 节点文本为 `"ok"`
- **THEN** 系统 SHALL 判定该 body 规则通过
#### Scenario: 提取器无匹配目标失败
- **WHEN** HTTP target 配置了 json、css 或 xpath 规则且对应路径、元素或节点不存在,并且规则未配置 `exists: false`
- **THEN** 系统 SHALL 判定 matched 为 false
### Requirement: 多种 body 校验方法 AND 组合
系统 SHALL 支持在 `expect.body` 数组中同时配置多条内容规则,所有规则均通过时 matched 方为 true。系统 SHALL 按数组顺序执行规则,任一规则失败后 MUST NOT 继续执行后续规则。
#### Scenario: 多种方法全部通过
- **WHEN** HTTP target 的 `expect.body` 数组依次配置 contains、json、regex且全部通过
- **THEN** 系统 SHALL 判定 matched 为 true
#### Scenario: 多种方法任一失败
- **WHEN** HTTP target 的 `expect.body` 数组第一条 contains 不通过,后续还有 json 规则
- **THEN** 系统 SHALL 判定 matched 为 false且不再检查后续 json 规则
#### Scenario: 直接 matcher 多字段组合
- **WHEN** HTTP target 配置 `expect.body: [{contains: "ok", regex: "status"}]`,且响应体同时满足 contains 和 regex
- **THEN** 系统 SHALL 判定该规则通过
### Requirement: 操作符系统
系统 SHALL 支持通过共享 `ValueMatcher` 对提取值和文本值进行比较:`equals`(深度等值)、`contains`(子串包含)、`regex`(正则匹配)、`empty`(空值判断)、`exists`(存在性判断)、`gte`/`lte`/`gt`/`lt`(数值比较)。系统 MUST NOT 支持旧 `match` 字段。
#### Scenario: equals 匹配 JSON value
- **WHEN** 配置 `{equals: {status: "ok"}}`,且实际值为相同 JSON object
- **THEN** 系统 SHALL 使用深度相等判定通过
#### Scenario: 显式 contains matcher
- **WHEN** 配置 `{contains: "success"}`,且实际值字符串化后包含 `"success"`
- **THEN** 系统 SHALL 判定该规则通过
#### Scenario: 显式 regex matcher
- **WHEN** 配置 `{regex: '\\d+\\.\\d+\\.\\d+'}`,且实际值字符串化后匹配该正则
- **THEN** 系统 SHALL 判定该规则通过
#### Scenario: empty matcher 判断为空
- **WHEN** 配置 `{empty: true}`,且实际值为空数组 `[]`
- **THEN** 系统 SHALL 判定该规则通过
#### Scenario: exists matcher 判断不存在
- **WHEN** 配置 `{exists: false}`,且实际值为 `undefined`
- **THEN** 系统 SHALL 判定该规则通过
#### Scenario: 数值比较 matcher
- **WHEN** 配置 `{gt: 0, lt: 1000}`,且实际值为 `500`
- **THEN** 系统 SHALL 对同一字段进行多 matcher 复合比较,全部通过则该规则通过
### Requirement: 响应头校验
系统 SHALL 支持通过共享 `KeyValueExpect` 配置 `expect.headers` 对 HTTP 响应头进行键值规则校验header 名称匹配 MUST 不区分大小写。header 期望值 MAY 为字符串字面量或 `ValueMatcher`。字符串字面量 SHALL 等价于 `{equals: <value>}`
#### Scenario: 响应头字面量匹配
- **WHEN** HTTP target 配置 `expect.headers: {"Content-Type": "application/json"}`,且响应包含该 header 且值精确匹配
- **THEN** 系统 SHALL 判定 headers 阶段通过
#### Scenario: 响应头 matcher 匹配
- **WHEN** HTTP target 配置 `expect.headers: {"Content-Type": {contains: "application/json"}}`,且响应 header 值包含该文本
- **THEN** 系统 SHALL 判定 headers 阶段通过
#### Scenario: 响应头缺失
- **WHEN** HTTP target 配置了某个 header 但响应中不存在该 header且未配置 `exists: false`
- **THEN** 系统 SHALL 判定 matched 为 false
### Requirement: 结构化 expect 失败信息
系统 SHALL 在任一 expect 规则失败时生成结构化 failure用于标识失败阶段、规则路径、期望值、实际值和可读错误信息。actual 值 SHALL 在构造时截断至不超过 200 字符超出部分以省略标记和总字符数替代。expected 值不截断。
#### Scenario: body 规则失败信息
- **WHEN** HTTP target 的 `expect.body[1].json` 规则失败
- **THEN** failure SHALL 包含 kind=`mismatch`、phase=`body`、path 指向 `expect.body[1]`,并包含 message
#### Scenario: actual 值截断
- **WHEN** 失败规则的实际值为字符串且长度超过 200 字符
- **THEN** failure.actual SHALL 为前 200 字符加 `…(共 N 字符)` 后缀,其中 N 为原始总字符数
#### Scenario: actual 值未超限
- **WHEN** 失败规则的实际值为字符串且长度不超过 200 字符
- **THEN** failure.actual SHALL 保留完整原始值,不做截断
#### Scenario: actual 值为对象或数组
- **WHEN** 失败规则的实际值为对象或数组,且 JSON 序列化后长度超过 200 字符
- **THEN** failure.actual SHALL 为序列化后前 200 字符加 `…(共 N 字符)` 后缀
#### Scenario: actual 值为标量
- **WHEN** 失败规则的实际值为 number、boolean、null 或 undefined
- **THEN** failure.actual SHALL 保留原始值,不做截断
### Requirement: 状态码范围匹配
系统 SHALL 支持在 `expect.status` 数组中使用范围模式字符串(`"1xx"``"2xx"``"3xx"``"4xx"``"5xx"`),与精确数字混合使用。范围模式 SHALL 匹配对应百位段内的所有状态码。其他范围模式 SHALL 在启动期配置校验失败。
#### Scenario: 1xx 范围匹配
- **WHEN** HTTP target 配置 `expect.status: ["1xx"]`,且响应状态码为 101
- **THEN** 系统 SHALL 判定状态码匹配
#### Scenario: 2xx 范围匹配
- **WHEN** HTTP target 配置 `expect.status: ["2xx"]`,且响应状态码为 200
- **THEN** 系统 SHALL 判定状态码匹配
#### Scenario: 2xx 范围匹配 204
- **WHEN** HTTP target 配置 `expect.status: ["2xx"]`,且响应状态码为 204
- **THEN** 系统 SHALL 判定状态码匹配
#### Scenario: 2xx 范围不匹配 301
- **WHEN** HTTP target 配置 `expect.status: ["2xx"]`,且响应状态码为 301
- **THEN** 系统 SHALL 判定状态码不匹配
#### Scenario: 混合精确值与范围模式
- **WHEN** HTTP target 配置 `expect.status: ["2xx", 301]`,且响应状态码为 301
- **THEN** 系统 SHALL 判定状态码匹配(精确值 301 匹配)
#### Scenario: 混合精确值与范围模式范围命中
- **WHEN** HTTP target 配置 `expect.status: ["2xx", 301]`,且响应状态码为 204
- **THEN** 系统 SHALL 判定状态码匹配2xx 范围命中)
#### Scenario: 5xx 范围匹配
- **WHEN** HTTP target 配置 `expect.status: ["5xx"]`,且响应状态码为 503
- **THEN** 系统 SHALL 判定状态码匹配
#### Scenario: 非 HTTP 范围模式启动失败
- **WHEN** HTTP target 配置 `expect.status: ["6xx"]`
- **THEN** 系统 SHALL 在启动期配置校验失败
### Requirement: HTTP expect 规则启动期校验
系统 SHALL 在启动期校验 HTTP expect 中已支持字段的类型、格式、未知字段和可编译表达式。HTTP expect SHALL 只允许 `status``headers``body``durationMs` 字段。`expect.body` MUST 为 `ContentRules` 数组。直接 `ValueMatcher` 对象 MUST 至少包含一个合法 matcher。Extractor 规则 MUST 只包含 `json``css``xpath` 中的一种 extractor。Extractor 内部可以不配置 matcher并 SHALL 在运行期以存在性作为通过语义。`equals` matcher SHALL 支持任意 JSON value包括数组和对象。系统 SHALL 在启动期对所有 `regex` 执行静态 ReDoS 检测。
#### Scenario: body rule 使用 regex 字段
- **WHEN** HTTP target 配置 `expect.body: [{regex: "ok|healthy"}]` 且 regex 可编译且无 ReDoS 风险
- **THEN** 系统 SHALL 接受该配置,并在运行期按 regex 规则匹配响应体
#### Scenario: body rule 不支持 match 字段
- **WHEN** HTTP target 配置 `expect.body: [{match: "ok"}]`
- **THEN** 系统 SHALL 在启动期配置校验失败,提示 `match` 是未知字段或不支持字段
#### Scenario: body rule 多 extractor 非法
- **WHEN** HTTP target 的同一条 body rule 同时配置 `json``css`
- **THEN** 系统 SHALL 在启动期配置校验失败
#### Scenario: matcher regex 正则非法
- **WHEN** HTTP target 的 expect.headers、body 直接 matcher 或 extractor 内部 matcher 配置了不可编译的 regex
- **THEN** 系统 SHALL 在启动期配置校验失败
#### Scenario: matcher 数值比较类型非法
- **WHEN** HTTP target 的 matcher 配置 gt、gte、lt 或 lte且对应值不是有限数字
- **THEN** 系统 SHALL 在启动期配置校验失败
#### Scenario: matcher 布尔类型非法
- **WHEN** HTTP target 的 matcher 配置 empty 或 exists且对应值不是布尔值
- **THEN** 系统 SHALL 在启动期配置校验失败
#### Scenario: JSONPath 子集非法
- **WHEN** HTTP target 的 json body rule path 不符合系统支持的 JSONPath 子集
- **THEN** 系统 SHALL 在启动期配置校验失败
#### Scenario: matcher 未知字段非法
- **WHEN** HTTP target 的 matcher 配置了 `foo: "bar"` 等未知字段
- **THEN** 系统 SHALL 在启动期配置校验失败
#### Scenario: durationMs matcher 非法
- **WHEN** HTTP target 配置 `expect.durationMs` 不是合法 `ValueMatcher` 或其中数值 matcher 不是有限数字
- **THEN** 系统 SHALL 在启动期配置校验失败
### Requirement: HTTP body 运行期失败结构化
系统 SHALL 将 HTTP body 运行期失败记录为结构化 CheckFailure并保留与具体规则相关的 phase 和 path。响应内容不符合配置 SHALL 记录为 mismatch响应内容无法按配置解析或解码 SHALL 记录为 error。
#### Scenario: JSON 响应不是合法 JSON
- **WHEN** HTTP target 配置 json body rule但响应体不是合法 JSON
- **THEN** 系统 SHALL 记录 `failure.kind="error"``failure.phase="body"`,且 failure.path SHALL 指向对应 json 规则
#### Scenario: CSS selector 无匹配元素
- **WHEN** HTTP target 配置 css body rule但响应 HTML 中无匹配元素
- **THEN** 系统 SHALL 记录 `failure.kind="mismatch"``failure.phase="body"`,且 failure.path SHALL 指向对应 css 规则
#### Scenario: XPath 无匹配节点
- **WHEN** HTTP target 配置 xpath body rule但响应 XML/HTML 中无匹配节点
- **THEN** 系统 SHALL 记录 `failure.kind="mismatch"``failure.phase="body"`,且 failure.path SHALL 指向对应 xpath 规则