## Purpose

定义 HTTP 拨测中响应体校验方法集（contains/regex/json/css/xpath）、操作符系统和响应头校验的行为规范。

## Requirements

### Requirement: 响应体多种校验方法
系统 SHALL 支持通过共享 `ContentExpectations` 对 HTTP 响应体进行有序内容校验。`expect.body` MUST 为 expectation 数组。每个 expectation SHALL 为直接 `ValueMatcher`，或 `json`、`css`、`xpath` extractor expectation 之一。直接 matcher SHALL 作用于完整响应体文本。`json` SHALL 解析响应体为 JSON 并用 JSONPath 子集提取值。`css` SHALL 使用 CSS selector 从 HTML 中提取元素文本或属性。`xpath` SHALL 使用 XPath 从 XML/HTML 中提取节点值。Extractor 未配置 matcher 时，resolve 阶段 SHALL 将其 matcher 物化为 `{exists: true}`。

#### Scenario: contains 子串匹配
- **WHEN** HTTP target 配置 `expect.body: [{contains: "healthy"}]`，且响应体包含 `"healthy"`
- **THEN** 系统 SHALL 判定该 body expectation 通过

#### Scenario: regex 正则匹配
- **WHEN** HTTP target 配置 `expect.body: [{regex: '"status"\\s*:\\s*"ok"'}]`，且响应体匹配该正则
- **THEN** 系统 SHALL 判定该 body expectation 通过

#### Scenario: json JSONPath 等值匹配
- **WHEN** HTTP target 配置 `expect.body: [{json: {path: "$.status", equals: "ok"}}]`，且响应 JSON 中 `$.status` 值为 `"ok"`
- **THEN** 系统 SHALL 判定该 body expectation 通过

#### Scenario: json JSONPath 存在性匹配
- **WHEN** HTTP target 配置 `expect.body: [{json: {path: "$.status"}}]`，且响应 JSON 中存在 `$.status`
- **THEN** resolve 阶段 SHALL 将该 expectation 按 `exists: true` 语义物化并在运行期判定通过

#### Scenario: css 选择器匹配
- **WHEN** HTTP target 配置 `expect.body: [{css: {selector: "div#health", equals: "OK"}}]`，且 HTML 中存在 `div#health` 元素文本为 `"OK"`
- **THEN** 系统 SHALL 判定该 body expectation 通过

#### Scenario: css 选择器匹配属性值
- **WHEN** HTTP target 配置 css expectation 带 `attr: "content"` 用于提取属性，且属性值匹配期望
- **THEN** 系统 SHALL 判定该 body expectation 通过

#### Scenario: xpath 表达式匹配
- **WHEN** HTTP target 配置 `expect.body: [{xpath: {path: "/root/status/text()", equals: "ok"}}]`，且 XML 中 `/root/status` 节点文本为 `"ok"`
- **THEN** 系统 SHALL 判定该 body expectation 通过

#### Scenario: 提取器无匹配目标失败
- **WHEN** HTTP target 配置了 json、css 或 xpath expectation 且对应路径、元素或节点不存在，并且 expectation 未配置 `exists: false`
- **THEN** 系统 SHALL 判定 matched 为 false

### Requirement: 多种 body 校验方法 AND 组合
系统 SHALL 支持在 `expect.body` 数组中同时配置多条内容 expectation，所有 expectation 均通过时 matched 方为 true。系统 SHALL 按数组顺序执行 expectation，任一 expectation 失败后 MUST NOT 继续执行后续 expectation。

#### Scenario: 多种方法全部通过
- **WHEN** HTTP target 的 `expect.body` 数组依次配置 contains、json、regex，且全部通过
- **THEN** 系统 SHALL 判定 matched 为 true

#### Scenario: 多种方法任一失败
- **WHEN** HTTP target 的 `expect.body` 数组第一条 contains 不通过，后续还有 json expectation
- **THEN** 系统 SHALL 判定 matched 为 false，且不再检查后续 json expectation

#### Scenario: 直接 matcher 多字段组合
- **WHEN** HTTP target 配置 `expect.body: [{contains: "ok", regex: "status"}]`，且响应体同时满足 contains 和 regex
- **THEN** 系统 SHALL 判定该 expectation 通过

### 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 支持通过共享 `KeyedExpectations` 配置 `expect.headers` 对 HTTP 响应头进行键值断言，header 名称匹配 MUST 不区分大小写。header 期望值 MUST 为 `RawValueExpectation`，primitive 字面量 SHALL 在 resolve 阶段等价为 `{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 为 `RawContentExpectations` 数组。直接 `ValueMatcher` 对象 MUST 至少包含一个合法 matcher。Extractor expectation MUST 只包含 `json`、`css`、`xpath` 中的一种 extractor。Extractor 内部可以不配置 matcher，并 SHALL 在 resolve 阶段以存在性 matcher 作为通过语义。`equals` matcher SHALL 支持任意 JSON value，包括数组和对象。系统 SHALL 在启动期对所有 `regex` 执行静态 ReDoS 检测。语义校验 MUST NOT 修改 Raw HTTP expect 输入。

#### Scenario: body rule 使用 regex 字段
- **WHEN** HTTP target 配置 `expect.body: [{regex: "ok|healthy"}]` 且 regex 可编译且无 ReDoS 风险
- **THEN** 系统 SHALL 接受该配置，并在运行期按 regex expectation 匹配响应体

#### Scenario: body rule 不支持 match 字段
- **WHEN** HTTP target 配置 `expect.body: [{match: "ok"}]`
- **THEN** 系统 SHALL 在启动期配置校验失败，提示 `match` 是未知字段或不支持字段

#### Scenario: body rule 多 extractor 非法
- **WHEN** HTTP target 的同一条 body expectation 同时配置 `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 expectation path 不符合系统支持的 JSONPath 子集
- **THEN** 系统 SHALL 在启动期配置校验失败

#### Scenario: matcher 未知字段非法
- **WHEN** HTTP target 的 matcher 配置了 `foo: "bar"` 等未知字段
- **THEN** 系统 SHALL 在启动期配置校验失败

#### Scenario: durationMs matcher 非法
- **WHEN** HTTP target 配置 `expect.durationMs` 不是合法 `RawValueExpectation` 或其中数值 matcher 不是有限数字
- **THEN** 系统 SHALL 在启动期配置校验失败

### Requirement: HTTP body 运行期失败结构化
系统 SHALL 将 HTTP body 运行期失败记录为结构化 CheckFailure，并保留与具体 expectation 相关的 phase 和 path。响应内容不符合配置 SHALL 记录为 mismatch；响应内容无法按配置解析或解码 SHALL 记录为 error。

#### Scenario: JSON 响应不是合法 JSON
- **WHEN** HTTP target 配置 json body expectation，但响应体不是合法 JSON
- **THEN** 系统 SHALL 记录 `failure.kind="error"`、`failure.phase="body"`，且 failure.path SHALL 指向对应 json expectation

#### Scenario: CSS selector 无匹配元素
- **WHEN** HTTP target 配置 css body expectation，但响应 HTML 中无匹配元素
- **THEN** 系统 SHALL 记录 `failure.kind="mismatch"`、`failure.phase="body"`，且 failure.path SHALL 指向对应 css expectation

#### Scenario: XPath 无匹配节点
- **WHEN** HTTP target 配置 xpath body expectation，但响应 XML/HTML 中无匹配节点
- **THEN** 系统 SHALL 记录 `failure.kind="mismatch"`、`failure.phase="body"`，且 failure.path SHALL 指向对应 xpath expectation
