1
0

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:
2026-05-10 00:10:42 +08:00
parent 57d3a5cfb4
commit 599d973cbd
22 changed files with 923 additions and 80 deletions

View File

@@ -19,3 +19,6 @@ rules:
- 先前的讨论技术方案要尽可能体现在设计文档中,便于指导实现阶段不偏离已定的技术路线
tasks:
- 一行一个任务,严禁任务内容跨行
- 如果是代码存在更新必须
- 执行完整的测试、代码检查、格式检查等质量保障手段
- 更新README文档

View File

@@ -0,0 +1,117 @@
## Purpose
定义 HTTP 拨测中响应体校验方法集contains/regex/json/css/xpath、操作符系统和响应头校验的行为规范。
## Requirements
### Requirement: 响应体多种校验方法
系统 SHALL 支持对 HTTP 响应体进行五种可组合的校验方法contains子串、regex正则、jsonJSONPath、cssCSS 选择器、xpathXPath配置在 `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

View File

@@ -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"`

View File

@@ -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 字段。