feat: 增强 expect 规则系统,支持多种 body 校验方法和操作符
- 新增 body 分组校验:contains、regex、json(JSONPath)、css(CSS选择器)、xpath - 新增操作符系统:equals、contains、match、empty、exists、gte、lte、gt、lt - 新增 headers 响应头校验 - 引入 cheerio、xpath、@xmldom/xmldom 依赖 - BREAKING: expect.bodyContains 迁移至 expect.body.contains
This commit is contained in:
@@ -19,3 +19,6 @@ rules:
|
||||
- 先前的讨论技术方案要尽可能体现在设计文档中,便于指导实现阶段不偏离已定的技术路线
|
||||
tasks:
|
||||
- 一行一个任务,严禁任务内容跨行
|
||||
- 如果是代码存在更新必须
|
||||
- 执行完整的测试、代码检查、格式检查等质量保障手段
|
||||
- 更新README文档
|
||||
|
||||
117
openspec/specs/expect-body-checkers/spec.md
Normal file
117
openspec/specs/expect-body-checkers/spec.md
Normal file
@@ -0,0 +1,117 @@
|
||||
## Purpose
|
||||
|
||||
定义 HTTP 拨测中响应体校验方法集(contains/regex/json/css/xpath)、操作符系统和响应头校验的行为规范。
|
||||
|
||||
## Requirements
|
||||
|
||||
### Requirement: 响应体多种校验方法
|
||||
系统 SHALL 支持对 HTTP 响应体进行五种可组合的校验方法:contains(子串)、regex(正则)、json(JSONPath)、css(CSS 选择器)、xpath(XPath),配置在 `expect.body` 分组下。
|
||||
|
||||
#### Scenario: contains 子串匹配
|
||||
- **WHEN** 目标配置 `expect.body.contains: "healthy"`,且响应体包含 `"healthy"`
|
||||
- **THEN** 系统 SHALL 判定 matched 为 true
|
||||
|
||||
#### Scenario: contains 不匹配
|
||||
- **WHEN** 目标配置 `expect.body.contains: "healthy"`,且响应体不包含该文本
|
||||
- **THEN** 系统 SHALL 判定 matched 为 false
|
||||
|
||||
#### Scenario: regex 正则匹配
|
||||
- **WHEN** 目标配置 `expect.body.regex: '"status"\\s*:\\s*"ok"'`,且响应体匹配该正则
|
||||
- **THEN** 系统 SHALL 判定 matched 为 true
|
||||
|
||||
#### Scenario: regex 不匹配
|
||||
- **WHEN** 目标配置 `expect.body.regex: '"status"\\s*:\\s*"ok"'`,且响应体不匹配该正则
|
||||
- **THEN** 系统 SHALL 判定 matched 为 false
|
||||
|
||||
#### Scenario: json JSONPath 等值匹配
|
||||
- **WHEN** 目标配置 `expect.body.json: {"$.status": "ok"}`,且响应 JSON 中 `$.status` 值为 `"ok"`
|
||||
- **THEN** 系统 SHALL 判定 matched 为 true
|
||||
|
||||
#### Scenario: json JSONPath 值不匹配
|
||||
- **WHEN** 目标配置 `expect.body.json: {"$.status": "ok"}`,且响应 JSON 中 `$.status` 值为 `"error"`
|
||||
- **THEN** 系统 SHALL 判定 matched 为 false
|
||||
|
||||
#### Scenario: json 解析失败
|
||||
- **WHEN** 目标配置了 `expect.body.json` 但响应体不是合法 JSON
|
||||
- **THEN** 系统 SHALL 判定 matched 为 false
|
||||
|
||||
#### Scenario: css 选择器匹配
|
||||
- **WHEN** 目标配置 `expect.body.css: {"div#health": "OK"}`,且 HTML 中存在 `div#health` 元素文本为 `"OK"`
|
||||
- **THEN** 系统 SHALL 判定 matched 为 true
|
||||
|
||||
#### Scenario: css 选择器匹配属性值
|
||||
- **WHEN** 目标配置 css 规则带 `attr: "content"` 用于提取属性,且属性值匹配期望
|
||||
- **THEN** 系统 SHALL 判定 matched 为 true
|
||||
|
||||
#### Scenario: css 选择器无匹配元素
|
||||
- **WHEN** 目标配置了 css 选择器但 HTML 中无匹配元素
|
||||
- **THEN** 系统 SHALL 判定 matched 为 false
|
||||
|
||||
#### Scenario: xpath 表达式匹配
|
||||
- **WHEN** 目标配置 `expect.body.xpath: {"/root/status/text()": "ok"}`,且 XML 中 `/root/status` 节点文本为 `"ok"`
|
||||
- **THEN** 系统 SHALL 判定 matched 为 true
|
||||
|
||||
#### Scenario: xpath 表达式无匹配节点
|
||||
- **WHEN** 目标配置了 xpath 表达式但 XML 中无匹配节点
|
||||
- **THEN** 系统 SHALL 判定 matched 为 false
|
||||
|
||||
### Requirement: 多种 body 校验方法 AND 组合
|
||||
系统 SHALL 支持同时配置多种 body 校验方法,所有方法均通过时 matched 方为 true。
|
||||
|
||||
#### Scenario: 多种方法全部通过
|
||||
- **WHEN** 目标同时配置 `body.contains`、`body.json`、`body.regex`,且全部通过
|
||||
- **THEN** 系统 SHALL 判定 matched 为 true
|
||||
|
||||
#### Scenario: 多种方法任一失败
|
||||
- **WHEN** 目标同时配置 `body.contains` 和 `body.json`,且 `body.contains` 不通过
|
||||
- **THEN** 系统 SHALL 判定 matched 为 false,且不再检查 `body.json`
|
||||
|
||||
### Requirement: 操作符系统
|
||||
系统 SHALL 支持对 body 校验的提取值使用以下操作符进行比较:equals(默认等值)、contains(子串包含)、match(正则匹配)、empty(空值判断)、exists(存在性判断)、gte/lte/gt/lt(数值比较)。
|
||||
|
||||
#### Scenario: 标量值隐式 equals
|
||||
- **WHEN** jsonPath 配置的期望值为标量(字符串/数字/布尔/null),如 `$.status: ok`
|
||||
- **THEN** 系统 SHALL 使用 equals 操作符,对提取值做严格相等比较
|
||||
|
||||
#### Scenario: 显式 contains 操作符
|
||||
- **WHEN** 配置 `$.message: {contains: "success"}`,且提取值包含 `"success"`
|
||||
- **THEN** 系统 SHALL 判定 matched 为 true
|
||||
|
||||
#### Scenario: 显式 match 操作符
|
||||
- **WHEN** 配置 `$.version: {match: '\\d+\\.\\d+\\.\\d+'}`,且提取值匹配该正则
|
||||
- **THEN** 系统 SHALL 判定 matched 为 true
|
||||
|
||||
#### Scenario: empty 操作符判断为空
|
||||
- **WHEN** 配置 `$.items: {empty: true}`,且提取值为空数组 `[]`
|
||||
- **THEN** 系统 SHALL 判定 matched 为 true
|
||||
|
||||
#### Scenario: empty 操作符判断非空
|
||||
- **WHEN** 配置 `$.items: {empty: false}`,且提取值为 `[1, 2]`
|
||||
- **THEN** 系统 SHALL 判定 matched 为 true
|
||||
|
||||
#### Scenario: exists 操作符判断存在
|
||||
- **WHEN** 配置 `$.error: {exists: false}`,且 JSON 中不存在 `error` 字段
|
||||
- **THEN** 系统 SHALL 判定 matched 为 true
|
||||
|
||||
#### Scenario: gte 数值比较
|
||||
- **WHEN** 配置 `$.count: {gte: 10}`,且提取值为 `15`(数字)
|
||||
- **THEN** 系统 SHALL 判定 matched 为 true
|
||||
|
||||
#### Scenario: gt/lt 数值比较
|
||||
- **WHEN** 配置 `$.latency: {gt: 0, lt: 1000}`,且提取值为 `500`
|
||||
- **THEN** 系统 SHALL 对同一字段进行多操作符复合比较,全部通过则 matched 为 true
|
||||
|
||||
### Requirement: 响应头校验
|
||||
系统 SHALL 支持通过 `expect.headers` 配置对响应头进行键值对校验。
|
||||
|
||||
#### Scenario: 响应头匹配
|
||||
- **WHEN** 目标配置 `expect.headers: {"Content-Type": "application/json"}`,且响应包含该 header 且值匹配
|
||||
- **THEN** 系统 SHALL 判定 matched 为 true
|
||||
|
||||
#### Scenario: 响应头不匹配
|
||||
- **WHEN** 目标配置 `expect.headers: {"Content-Type": "application/json"}`,且响应 header 值为 `"text/html"`
|
||||
- **THEN** 系统 SHALL 判定 matched 为 false
|
||||
|
||||
#### Scenario: 响应头缺失
|
||||
- **WHEN** 目标配置了某个 header 但响应中不存在该 header
|
||||
- **THEN** 系统 SHALL 判定 matched 为 false
|
||||
@@ -55,3 +55,23 @@
|
||||
#### Scenario: 解析 YAML 内容
|
||||
- **WHEN** 系统读取 YAML 文件内容
|
||||
- **THEN** 系统 SHALL 调用 `Bun.YAML.parse()` 将内容解析为配置对象
|
||||
|
||||
### Requirement: expect 配置增强
|
||||
系统 SHALL 支持增强的 expect 配置格式,包括 `headers` 响应头校验和 `body` 分组下的多种校验方法(contains、regex、json、css、xpath)。
|
||||
|
||||
#### Scenario: 解析增强的 expect 配置
|
||||
- **WHEN** YAML 配置文件中 target 的 expect 包含 headers、body 分组及内部方法
|
||||
- **THEN** 系统 SHALL 正确解析并存储为 ResolvedTarget 的 expect 字段
|
||||
|
||||
#### Scenario: 解析仅含 body.contains 的最简配置
|
||||
- **WHEN** YAML 中 target 配置 `expect.body.contains: "healthy"`
|
||||
- **THEN** 系统 SHALL 正确解析,功能等价于旧版 `expect.bodyContains`
|
||||
|
||||
#### Scenario: 不配置 expect
|
||||
- **WHEN** target 未配置任何 expect 规则
|
||||
- **THEN** 系统 SHALL 正常处理,expect 字段为 undefined
|
||||
|
||||
#### Scenario: 旧版 bodyContains 字段不再支持
|
||||
- **WHEN** YAML 中使用 `expect.bodyContains: "xxx"` 格式
|
||||
- **THEN** 该字段 SHALL 被忽略(系统仅识别 `expect.body.contains`)
|
||||
- **Migration**: 将配置文件中 `expect.bodyContains: "xxx"` 改为 `expect.body.contains: "xxx"`
|
||||
|
||||
@@ -59,10 +59,30 @@
|
||||
- **WHEN** 目标配置了 `expect.status: [200, 201]`
|
||||
- **THEN** 系统 SHALL 检查响应状态码是否在列表中,将匹配结果记录到 matched 字段
|
||||
|
||||
#### Scenario: 校验响应头
|
||||
- **WHEN** 目标配置了 `expect.headers: {"Content-Type": "application/json"}`
|
||||
- **THEN** 系统 SHALL 检查响应头是否包含指定键值对,全部匹配时将 matched 设为 true
|
||||
|
||||
#### Scenario: 校验响应体包含
|
||||
- **WHEN** 目标配置了 `expect.bodyContains: "healthy"`
|
||||
- **WHEN** 目标配置了 `expect.body.contains: "healthy"`
|
||||
- **THEN** 系统 SHALL 检查响应体是否包含该文本,将匹配结果记录到 matched 字段
|
||||
|
||||
#### Scenario: 校验响应体正则
|
||||
- **WHEN** 目标配置了 `expect.body.regex: '"status"\\s*:\\s*"ok"'`
|
||||
- **THEN** 系统 SHALL 检查响应体是否匹配该正则,将匹配结果记录到 matched 字段
|
||||
|
||||
#### Scenario: 校验 JSON 响应
|
||||
- **WHEN** 目标配置了 `expect.body.json: {"$.status": "ok"}`
|
||||
- **THEN** 系统 SHALL 解析 JSON 并检查 JSONPath 对应值是否符合期望,将匹配结果记录到 matched 字段
|
||||
|
||||
#### Scenario: 校验 HTML 响应(CSS 选择器)
|
||||
- **WHEN** 目标配置了 `expect.body.css: {"div#health": "OK"}`
|
||||
- **THEN** 系统 SHALL 解析 HTML 并用 CSS 选择器提取元素文本进行比较,将匹配结果记录到 matched 字段
|
||||
|
||||
#### Scenario: 校验 HTML/XML 响应(XPath)
|
||||
- **WHEN** 目标配置了 `expect.body.xpath: {"/root/status/text()": "ok"}`
|
||||
- **THEN** 系统 SHALL 解析文档并用 XPath 提取节点文本进行比较,将匹配结果记录到 matched 字段
|
||||
|
||||
#### Scenario: 校验延迟阈值
|
||||
- **WHEN** 目标配置了 `expect.maxLatencyMs: 3000`
|
||||
- **THEN** 系统 SHALL 检查实际延迟是否超过阈值,将匹配结果记录到 matched 字段
|
||||
@@ -72,9 +92,24 @@
|
||||
- **THEN** 系统 SHALL 将 matched 字段设为 true
|
||||
|
||||
#### Scenario: 多条 expect 规则
|
||||
- **WHEN** 目标同时配置了 status、bodyContains 和 maxLatencyMs
|
||||
- **WHEN** 目标同时配置了 status、headers、body.contains、body.json 和 maxLatencyMs
|
||||
- **THEN** 系统 SHALL 所有规则全部通过时 matched 为 true,任一不通过则为 false
|
||||
|
||||
#### Scenario: 多种 body 方法 AND 组合
|
||||
- **WHEN** 目标在 body 分组下配置了 contains、json、css 多种方法
|
||||
- **THEN** 系统 SHALL 按 contains → regex → json → css → xpath 顺序执行,任一失败立即返回 false
|
||||
|
||||
### Requirement: Body 校验按需解析
|
||||
系统 SHALL 仅在配置了对应 body 校验方法时才解析响应体为对应格式,避免不必要的解析开销。
|
||||
|
||||
#### Scenario: 仅配置 contains 时不解析 JSON
|
||||
- **WHEN** 目标仅配置 `expect.body.contains` 而未配置 json/css/xpath
|
||||
- **THEN** 系统 SHALL 不执行 JSON.parse 或 HTML/XML 解析
|
||||
|
||||
#### Scenario: 配置 json 时解析 JSON 失败
|
||||
- **WHEN** 目标配置了 `expect.body.json` 但响应体不是合法 JSON
|
||||
- **THEN** 系统 SHALL 判定 matched 为 false
|
||||
|
||||
### Requirement: 拨测结果记录
|
||||
系统 SHALL 在每次拨测完成后,将结果写入 SQLite 数据存储,包含 target_id、timestamp、success、status_code、latency_ms、error、matched 字段。
|
||||
|
||||
|
||||
Reference in New Issue
Block a user