refactor: expect 类型模型重构,Raw/Resolved 双层分离与断言基础设施内聚
- 重命名 ContentRules→ContentExpectations, KeyValueExpect→KeyedExpectations - 新增 Raw/Resolved 双层模型:resolve 阶段物化为执行计划,store 持久化 Raw 快照 - HTTP body 按需读取:status/headers 失败或无 body expectation 时不读取 body - 新增 displayValueExpectation() 解包 failure.expected 用户可读展示 - 修复 checkEarlyTimeout 独立 lte/lt 检查,修复 KeyedExpectations JSON Schema - 新增 expect/value.ts(resolve/check/display)、keyed.ts、content.ts、headers.ts、status.ts - 删除旧 normalize.ts/matcher.ts/validate-matcher.ts/key-value.ts - 更新 DEVELOPMENT.md:expect 五层管线表、displayValueExpectation、1.7↔1.10 交叉引用 - 同步 13 个 main specs,归档 refactor-expect-type-model 变更(62/62 tasks)
This commit is contained in:
@@ -47,30 +47,30 @@
|
||||
- **THEN** 该 checker 专属的断言函数 SHALL 定义在 `expect.ts` 中
|
||||
|
||||
### Requirement: 断言基础设施目录
|
||||
系统 SHALL 在 `src/server/checker/expect/` 目录中提供所有 checker 共享的断言基础设施。
|
||||
系统 SHALL 在 `src/server/checker/expect/` 目录中提供所有 checker 共享的断言基础设施。共享 expect 目录 SHALL 使用 Raw/Resolved expectation 术语和 value/content/keyed/status/headers 模块边界。
|
||||
|
||||
#### Scenario: expect 共享类型位置
|
||||
- **WHEN** 任何 checker 需要使用断言相关的共享类型(如 `ExpectResult`)
|
||||
- **WHEN** 任何 checker 需要使用断言相关的共享类型(如 `ExpectationResult`、`ValueExpectation`、`ContentExpectations` 或 `KeyedExpectations`)
|
||||
- **THEN** 这些类型 SHALL 从 `src/server/checker/expect/types.ts` 导入
|
||||
|
||||
#### Scenario: operator 断言引擎位置
|
||||
- **WHEN** 任何 checker 需要使用 `applyOperator`、`evaluateJsonPath` 或 `checkExpectValue`
|
||||
- **THEN** 这些函数 SHALL 从 `src/server/checker/expect/operator.ts` 导入
|
||||
#### Scenario: value 断言引擎位置
|
||||
- **WHEN** 任何 checker 需要使用 `applyValueMatcher`、`evaluateJsonPath`、`resolveValueExpectation` 或 `checkValueExpectation`
|
||||
- **THEN** 这些函数 SHALL 从 `src/server/checker/expect/value.ts` 导入
|
||||
|
||||
#### Scenario: duration 断言位置
|
||||
- **WHEN** 任何 checker 需要使用 `checkDuration`
|
||||
- **THEN** 该函数 SHALL 从 `src/server/checker/expect/duration.ts` 导入
|
||||
#### Scenario: content 和 keyed 断言位置
|
||||
- **WHEN** 任何 checker 需要执行内容数组或键值表 expectation
|
||||
- **THEN** SHALL 分别从 `src/server/checker/expect/content.ts` 和 `src/server/checker/expect/keyed.ts` 导入共享函数
|
||||
|
||||
#### Scenario: failure 构造器位置
|
||||
- **WHEN** 任何 checker 需要使用 `errorFailure` 或 `mismatchFailure`
|
||||
- **THEN** 这些函数 SHALL 从 `src/server/checker/expect/failure.ts` 导入
|
||||
|
||||
#### Scenario: operator 校验位置
|
||||
- **WHEN** 任何 checker 的 validate 需要使用 `validateOperatorObject`
|
||||
- **THEN** 该函数 SHALL 从 `src/server/checker/expect/validate-operator.ts` 导入
|
||||
#### Scenario: expectation 校验位置
|
||||
- **WHEN** 任何 checker 的 validate 需要校验 Raw value、Raw content 或 Raw keyed expectation
|
||||
- **THEN** 对应函数 SHALL 从 `src/server/checker/expect/validate.ts` 导入
|
||||
|
||||
#### Scenario: ExpectResult 类型位置
|
||||
- **WHEN** 任何 checker 需要使用 `ExpectResult` 类型
|
||||
#### Scenario: ExpectationResult 类型位置
|
||||
- **WHEN** 任何 checker 需要使用 `ExpectationResult` 类型
|
||||
- **THEN** 该类型 SHALL 从 `src/server/checker/expect/types.ts` 导入
|
||||
|
||||
### Requirement: Schema 目录结构
|
||||
@@ -130,15 +130,15 @@ checker 系统内的模块依赖 SHALL 遵循严格的分层方向。
|
||||
- **THEN** 开发者 SHALL 在 `runner/index.ts` 中添加一行 import 和一行数组项,无需修改其他文件
|
||||
|
||||
### Requirement: 公共类型文件瘦身
|
||||
顶层 `src/server/checker/types.ts` SHALL 仅保留跨 checker 共享的 base 类型和存储相关类型。
|
||||
顶层 `src/server/checker/types.ts` SHALL 仅保留跨 checker 共享的 base 类型和存储相关类型;expect 专属类型 SHALL 放在 `src/server/checker/expect/types.ts`。
|
||||
|
||||
#### Scenario: types.ts 不包含 checker 专属类型
|
||||
#### Scenario: types.ts 不包含 checker 或 expect 专属类型
|
||||
- **WHEN** 开发者查看顶层 `types.ts`
|
||||
- **THEN** 该文件 SHALL NOT 包含 `HttpTargetConfig`、`ResolvedHttpTarget`、`CommandExpectConfig`、`BodyRule`、`TextRule` 等 checker 专属类型
|
||||
- **THEN** 该文件 SHALL NOT 包含 `HttpTargetConfig`、`ResolvedHttpTarget`、`RawCommandExpectConfig`、`ContentExpectation` 等 checker 专属或 expect 专属类型
|
||||
|
||||
#### Scenario: types.ts 保留 base 类型
|
||||
- **WHEN** 开发者查看顶层 `types.ts`
|
||||
- **THEN** 该文件 SHALL 包含 `ResolvedTargetBase`、`RawTargetConfig`、`DefaultsConfig`、`CheckResult`、`ExpectOperator`、`CheckFailure`、`StoredTarget`、`StoredCheckResult`、`JsonValue` 等公共类型
|
||||
- **THEN** 该文件 SHALL 包含 `ResolvedTargetBase`、`RawTargetConfig`、`DefaultsConfig`、`CheckResult`、`CheckFailure`、`StoredTarget`、`StoredCheckResult`、`JsonValue` 等公共类型
|
||||
|
||||
#### Scenario: ResolvedTargetBase 替代联合类型
|
||||
- **WHEN** engine、store、config-loader 需要引用 resolved target 类型
|
||||
|
||||
@@ -146,38 +146,57 @@
|
||||
- **THEN** HttpChecker 的语义校验 SHALL 抛出校验错误,提示 URL 格式不合法
|
||||
|
||||
### Requirement: 存储序列化通过 registry 获取展示格式
|
||||
系统 SHALL 在 `ProbeStore.syncTargets()` 中通过 `checkerRegistry.get(t.type).serialize(t)` 获取每个 target 的展示摘要(`target` 列)和配置 JSON(`config` 列),替代 `buildTargetDisplay()` / `buildTargetConfig()` 中的类型分支。
|
||||
系统 SHALL 在 `ProbeStore.syncTargets()` 中通过 `checkerRegistry.get(t.type).serialize(t)` 获取每个 target 的展示摘要(`target` 列)和配置 JSON(`config` 列),替代 `buildTargetDisplay()` / `buildTargetConfig()` 中的类型分支。系统 SHALL 将 `targets.expect` 持久化为 resolved target 上的 Raw expect 快照,而不是运行期 Resolved expect 执行计划。
|
||||
|
||||
#### Scenario: 序列化委托 checker
|
||||
- **WHEN** store 同步 targets 表
|
||||
- **THEN** store SHALL 对每个 target 调用对应 checker 的 `serialize()` 方法获取 `{ target, config }`
|
||||
|
||||
#### Scenario: expect 持久化使用 rawExpect
|
||||
- **WHEN** store 同步带 expect 的 target 到 targets 表
|
||||
- **THEN** store SHALL 将 `rawExpect` 序列化写入 `targets.expect`,MUST NOT 将包含 `kind` 的 Resolved content expectation 写入该列
|
||||
|
||||
### Requirement: Checker resolve 生成 Raw 与 Resolved expect
|
||||
每个 checker 的 `resolve()` SHALL 在解析 checker 专属 target 配置时,同时保留变量替换后的 Raw expect 快照并生成运行期 Resolved expect 执行计划。Raw expect SHALL 用于配置快照持久化;Resolved expect SHALL 用于 checker `execute()`。`config-loader` SHALL 继续通过 registry 委托 checker resolve,MUST NOT 在中间层理解 checker 专属 expect 字段。
|
||||
|
||||
#### Scenario: resolve 输出双 expect 模型
|
||||
- **WHEN** config-loader 解析一个带 `expect.durationMs: 1000` 的 target
|
||||
- **THEN** 对应 checker 的 resolved target SHALL 包含 Raw expect 中的 `durationMs: 1000`,并在 Resolved expect 中包含 `{equals: 1000}` 形式的运行期 matcher
|
||||
|
||||
#### Scenario: 中间层不感知 checker expect 字段
|
||||
- **WHEN** 新增 checker 定义自己的 Raw/Resolved expect 字段
|
||||
- **THEN** config-loader SHALL 只调用该 checker 的 `validate()` 和 `resolve()`,不新增 checker 类型分支
|
||||
|
||||
### Requirement: 共享 expect 断言函数
|
||||
系统 SHALL 在 `src/server/checker/expect/` 中提供可被多个 checker 复用的 expect 基础设施。共享基础设施 SHALL 包含 matcher、content rules、key-value expect、failure 构造和 ReDoS 校验。checker 专用的状态类断言 SHALL 保留在对应 checker 目录,或在多个 checker 复用时移动到共享模块。仅被单个 checker 使用且不属于通用 matcher/content/key-value 模型的断言模块 SHALL 位于该 checker 目录内。
|
||||
系统 SHALL 在 `src/server/checker/expect/` 中提供可被多个 checker 复用的 expect 基础设施。共享基础设施 SHALL 包含 value expectation、content expectations、keyed expectations、status code 断言、headers keyed 断言、failure 构造和 ReDoS 校验。checker 专用的状态类断言 SHALL 保留在对应 checker 目录,或在多个 checker 复用时移动到共享模块。仅被单个 checker 使用且不属于通用 value/content/keyed/status/header 模型的断言模块 SHALL 位于该 checker 目录内。
|
||||
|
||||
#### Scenario: 共享 ValueMatcher 断言
|
||||
#### Scenario: 共享 ValueExpectation 断言
|
||||
- **WHEN** 任何 checker 需要对数字、字符串、布尔或 JSON value 执行 matcher 匹配
|
||||
- **THEN** SHALL 调用共享 matcher 工具执行 `equals`、`contains`、`regex`、`exists`、`empty`、`gt`、`gte`、`lt` 和 `lte` 语义
|
||||
- **THEN** SHALL 调用共享 value expectation 工具执行 `equals`、`contains`、`regex`、`exists`、`empty`、`gt`、`gte`、`lt` 和 `lte` 语义
|
||||
|
||||
#### Scenario: 共享 ContentRules 断言
|
||||
#### Scenario: 共享 ContentExpectations 断言
|
||||
- **WHEN** HTTP body、LLM output、Cmd stdout/stderr、UDP response 或 TCP banner 需要执行返回内容校验
|
||||
- **THEN** SHALL 调用共享 content rules 工具,而不是在 checker 目录内复制 contains/regex/json/css/xpath 逻辑
|
||||
- **THEN** SHALL 调用共享 content expectations 工具,而不是在 checker 目录内复制 contains/regex/json/css/xpath 逻辑
|
||||
|
||||
#### Scenario: 共享 KeyValueExpect 断言
|
||||
#### Scenario: 共享 KeyedExpectations 断言
|
||||
- **WHEN** HTTP 或 LLM checker 需要校验响应 headers,或 DB checker 需要校验 rows 中的列值
|
||||
- **THEN** SHALL 调用共享 key-value expect 工具,并按调用方规则决定 key 是否大小写敏感
|
||||
- **THEN** SHALL 调用共享 keyed expectations 工具,并按调用方规则决定 key 是否大小写敏感
|
||||
|
||||
#### Scenario: 共享 headers 断言
|
||||
- **WHEN** HTTP 或 LLM checker 需要校验响应 headers
|
||||
- **THEN** SHALL 调用共享 header expectation 包装函数,确保 header key 大小写不敏感
|
||||
|
||||
#### Scenario: 共享 regex ReDoS 校验
|
||||
- **WHEN** 任一 matcher 或 content rule 配置 `regex`
|
||||
- **WHEN** 任一 matcher 或 content expectation 配置 `regex`
|
||||
- **THEN** SHALL 调用共享 ReDoS 校验工具在启动期拒绝危险正则
|
||||
|
||||
#### Scenario: 共享 failure 构造
|
||||
- **WHEN** 任何 checker 需要构造 CheckFailure 对象
|
||||
- **THEN** SHALL 调用 `expect/failure.ts` 中的 `errorFailure()` 或 `mismatchFailure()`,并保留 actual 截断策略
|
||||
|
||||
#### Scenario: HTTP 专用 status 断言
|
||||
#### Scenario: 共享 status 断言
|
||||
- **WHEN** HTTP 或 LLM checker 需要校验响应状态码
|
||||
- **THEN** SHALL 复用同一 status 断言函数,支持精确状态码和 `1xx` 到 `5xx` 范围模式
|
||||
- **THEN** SHALL 复用共享 status code 断言函数,支持精确状态码和 `1xx` 到 `5xx` 范围模式
|
||||
|
||||
### Requirement: 超时控制由引擎注入 signal
|
||||
Checker 实现的 `execute()` MUST 使用 `ctx.signal` 感知超时,不得自行创建 `AbortController` 或 `setTimeout` 用于超时控制。Cmd checker 和 ping checker 可在 signal abort 时 `proc.kill()` 以确保子进程被终止。
|
||||
|
||||
@@ -55,11 +55,11 @@
|
||||
- **THEN** 系统 MUST 停止收集输出并终止该检查,记录 `matched=false`,并在 failure 中写入输出超限信息;observation SHALL 包含已截断输出预览和 error
|
||||
|
||||
### Requirement: cmd expect 校验
|
||||
系统 SHALL 支持 cmd 专用 expect,包括 `exitCode`、`durationMs`、`stdout` 和 `stderr`,并按 exitCode、durationMs、stdout、stderr 的阶段顺序快速失败。`exitCode` SHALL 保持有限整数数组语义,未配置时默认 `[0]`。`durationMs` SHALL 使用共享 `ValueMatcher` 校验完整命令执行耗时。`stdout` 和 `stderr` MUST 使用共享 `ContentRules` 数组,直接 matcher 作用于对应输出文本,`json` extractor SHALL 支持对 JSON CLI 输出执行 JSONPath 断言。
|
||||
系统 SHALL 支持 cmd 专用 expect,包括 `exitCode`、`durationMs`、`stdout` 和 `stderr`,并按 exitCode、durationMs、stdout、stderr 的阶段顺序快速失败。`exitCode` SHALL 保持有限整数数组语义,未配置时在 Resolved expect 中默认 `[0]`。`durationMs` SHALL 使用共享 `RawValueExpectation` 输入并在运行期使用 `ValueExpectation` 校验完整命令执行耗时。`stdout` 和 `stderr` MUST 使用共享 `RawContentExpectations` 数组输入并在运行期使用 `ContentExpectations`,直接 matcher 作用于对应输出文本,`json` extractor SHALL 支持对 JSON CLI 输出执行 JSONPath 断言。
|
||||
|
||||
#### Scenario: 默认 exitCode 成功语义
|
||||
- **WHEN** cmd target 未显式配置 `expect.exitCode`
|
||||
- **THEN** 系统 SHALL 使用默认 `expect.exitCode: [0]` 进行校验
|
||||
- **THEN** 系统 SHALL 在 Resolved cmd expect 中使用默认 `exitCode: [0]` 进行校验
|
||||
|
||||
#### Scenario: 显式 exitCode 校验
|
||||
- **WHEN** cmd target 配置 `expect.exitCode: [0, 2]` 且实际 exit code 为 2
|
||||
@@ -74,8 +74,8 @@
|
||||
- **THEN** 系统 SHALL 返回 `matched=false`,failure 的 phase 为 `duration`
|
||||
|
||||
#### Scenario: stdout 按配置顺序校验
|
||||
- **WHEN** cmd target 配置 `expect.stdout` 为两个 ContentRules,第一条通过且第二条失败
|
||||
- **THEN** 系统 SHALL 先执行第一条 stdout 规则,再执行第二条,并将 failure.path 指向失败的 `stdout[1]`
|
||||
- **WHEN** cmd target 配置 `expect.stdout` 为两个 ContentExpectations,第一条通过且第二条失败
|
||||
- **THEN** 系统 SHALL 先执行第一条 stdout expectation,再执行第二条,并将 failure.path 指向失败的 `stdout[1]`
|
||||
|
||||
#### Scenario: stderr 校验为空
|
||||
- **WHEN** cmd target 配置 `expect.stderr: [{empty: true}]` 且实际 stderr 为空字符串
|
||||
@@ -86,11 +86,11 @@
|
||||
- **THEN** 系统 SHALL 判定 stdout 阶段通过
|
||||
|
||||
#### Scenario: stdout 失败后不检查 stderr
|
||||
- **WHEN** cmd target 同时配置 stdout 和 stderr 规则,且 stdout 规则失败
|
||||
- **THEN** 系统 SHALL 快速失败并 MUST NOT 继续执行 stderr 规则
|
||||
- **WHEN** cmd target 同时配置 stdout 和 stderr expectation,且 stdout expectation 失败
|
||||
- **THEN** 系统 SHALL 快速失败并 MUST NOT 继续执行 stderr expectation
|
||||
|
||||
### Requirement: cmd checker 启动期配置校验
|
||||
系统 SHALL 在启动期对 cmd checker 的配置契约和语义执行严格校验。Cmd target 的 `cmd` 分组 SHALL 只允许 `exec`、`args`、`cwd`、`env`、`maxOutputBytes` 字段。Cmd expect SHALL 只允许 `exitCode`、`durationMs`、`stdout`、`stderr` 字段。未知字段、非法类型、不可编译正则和 ReDoS 风险正则 MUST 导致启动期配置错误。`expect.exitCode` SHALL 保留原有有限整数数组语义,不限制到特定平台范围。
|
||||
系统 SHALL 在启动期对 cmd checker 的配置契约和语义执行严格校验。Cmd target 的 `cmd` 分组 SHALL 只允许 `exec`、`args`、`cwd`、`env`、`maxOutputBytes` 字段。Cmd expect SHALL 只允许 `exitCode`、`durationMs`、`stdout`、`stderr` 字段。未知字段、非法类型、不可编译正则和 ReDoS 风险正则 MUST 导致启动期配置错误。`expect.exitCode` SHALL 保留原有有限整数数组语义,不限制到特定平台范围。语义校验 MUST NOT 修改 Raw cmd expect 输入。
|
||||
|
||||
#### Scenario: cmd args 类型非法
|
||||
- **WHEN** YAML 中 cmd target 配置 `cmd.args` 不是字符串数组
|
||||
@@ -105,24 +105,24 @@
|
||||
- **THEN** 系统 SHALL 以配置错误退出,提示 expect.exitCode 必须为整数数组
|
||||
|
||||
#### Scenario: cmd expect durationMs 非法
|
||||
- **WHEN** YAML 中 cmd target 配置 `expect.durationMs` 不是合法 `ValueMatcher`
|
||||
- **WHEN** YAML 中 cmd target 配置 `expect.durationMs` 不是合法 `RawValueExpectation`
|
||||
- **THEN** 系统 SHALL 以配置错误退出,提示 expect.durationMs 格式错误
|
||||
|
||||
#### Scenario: stdout 必须为 ContentRules 数组
|
||||
#### Scenario: stdout 必须为 ContentExpectations 数组
|
||||
- **WHEN** YAML 中 cmd target 配置 `expect.stdout` 但其值不是数组
|
||||
- **THEN** 系统 SHALL 以配置错误退出,提示 expect.stdout 必须为数组
|
||||
|
||||
#### Scenario: stderr 必须为 ContentRules 数组
|
||||
#### Scenario: stderr 必须为 ContentExpectations 数组
|
||||
- **WHEN** YAML 中 cmd target 配置 `expect.stderr` 但其值不是数组
|
||||
- **THEN** 系统 SHALL 以配置错误退出,提示 expect.stderr 必须为数组
|
||||
|
||||
#### Scenario: stdout text rule 空对象非法
|
||||
#### Scenario: stdout text expectation 空对象非法
|
||||
- **WHEN** YAML 中 cmd target 配置 `expect.stdout: [{}]`
|
||||
- **THEN** 系统 SHALL 以配置错误退出,提示 stdout 规则必须包含至少一个合法 matcher 或 extractor
|
||||
- **THEN** 系统 SHALL 以配置错误退出,提示 stdout expectation 必须包含至少一个合法 matcher 或 extractor
|
||||
|
||||
#### Scenario: stderr text rule 未知字段非法
|
||||
#### Scenario: stderr text expectation 未知字段非法
|
||||
- **WHEN** YAML 中 cmd target 配置 `expect.stderr: [{foo: "bar"}]`
|
||||
- **THEN** 系统 SHALL 以配置错误退出,提示 stderr 规则包含未知 matcher 或未知 extractor
|
||||
- **THEN** 系统 SHALL 以配置错误退出,提示 stderr expectation 包含未知 matcher 或未知 extractor
|
||||
|
||||
#### Scenario: stdout regex 正则非法
|
||||
- **WHEN** YAML 中 cmd target 配置 `expect.stdout: [{regex: "[invalid"}]`
|
||||
|
||||
@@ -75,7 +75,7 @@
|
||||
- **THEN** 系统 SHALL 立即关闭数据库连接
|
||||
|
||||
### Requirement: db expect 校验
|
||||
系统 SHALL 支持 db 专用 expect,包括 `durationMs`、`rowCount`、`rows` 和 `result`,按 durationMs、rowCount、rows、result 的阶段顺序快速失败。`durationMs` 和 `rowCount` SHALL 使用共享 `ValueMatcher`。`rows` SHALL 保留按行索引匹配列值的语义,类型为 `Array<KeyValueExpect>`(外层数组按行索引,内层每个元素为一个 `KeyValueExpect` 表达该行的列值断言),每个行规则中列值字面量等价于 `{equals: <literal>}`。`result` MUST 使用共享 `ContentRules` 数组,对查询结果对象 `{ rows, rowCount }` 执行断言。
|
||||
系统 SHALL 支持 db 专用 expect,包括 `durationMs`、`rowCount`、`rows` 和 `result`,按 durationMs、rowCount、rows、result 的阶段顺序快速失败。`durationMs` 和 `rowCount` SHALL 使用共享 `RawValueExpectation` 输入,并在 resolve 阶段转换为运行期 `ValueExpectation`。`rows` SHALL 保留按行索引匹配列值的语义,Raw 类型为 `Array<RawKeyedExpectations>`(外层数组按行索引,内层每个元素表达该行的列值断言),Resolved 类型为 `Array<KeyedExpectations>`。每个行规则中列值 primitive 字面量等价于 `{equals: <literal>}`。`result` MUST 使用共享 `RawContentExpectations` 数组输入,并在运行期以 `ContentExpectations` 对查询结果对象 `{ rows, rowCount }` 执行断言。
|
||||
|
||||
#### Scenario: durationMs 校验
|
||||
- **WHEN** db target 配置 `expect.durationMs: {lte: 3000}` 且实际执行耗时 4000ms
|
||||
@@ -95,7 +95,7 @@
|
||||
|
||||
#### Scenario: rows 按索引匹配列值字面量形式
|
||||
- **WHEN** db target 配置 `expect.rows: [{ status: "active" }]` 且查询首行 status 列值为 `"active"`
|
||||
- **THEN** 系统 SHALL 判定该行该列通过(字面量等价于 `{ equals: "active" }`)
|
||||
- **THEN** 系统 SHALL 在 resolve 阶段将该列值解析为 `{equals: "active"}` 并判定该行该列通过
|
||||
|
||||
#### Scenario: rows 只检查声明的列
|
||||
- **WHEN** db target 配置 `expect.rows: [{ cnt: { gte: 1 } }]` 且查询首行包含 cnt、name、age 三列
|
||||
@@ -122,14 +122,14 @@
|
||||
- **THEN** 系统 SHALL 按 durationMs → rowCount → rows → result 顺序校验,任一阶段失败立即返回
|
||||
|
||||
### Requirement: db checker 启动期配置校验
|
||||
系统 SHALL 在启动期对 db checker 的配置契约和语义执行严格校验。Db target 的 `db` 分组 SHALL 只允许 `url` 和 `query` 字段。Db expect SHALL 只允许 `durationMs`、`rowCount`、`rows` 和 `result` 字段。未知字段、非法 matcher、非法 ContentRules、非法 regex 和 ReDoS 风险正则 MUST 导致启动期配置错误。
|
||||
系统 SHALL 在启动期对 db checker 的配置契约和语义执行严格校验。Db target 的 `db` 分组 SHALL 只允许 `url` 和 `query` 字段。Db expect SHALL 只允许 `durationMs`、`rowCount`、`rows` 和 `result` 字段。未知字段、非法 ValueExpectation、非法 ContentExpectations、非法 regex 和 ReDoS 风险正则 MUST 导致启动期配置错误。语义校验 MUST NOT 修改 Raw db expect 输入。
|
||||
|
||||
#### Scenario: db expect durationMs 非法
|
||||
- **WHEN** YAML 中 db target 配置 `expect.durationMs` 不是合法 `ValueMatcher`
|
||||
- **WHEN** YAML 中 db target 配置 `expect.durationMs` 不是合法 `RawValueExpectation`
|
||||
- **THEN** 系统 SHALL 以配置错误退出,提示 expect.durationMs 格式错误
|
||||
|
||||
#### Scenario: db expect rowCount 非法
|
||||
- **WHEN** YAML 中 db target 配置 `expect.rowCount` 不是合法 `ValueMatcher`
|
||||
- **WHEN** YAML 中 db target 配置 `expect.rowCount` 不是合法 `RawValueExpectation`
|
||||
- **THEN** 系统 SHALL 以配置错误退出,提示 expect.rowCount 格式错误
|
||||
|
||||
#### Scenario: db expect rows 非法
|
||||
@@ -140,8 +140,12 @@
|
||||
- **WHEN** YAML 中 db target 配置 `expect.rows: [{ cnt: { foo: 1 } }]`,其中 foo 不是合法 matcher
|
||||
- **THEN** 系统 SHALL 以配置错误退出,提示 rows 中包含未知 matcher
|
||||
|
||||
#### Scenario: db expect rows 对象值必须显式 equals
|
||||
- **WHEN** YAML 中 db target 配置 `expect.rows: [{ payload: { status: "ok" } }]` 且 payload 值不是合法 matcher 对象
|
||||
- **THEN** 系统 SHALL 以配置错误退出,提示对象值必须显式写成 `{equals: {status: "ok"}}`
|
||||
|
||||
#### Scenario: db expect result 非法
|
||||
- **WHEN** YAML 中 db target 配置 `expect.result` 不是合法 ContentRules 数组
|
||||
- **WHEN** YAML 中 db target 配置 `expect.result` 不是合法 ContentExpectations 数组
|
||||
- **THEN** 系统 SHALL 以配置错误退出,提示 expect.result 格式错误
|
||||
|
||||
#### Scenario: db expect 未知字段失败
|
||||
|
||||
@@ -5,54 +5,54 @@
|
||||
## 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`。
|
||||
系统 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 规则通过
|
||||
- **THEN** 系统 SHALL 判定该 body expectation 通过
|
||||
|
||||
#### Scenario: regex 正则匹配
|
||||
- **WHEN** HTTP target 配置 `expect.body: [{regex: '"status"\\s*:\\s*"ok"'}]`,且响应体匹配该正则
|
||||
- **THEN** 系统 SHALL 判定该 body 规则通过
|
||||
- **THEN** 系统 SHALL 判定该 body expectation 通过
|
||||
|
||||
#### Scenario: json JSONPath 等值匹配
|
||||
- **WHEN** HTTP target 配置 `expect.body: [{json: {path: "$.status", equals: "ok"}}]`,且响应 JSON 中 `$.status` 值为 `"ok"`
|
||||
- **THEN** 系统 SHALL 判定该 body 规则通过
|
||||
- **THEN** 系统 SHALL 判定该 body expectation 通过
|
||||
|
||||
#### Scenario: json JSONPath 存在性匹配
|
||||
- **WHEN** HTTP target 配置 `expect.body: [{json: {path: "$.status"}}]`,且响应 JSON 中存在 `$.status`
|
||||
- **THEN** 系统 SHALL 将该规则按 `exists: true` 语义判定通过
|
||||
- **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 规则通过
|
||||
- **THEN** 系统 SHALL 判定该 body expectation 通过
|
||||
|
||||
#### Scenario: css 选择器匹配属性值
|
||||
- **WHEN** HTTP target 配置 css 规则带 `attr: "content"` 用于提取属性,且属性值匹配期望
|
||||
- **THEN** 系统 SHALL 判定该 body 规则通过
|
||||
- **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 规则通过
|
||||
- **THEN** 系统 SHALL 判定该 body expectation 通过
|
||||
|
||||
#### Scenario: 提取器无匹配目标失败
|
||||
- **WHEN** HTTP target 配置了 json、css 或 xpath 规则且对应路径、元素或节点不存在,并且规则未配置 `exists: false`
|
||||
- **WHEN** HTTP target 配置了 json、css 或 xpath expectation 且对应路径、元素或节点不存在,并且 expectation 未配置 `exists: false`
|
||||
- **THEN** 系统 SHALL 判定 matched 为 false
|
||||
|
||||
### Requirement: 多种 body 校验方法 AND 组合
|
||||
系统 SHALL 支持在 `expect.body` 数组中同时配置多条内容规则,所有规则均通过时 matched 方为 true。系统 SHALL 按数组顺序执行规则,任一规则失败后 MUST NOT 继续执行后续规则。
|
||||
系统 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 规则
|
||||
- **THEN** 系统 SHALL 判定 matched 为 false,且不再检查后续 json 规则
|
||||
- **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 判定该规则通过
|
||||
- **THEN** 系统 SHALL 判定该 expectation 通过
|
||||
|
||||
### Requirement: 操作符系统
|
||||
系统 SHALL 支持通过共享 `ValueMatcher` 对提取值和文本值进行比较:`equals`(深度等值)、`contains`(子串包含)、`regex`(正则匹配)、`empty`(空值判断)、`exists`(存在性判断)、`gte`/`lte`/`gt`/`lt`(数值比较)。系统 MUST NOT 支持旧 `match` 字段。
|
||||
@@ -82,7 +82,7 @@
|
||||
- **THEN** 系统 SHALL 对同一字段进行多 matcher 复合比较,全部通过则该规则通过
|
||||
|
||||
### Requirement: 响应头校验
|
||||
系统 SHALL 支持通过共享 `KeyValueExpect` 配置 `expect.headers` 对 HTTP 响应头进行键值规则校验,header 名称匹配 MUST 不区分大小写。header 期望值 MAY 为字符串字面量或 `ValueMatcher`。字符串字面量 SHALL 等价于 `{equals: <value>}`。
|
||||
系统 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 且值精确匹配
|
||||
@@ -155,18 +155,18 @@
|
||||
- **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 检测。
|
||||
系统 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 规则匹配响应体
|
||||
- **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 rule 同时配置 `json` 和 `css`
|
||||
- **WHEN** HTTP target 的同一条 body expectation 同时配置 `json` 和 `css`
|
||||
- **THEN** 系统 SHALL 在启动期配置校验失败
|
||||
|
||||
#### Scenario: matcher regex 正则非法
|
||||
@@ -182,7 +182,7 @@
|
||||
- **THEN** 系统 SHALL 在启动期配置校验失败
|
||||
|
||||
#### Scenario: JSONPath 子集非法
|
||||
- **WHEN** HTTP target 的 json body rule path 不符合系统支持的 JSONPath 子集
|
||||
- **WHEN** HTTP target 的 json body expectation path 不符合系统支持的 JSONPath 子集
|
||||
- **THEN** 系统 SHALL 在启动期配置校验失败
|
||||
|
||||
#### Scenario: matcher 未知字段非法
|
||||
@@ -190,20 +190,20 @@
|
||||
- **THEN** 系统 SHALL 在启动期配置校验失败
|
||||
|
||||
#### Scenario: durationMs matcher 非法
|
||||
- **WHEN** HTTP target 配置 `expect.durationMs` 不是合法 `ValueMatcher` 或其中数值 matcher 不是有限数字
|
||||
- **WHEN** HTTP target 配置 `expect.durationMs` 不是合法 `RawValueExpectation` 或其中数值 matcher 不是有限数字
|
||||
- **THEN** 系统 SHALL 在启动期配置校验失败
|
||||
|
||||
### Requirement: HTTP body 运行期失败结构化
|
||||
系统 SHALL 将 HTTP body 运行期失败记录为结构化 CheckFailure,并保留与具体规则相关的 phase 和 path。响应内容不符合配置 SHALL 记录为 mismatch;响应内容无法按配置解析或解码 SHALL 记录为 error。
|
||||
系统 SHALL 将 HTTP body 运行期失败记录为结构化 CheckFailure,并保留与具体 expectation 相关的 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 规则
|
||||
- **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 rule,但响应 HTML 中无匹配元素
|
||||
- **THEN** 系统 SHALL 记录 `failure.kind="mismatch"`、`failure.phase="body"`,且 failure.path SHALL 指向对应 css 规则
|
||||
- **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 rule,但响应 XML/HTML 中无匹配节点
|
||||
- **THEN** 系统 SHALL 记录 `failure.kind="mismatch"`、`failure.phase="body"`,且 failure.path SHALL 指向对应 xpath 规则
|
||||
- **WHEN** HTTP target 配置 xpath body expectation,但响应 XML/HTML 中无匹配节点
|
||||
- **THEN** 系统 SHALL 记录 `failure.kind="mismatch"`、`failure.phase="body"`,且 failure.path SHALL 指向对应 xpath expectation
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
## Purpose
|
||||
|
||||
定义共享 expect 断言规则系统的核心概念和基础设施:ValueMatcher 统一匹配器、ContentRules 内容规则数组、KeyValueExpect 键值规则、以及相关的启动期校验和失败路径规范。
|
||||
定义共享 expect 断言规则系统的核心概念和基础设施:ValueMatcher 统一匹配器、ContentExpectations 内容断言数组、KeyedExpectations 键控断言数组、以及相关的启动期校验和失败路径规范。
|
||||
|
||||
## Requirements
|
||||
|
||||
### Requirement: ValueMatcher 统一匹配器
|
||||
系统 SHALL 提供共享 `ValueMatcher` 作为所有非状态类 expect 的基础匹配结构。`ValueMatcher` SHALL 支持 `equals`、`contains`、`regex`、`exists`、`empty`、`gt`、`gte`、`lt` 和 `lte` 字段。`equals` MUST 支持任意 JSON value,并使用深度相等比较。`contains` 和 `regex` SHALL 将实际值转换为字符串后匹配。`gt`、`gte`、`lt` 和 `lte` SHALL 将实际值转换为有限数字后比较,无法转换为有限数字时 SHALL 判定不匹配。一个 `ValueMatcher` 对象包含多个 matcher 字段时,系统 SHALL 要求全部 matcher 均通过。
|
||||
系统 SHALL 提供共享 `ValueMatcher` 作为所有非状态类 value expectation 的基础匹配结构。`ValueMatcher` SHALL 支持 `equals`、`contains`、`regex`、`exists`、`empty`、`gt`、`gte`、`lt` 和 `lte` 字段。`equals` MUST 支持任意 JSON value,并使用深度相等比较。`contains` 和 `regex` SHALL 将实际值转换为字符串后匹配。`gt`、`gte`、`lt` 和 `lte` SHALL 将实际值转换为有限数字后比较,无法转换为有限数字时 SHALL 判定不匹配。一个 `ValueMatcher` 对象包含多个 matcher 字段时,系统 SHALL 要求全部 matcher 均通过。
|
||||
|
||||
所有类型为 `ValueMatcher` 的 expect 字段 SHALL 同时接受 primitive 原始值(string / number / boolean / null)作为简写形式。原始值简写 SHALL 等价于 `{ equals: value }`。系统 SHALL 在语义校验入口将 primitive 原始值归一化为 `{ equals: value }` 对象形式,后续 resolve 和运行期逻辑 SHALL 仅处理 ValueMatcher 对象形式。数组和对象 MUST NOT 作为原始值简写;需要对数组或对象执行 equals 匹配时,配置 MUST 显式写成 `{ equals: value }`。
|
||||
所有类型为 `RawValueExpectation` 的 expect 字段 SHALL 同时接受 primitive 原始值(string / number / boolean / null)作为简写形式。原始值简写 SHALL 等价于 `{ equals: value }`。系统 SHALL 在 resolve 阶段将 primitive 原始值归一化为 `{ equals: value }` 对象形式,运行期逻辑 SHALL 仅处理 `ValueMatcher` 对象形式。数组和对象 MUST NOT 作为原始值简写;需要对数组或对象执行 equals 匹配时,配置 MUST 显式写成 `{ equals: value }`。
|
||||
|
||||
#### Scenario: equals 匹配对象
|
||||
- **WHEN** 实际值为 `{status: "ok", count: 1}` 且 matcher 为 `{equals: {status: "ok", count: 1}}`
|
||||
@@ -27,26 +27,26 @@
|
||||
|
||||
#### Scenario: 字符串原始值简写等价 equals
|
||||
- **WHEN** expect 字段配置为 `finishReason: "stop"` 且实际值为 `"stop"`
|
||||
- **THEN** 系统 SHALL 将 `"stop"` 归一化为 `{equals: "stop"}` 并判定通过
|
||||
- **THEN** 系统 SHALL 在 resolve 阶段将 `"stop"` 归一化为 `{equals: "stop"}` 并判定通过
|
||||
|
||||
#### Scenario: 数字原始值简写等价 equals
|
||||
- **WHEN** expect 字段配置为 `rowCount: 1` 且实际值为 `1`
|
||||
- **THEN** 系统 SHALL 将 `1` 归一化为 `{equals: 1}` 并判定通过
|
||||
- **THEN** 系统 SHALL 在 resolve 阶段将 `1` 归一化为 `{equals: 1}` 并判定通过
|
||||
|
||||
#### Scenario: 布尔原始值简写等价 equals
|
||||
- **WHEN** expect 字段配置为 ValueMatcher 类型且值为 `true`,实际值为 `true`
|
||||
- **THEN** 系统 SHALL 将 `true` 归一化为 `{equals: true}` 并判定通过
|
||||
- **WHEN** expect 字段配置为 RawValueExpectation 类型且值为 `true`,实际值为 `true`
|
||||
- **THEN** 系统 SHALL 在 resolve 阶段将 `true` 归一化为 `{equals: true}` 并判定通过
|
||||
|
||||
#### Scenario: null 原始值简写等价 equals
|
||||
- **WHEN** expect 字段配置为 ValueMatcher 类型且值为 `null`,实际值为 `null`
|
||||
- **THEN** 系统 SHALL 将 `null` 归一化为 `{equals: null}` 并判定通过
|
||||
- **WHEN** expect 字段配置为 RawValueExpectation 类型且值为 `null`,实际值为 `null`
|
||||
- **THEN** 系统 SHALL 在 resolve 阶段将 `null` 归一化为 `{equals: null}` 并判定通过
|
||||
|
||||
#### Scenario: 原始值简写不匹配
|
||||
- **WHEN** expect 字段配置为 `finishReason: "stop"` 且实际值为 `"error"`
|
||||
- **THEN** 系统 SHALL 判定不通过并生成 mismatch failure
|
||||
|
||||
### Requirement: ValueMatcher 启动期校验
|
||||
系统 SHALL 在启动期对所有 `ValueMatcher` 字段执行严格的类型和语义校验。校验 SHALL 同时接受 primitive 原始值和 ValueMatcher 对象两种形式。当输入为 primitive 原始值时,系统 SHALL 视为合法配置(等价于 `{equals: value}`),无需进一步校验 matcher 字段。当输入为 ValueMatcher 对象时,`contains` MUST 为 string。`equals` MAY 为任意 JSON value。`exists` 和 `empty` MUST 为 boolean。`gt`、`gte`、`lt` 和 `lte` MUST 为有限数字(`Number.isFinite`)。`regex` MUST 为可编译的 string pattern,并通过 ReDoS 风险校验。ValueMatcher 对象 MUST 至少包含一个合法 matcher 字段,空对象 `{}` SHALL 导致启动期配置错误。ValueMatcher 对象 MUST NOT 包含未知字段,任何不属于 `equals`、`contains`、`regex`、`exists`、`empty`、`gt`、`gte`、`lt`、`lte` 的字段 SHALL 导致启动期配置错误。
|
||||
系统 SHALL 在启动期对所有 `RawValueExpectation` 字段执行严格的类型和语义校验。校验 SHALL 同时接受 primitive 原始值和 `ValueMatcher` 对象两种形式,但 MUST NOT 修改输入对象。当输入为 primitive 原始值时,系统 SHALL 视为合法配置(等价于 `{equals: value}`),无需进一步校验 matcher 字段。当输入为 `ValueMatcher` 对象时,`contains` MUST 为 string。`equals` MAY 为任意 JSON value。`exists` 和 `empty` MUST 为 boolean。`gt`、`gte`、`lt` 和 `lte` MUST 为有限数字(`Number.isFinite`)。`regex` MUST 为可编译的 string pattern,并通过 ReDoS 风险校验。`ValueMatcher` 对象 MUST 至少包含一个合法 matcher 字段,空对象 `{}` SHALL 导致启动期配置错误。`ValueMatcher` 对象 MUST NOT 包含未知字段,任何不属于 `equals`、`contains`、`regex`、`exists`、`empty`、`gt`、`gte`、`lt`、`lte` 的字段 SHALL 导致启动期配置错误。
|
||||
|
||||
#### Scenario: 空 matcher 对象被拒绝
|
||||
- **WHEN** YAML 配置中任一 matcher 对象为空 `{}`
|
||||
@@ -65,23 +65,23 @@
|
||||
- **THEN** 系统 SHALL 在启动期配置校验失败,提示该字段必须为布尔值
|
||||
|
||||
#### Scenario: 字符串原始值校验通过
|
||||
- **WHEN** YAML 配置中 ValueMatcher 字段值为字符串 `"stop"`
|
||||
- **THEN** 系统 SHALL 接受该配置,视为 `{equals: "stop"}`
|
||||
- **WHEN** YAML 配置中 RawValueExpectation 字段值为字符串 `"stop"`
|
||||
- **THEN** 系统 SHALL 接受该配置,但 SHALL NOT 在语义校验阶段修改输入对象
|
||||
|
||||
#### Scenario: 数字原始值校验通过
|
||||
- **WHEN** YAML 配置中 ValueMatcher 字段值为数字 `5000`
|
||||
- **THEN** 系统 SHALL 接受该配置,视为 `{equals: 5000}`
|
||||
- **WHEN** YAML 配置中 RawValueExpectation 字段值为数字 `5000`
|
||||
- **THEN** 系统 SHALL 接受该配置,但 SHALL NOT 在语义校验阶段修改输入对象
|
||||
|
||||
#### Scenario: null 原始值校验通过
|
||||
- **WHEN** YAML 配置中 ValueMatcher 字段值为 `null`
|
||||
- **THEN** 系统 SHALL 接受该配置,视为 `{equals: null}`
|
||||
- **WHEN** YAML 配置中 RawValueExpectation 字段值为 `null`
|
||||
- **THEN** 系统 SHALL 接受该配置,但 SHALL NOT 在语义校验阶段修改输入对象
|
||||
|
||||
#### Scenario: 数组原始值被拒绝
|
||||
- **WHEN** YAML 配置中 ValueMatcher 字段值为数组 `[1, 2]`
|
||||
- **WHEN** YAML 配置中 RawValueExpectation 字段值为数组 `[1, 2]`
|
||||
- **THEN** 系统 SHALL 在启动期配置校验失败,提示必须为 primitive 原始值或 matcher 对象;如需数组 equals 匹配应写成 `{equals: [1, 2]}`
|
||||
|
||||
#### Scenario: 对象原始值必须显式 equals
|
||||
- **WHEN** YAML 配置中 ValueMatcher 字段值为对象 `{foo: "bar"}`,且 `foo` 不是合法 matcher 字段
|
||||
- **WHEN** YAML 配置中 RawValueExpectation 字段值为对象 `{foo: "bar"}`,且 `foo` 不是合法 matcher 字段
|
||||
- **THEN** 系统 SHALL 在启动期配置校验失败,提示 `foo` 是未知 matcher;如需对象 equals 匹配应写成 `{equals: {foo: "bar"}}`
|
||||
|
||||
### Requirement: empty matcher 语义
|
||||
@@ -141,92 +141,108 @@
|
||||
- **WHEN** YAML 配置中任一 `regex` 为 `"(a+)+$"`
|
||||
- **THEN** 系统 SHALL 在启动期配置校验失败,提示正则存在 ReDoS 风险
|
||||
|
||||
### Requirement: ContentRules 内容规则数组
|
||||
系统 SHALL 提供共享 `ContentRules` 表达返回内容断言。`ContentRules` MUST 为有序数组,数组项 SHALL 为直接 `ValueMatcher`,或 `json`、`css`、`xpath` 三类 extractor 规则之一。系统 SHALL 按数组顺序执行全部规则,任一规则失败时 SHALL 立即停止并返回该规则的 failure。系统 MUST NOT 支持内容字段的非数组对象快捷写法。
|
||||
### Requirement: ContentExpectations 内容断言数组
|
||||
系统 SHALL 提供共享 `ContentExpectations` 表达返回内容断言。`ContentExpectations` MUST 为有序数组,数组项 SHALL 为直接 `ValueMatcher`,或 `json`、`css`、`xpath` 三类 extractor expectation 之一。系统 SHALL 在 resolve 阶段将 Raw content DSL 解析为带 `kind` 字段的 Resolved `ContentExpectation` 执行计划,并按数组顺序执行全部 expectation,任一 expectation 失败时 SHALL 立即停止并返回该 expectation 的 failure。系统 MUST NOT 支持内容字段的非数组对象快捷写法。
|
||||
|
||||
#### Scenario: 直接 matcher 内容规则
|
||||
- **WHEN** 内容字段配置 `[{contains: "ready"}, {regex: "listening on \\d+"}]` 且原始内容同时满足两条规则
|
||||
#### Scenario: 直接 matcher 内容 expectation
|
||||
- **WHEN** 内容字段配置 `[{contains: "ready"}, {regex: "listening on \\d+"}]` 且原始内容同时满足两条 expectation
|
||||
- **THEN** 系统 SHALL 判定该内容字段通过
|
||||
|
||||
#### Scenario: 内容规则数组快速失败
|
||||
- **WHEN** 内容字段配置三条规则且第二条规则失败
|
||||
- **THEN** 系统 SHALL 返回第二条规则的 failure,并 MUST NOT 执行第三条规则
|
||||
#### Scenario: 内容 expectation 数组快速失败
|
||||
- **WHEN** 内容字段配置三条 expectation 且第二条 expectation 失败
|
||||
- **THEN** 系统 SHALL 返回第二条 expectation 的 failure,并 MUST NOT 执行第三条 expectation
|
||||
|
||||
#### Scenario: 内容字段必须为数组
|
||||
- **WHEN** YAML 中内容字段配置为 `{contains: "ok"}` 而不是数组
|
||||
- **THEN** 系统 SHALL 在启动期配置校验失败,提示该内容字段必须为规则数组
|
||||
- **THEN** 系统 SHALL 在启动期配置校验失败,提示该内容字段必须为 expectation 数组
|
||||
|
||||
### Requirement: ContentRule 互斥性约束
|
||||
一条 `ContentRule` MUST 为直接 `ValueMatcher` 或恰好一个 extractor(`json`、`css`、`xpath` 之一)。系统 MUST NOT 允许同一条规则同时包含多个 extractor。直接 `ValueMatcher` 规则 MUST NOT 包含 `json`、`css`、`xpath` 字段。系统 SHALL 在启动期对违反互斥性的规则报错。
|
||||
#### Scenario: Resolved content expectation 使用 kind
|
||||
- **WHEN** Raw 内容字段包含直接 matcher、json extractor、css extractor 和 xpath extractor
|
||||
- **THEN** resolve 阶段 SHALL 分别生成 `kind="value"`、`kind="json"`、`kind="css"` 和 `kind="xpath"` 的 Resolved `ContentExpectation`
|
||||
|
||||
### Requirement: ContentExpectation 互斥性约束
|
||||
一条 Raw `ContentExpectation` MUST 为直接 `ValueMatcher` 或恰好一个 extractor(`json`、`css`、`xpath` 之一)。系统 MUST NOT 允许同一条 Raw expectation 同时包含多个 extractor。直接 `ValueMatcher` expectation MUST NOT 包含 `json`、`css`、`xpath` 字段。系统 SHALL 在启动期对违反互斥性的 Raw expectation 报错。
|
||||
|
||||
#### Scenario: 多 extractor 被拒绝
|
||||
- **WHEN** YAML 中内容规则为 `{json: {path: "$.a"}, css: {selector: "div"}}`
|
||||
- **THEN** 系统 SHALL 在启动期配置校验失败,提示一条规则不能同时包含多个 extractor
|
||||
- **WHEN** YAML 中内容 expectation 为 `{json: {path: "$.a"}, css: {selector: "div"}}`
|
||||
- **THEN** 系统 SHALL 在启动期配置校验失败,提示一条 expectation 不能同时包含多个 extractor
|
||||
|
||||
#### Scenario: 直接 matcher 混入 extractor 被拒绝
|
||||
- **WHEN** YAML 中内容规则为 `{contains: "ok", json: {path: "$.a"}}`
|
||||
- **WHEN** YAML 中内容 expectation 为 `{contains: "ok", json: {path: "$.a"}}`
|
||||
- **THEN** 系统 SHALL 在启动期配置校验失败,提示直接 matcher 不能与 extractor 混用
|
||||
|
||||
### Requirement: 空 ContentRules 数组语义
|
||||
`ContentRules` 空数组 `[]` SHALL 被系统接受为合法配置。运行期空数组 SHALL 等价于无规则,即该内容字段的断言直接通过。
|
||||
### Requirement: 空 ContentExpectations 数组语义
|
||||
`ContentExpectations` 空数组 `[]` SHALL 被系统接受为合法配置。运行期空数组 SHALL 等价于无内容 expectation,即该内容字段的断言直接通过。
|
||||
|
||||
#### Scenario: 空 body 数组通过
|
||||
- **WHEN** HTTP target 配置 `expect.body: []` 且响应体为任意内容
|
||||
- **THEN** 系统 SHALL 判定 body 阶段通过
|
||||
|
||||
### Requirement: ContentRules 非字符串值序列化
|
||||
当 `ContentRules` 的观测源为非字符串值(如对象或数组)时,直接 `ValueMatcher` 的 `contains` 和 `regex` SHALL 先将值 JSON 序列化为字符串后匹配。`equals` SHALL 直接在原始结构化值上使用深度相等比较,不进行序列化。
|
||||
### Requirement: ContentExpectations 非字符串值序列化
|
||||
当 `ContentExpectations` 的观测源为非字符串值(如对象或数组)时,直接 `ValueMatcher` 的 `contains` 和 `regex` SHALL 先将值 JSON 序列化为字符串后匹配。`equals` SHALL 直接在原始结构化值上使用深度相等比较,不进行序列化。
|
||||
|
||||
#### Scenario: 对象序列化后 contains 匹配
|
||||
- **WHEN** ContentRules 观测源为 `{status: "ok"}` 且规则为 `{contains: "ok"}`
|
||||
- **WHEN** ContentExpectations 观测源为 `{status: "ok"}` 且 expectation 为 `{contains: "ok"}`
|
||||
- **THEN** 系统 SHALL 将对象 JSON 序列化后执行 contains 匹配
|
||||
|
||||
#### Scenario: 对象 equals 不序列化
|
||||
- **WHEN** ContentRules 观测源为 `{status: "ok"}` 且规则为 `{equals: {status: "ok"}}`
|
||||
- **WHEN** ContentExpectations 观测源为 `{status: "ok"}` 且 expectation 为 `{equals: {status: "ok"}}`
|
||||
- **THEN** 系统 SHALL 直接在结构化值上使用深度相等比较
|
||||
|
||||
### Requirement: ContentRules 提取器
|
||||
系统 SHALL 支持在 `ContentRules` 中使用 `json`、`css` 和 `xpath` extractor。`json.path` MUST 使用现有 JSONPath 子集。`css.selector` MUST 为非空字符串,并 MAY 配置 `attr` 提取属性值。`xpath.path` MUST 为非空字符串,并 SHALL 在启动期进行可编译校验。Extractor 内部 MAY 包含任意 `ValueMatcher` 字段。Extractor 规则未配置任何 matcher 时 SHALL 等价于 `exists: true`。
|
||||
### Requirement: ContentExpectations 提取器
|
||||
系统 SHALL 支持在 `ContentExpectations` 中使用 `json`、`css` 和 `xpath` extractor。`json.path` MUST 使用现有 JSONPath 子集。`css.selector` MUST 为非空字符串,并 MAY 配置 `attr` 提取属性值。`xpath.path` MUST 为非空字符串,并 SHALL 在启动期进行可编译校验。Extractor 内部 MAY 包含任意 `ValueMatcher` 字段。Extractor expectation 未配置任何 matcher 时,resolve 阶段 SHALL 将其 Resolved matcher 物化为 `{ exists: true }`。
|
||||
|
||||
#### Scenario: json extractor 数字比较
|
||||
- **WHEN** 原始内容为 JSON 字符串 `{"count": 2}` 且规则为 `{json: {path: "$.count", gte: 1}}`
|
||||
- **THEN** 系统 SHALL 解析 JSON、提取 `$.count` 并判定该规则通过
|
||||
- **WHEN** 原始内容为 JSON 字符串 `{"count": 2}` 且 expectation 为 `{json: {path: "$.count", gte: 1}}`
|
||||
- **THEN** 系统 SHALL 解析 JSON、提取 `$.count` 并判定该 expectation 通过
|
||||
|
||||
#### Scenario: json extractor 存在性默认语义
|
||||
- **WHEN** 原始内容为 JSON 字符串 `{"user": {"id": null}}` 且规则为 `{json: {path: "$.user.id"}}`
|
||||
- **THEN** 系统 SHALL 将该规则视为 `{json: {path: "$.user.id", exists: true}}` 并判定通过
|
||||
- **WHEN** 原始内容为 JSON 字符串 `{"user": {"id": null}}` 且 expectation 为 `{json: {path: "$.user.id"}}`
|
||||
- **THEN** resolve 阶段 SHALL 将该 expectation 的 matcher 物化为 `{exists: true}` 并在运行期判定通过
|
||||
|
||||
#### Scenario: css attr 存在性默认语义
|
||||
- **WHEN** 原始内容包含 `<meta name="status" content="ok">` 且规则为 `{css: {selector: "meta[name=status]", attr: "content"}}`
|
||||
- **THEN** 系统 SHALL 在属性存在时判定该规则通过
|
||||
- **WHEN** 原始内容包含 `<meta name="status" content="ok">` 且 expectation 为 `{css: {selector: "meta[name=status]", attr: "content"}}`
|
||||
- **THEN** resolve 阶段 SHALL 将该 expectation 的 matcher 物化为 `{exists: true}` 并在属性存在时判定通过
|
||||
|
||||
#### Scenario: xpath 无匹配节点失败
|
||||
- **WHEN** XML 内容中不存在 XPath 指向的节点,且规则为 `{xpath: {path: "/root/status"}}`
|
||||
- **THEN** 系统 SHALL 判定该规则不通过并生成 phase 对应内容字段的 mismatch failure
|
||||
- **WHEN** XML 内容中不存在 XPath 指向的节点,且 expectation 为 `{xpath: {path: "/root/status"}}`
|
||||
- **THEN** 系统 SHALL 判定该 expectation 不通过并生成 phase 对应内容字段的 mismatch failure
|
||||
|
||||
### Requirement: KeyValueExpect 键值规则
|
||||
系统 SHALL 提供共享 `KeyValueExpect` 表达键值型观测值断言。`KeyValueExpect` SHALL 为动态键对象,每个键对应的值 MAY 为 `ValueMatcher` 或 JSON 字面量。字面量值 SHALL 等价于 `{equals: <literal>}`。调用方 MAY 指定 key 规范化策略;HTTP 与 LLM headers MUST 使用大小写不敏感的 key 匹配。
|
||||
### Requirement: KeyedExpectations 键控断言数组
|
||||
系统 SHALL 提供共享 `KeyedExpectations` 表达键值型观测值断言。Raw `KeyedExpectations` SHALL 为动态键对象,每个键对应的值 MUST 为 `RawValueExpectation`;数组和对象简写 MUST 被拒绝,数组或对象 equals 匹配 MUST 显式写为 `{equals: <value>}`。Resolved `KeyedExpectations` SHALL 为有序数组,每个元素包含原始 key 和已归一化的 `ValueExpectation` matcher。调用方 MAY 指定 key 规范化策略;HTTP 与 LLM headers MUST 使用大小写不敏感的 key 匹配,并 MUST 在启动期校验或 resolve 阶段拒绝归一化后重复的 header key。DB rows 不做 key 归一化,按查询结果列名大小写敏感匹配。
|
||||
|
||||
#### Scenario: headers 字面量快捷写法
|
||||
- **WHEN** 响应 headers 中 `content-type` 为 `application/json`,且配置为 `headers: {Content-Type: "application/json"}`
|
||||
- **THEN** 系统 SHALL 按大小写不敏感 key 匹配并使用 equals 语义判定通过
|
||||
- **THEN** resolve 阶段 SHALL 将该项解析为 keyed expectation `{key: "Content-Type", matcher: {equals: "application/json"}}`,运行期按大小写不敏感 key 匹配并判定通过
|
||||
|
||||
#### Scenario: headers matcher 写法
|
||||
- **WHEN** 响应 headers 中 `content-type` 为 `application/json; charset=utf-8`,且配置为 `headers: {Content-Type: {contains: "application/json"}}`
|
||||
- **THEN** 系统 SHALL 判定该 header 规则通过
|
||||
- **THEN** 系统 SHALL 判定该 header expectation 通过
|
||||
|
||||
#### Scenario: 缺失键 exists false
|
||||
- **WHEN** 观测键值表中不存在 `x-debug`,且配置为 `{x-debug: {exists: false}}`
|
||||
- **THEN** 系统 SHALL 判定该键规则通过
|
||||
- **THEN** 系统 SHALL 判定该 keyed expectation 通过
|
||||
|
||||
#### Scenario: keyed 对象值必须显式 equals
|
||||
- **WHEN** Raw keyed expectation 的某个值是对象 `{foo: "bar"}` 且未写在 `equals` 下
|
||||
- **THEN** 系统 SHALL 在启动期配置校验失败,提示对象 equals 必须显式写成 `{equals: {foo: "bar"}}`
|
||||
|
||||
#### Scenario: keyed 数组值必须显式 equals
|
||||
- **WHEN** Raw keyed expectation 的某个值是数组 `["a"]` 且未写在 `equals` 下
|
||||
- **THEN** 系统 SHALL 在启动期配置校验失败,提示数组 equals 必须显式写成 `{equals: ["a"]}`
|
||||
|
||||
#### Scenario: header 归一化重复 key 被拒绝
|
||||
- **WHEN** HTTP 或 LLM `expect.headers` 同时配置 `Content-Type` 和 `content-type`
|
||||
- **THEN** 系统 SHALL 在启动期配置校验失败,提示 header key 归一化后重复
|
||||
|
||||
### Requirement: 结构化失败路径
|
||||
系统 SHALL 在共享 matcher、content 和 key-value 断言失败时生成结构化 `CheckFailure`。failure SHALL 包含 `kind`、`phase`、`path`、`message`,并在 mismatch 场景包含 `expected` 和 `actual`。内容规则 failure path SHALL 包含数组下标,key-value failure path SHALL 包含键名,extractor failure path SHALL 包含 extractor 类型和 path/selector 信息。
|
||||
系统 SHALL 在共享 matcher、content 和 keyed expectation 断言失败时生成结构化 `CheckFailure`。failure SHALL 包含 `kind`、`phase`、`path`、`message`,并在 mismatch 场景包含 `expected` 和 `actual`。内容 expectation failure path SHALL 包含数组下标,keyed expectation failure path SHALL 包含键名,extractor failure path SHALL 包含 extractor 类型和 path/selector 信息。failure.expected SHOULD 使用用户可理解的 matcher 或 expectation 片段,MUST NOT 直接暴露 Resolved `kind` 执行计划;单字段 `equals` 包装 SHOULD 展示为原始 expected 值。
|
||||
|
||||
#### Scenario: ContentRules 失败路径
|
||||
- **WHEN** `expect.body[1].json` 规则失败
|
||||
#### Scenario: ContentExpectations 失败路径
|
||||
- **WHEN** `expect.body[1].json` expectation 失败
|
||||
- **THEN** failure.path SHALL 指向 `body[1].json($.path)` 或等价可定位路径,failure.phase SHALL 为 `body`
|
||||
|
||||
#### Scenario: KeyValueExpect 失败路径
|
||||
#### Scenario: KeyedExpectations 失败路径
|
||||
- **WHEN** `expect.headers.Content-Type` 不匹配
|
||||
- **THEN** failure.path SHALL 指向 `headers.Content-Type`,failure.phase SHALL 为 `headers`
|
||||
|
||||
|
||||
@@ -110,11 +110,11 @@
|
||||
- **THEN** 系统 SHALL 记录 `matched=false`,failure 的 kind 为 `error`,phase 为 `icmp`,path 为 `parse`,message 包含 "无法解析 icmp 输出"
|
||||
|
||||
### Requirement: icmp expect 校验
|
||||
系统 SHALL 支持 icmp 专属 expect,包括 `alive`、`packetLossPercent`、`avgLatencyMs`、`maxLatencyMs` 和 `durationMs`,并按 alive、packetLossPercent、avgLatencyMs、maxLatencyMs、durationMs 的阶段顺序快速失败。`alive` SHALL 保持布尔状态语义,未配置时默认 `true`。`packetLossPercent` SHALL 表示 0 到 100 的丢包率百分比,并使用共享 `ValueMatcher`。`avgLatencyMs`、`maxLatencyMs` 和 `durationMs` SHALL 使用共享 `ValueMatcher`。
|
||||
系统 SHALL 支持 icmp 专属 expect,包括 `alive`、`packetLossPercent`、`avgLatencyMs`、`maxLatencyMs` 和 `durationMs`,并按 alive、packetLossPercent、avgLatencyMs、maxLatencyMs、durationMs 的阶段顺序快速失败。`alive` SHALL 保持布尔状态语义,未配置时在 Resolved expect 中默认 `true`。`packetLossPercent` SHALL 表示 0 到 100 的丢包率百分比,并使用共享 `RawValueExpectation` 输入、运行期 `ValueExpectation` 执行。`avgLatencyMs`、`maxLatencyMs` 和 `durationMs` SHALL 使用共享 `RawValueExpectation` 输入、运行期 `ValueExpectation` 执行。
|
||||
|
||||
#### Scenario: 默认 alive 成功语义
|
||||
- **WHEN** icmp target 未显式配置 `expect.alive`
|
||||
- **THEN** 系统 SHALL 使用默认 `expect.alive: true` 进行校验
|
||||
- **THEN** 系统 SHALL 在 Resolved icmp expect 中使用默认 `alive: true` 进行校验
|
||||
|
||||
#### Scenario: alive 校验通过
|
||||
- **WHEN** icmp target 配置 `expect.alive: true`,且目标主机可达
|
||||
@@ -165,17 +165,21 @@
|
||||
- **THEN** 系统 SHALL 以配置错误退出,提示 expect 包含未知字段
|
||||
|
||||
#### Scenario: packetLossPercent 类型非法
|
||||
- **WHEN** YAML 中 icmp target 的 `expect.packetLossPercent` 不是合法 `ValueMatcher`,或其数值范围无法用于 0 到 100 的百分比断言
|
||||
- **WHEN** YAML 中 icmp target 的 `expect.packetLossPercent` 不是合法 `RawValueExpectation`,或其数值范围无法用于 0 到 100 的百分比断言
|
||||
- **THEN** 系统 SHALL 以配置错误退出,提示 expect.packetLossPercent 格式错误
|
||||
|
||||
#### Scenario: avgLatencyMs 类型非法
|
||||
- **WHEN** YAML 中 icmp target 的 `expect.avgLatencyMs` 不是合法 `ValueMatcher`
|
||||
- **WHEN** YAML 中 icmp target 的 `expect.avgLatencyMs` 不是合法 `RawValueExpectation`
|
||||
- **THEN** 系统 SHALL 以配置错误退出,提示 expect.avgLatencyMs 格式错误
|
||||
|
||||
#### Scenario: maxLatencyMs 类型非法
|
||||
- **WHEN** YAML 中 icmp target 的 `expect.maxLatencyMs` 不是合法 `ValueMatcher`
|
||||
- **WHEN** YAML 中 icmp target 的 `expect.maxLatencyMs` 不是合法 `RawValueExpectation`
|
||||
- **THEN** 系统 SHALL 以配置错误退出,提示 expect.maxLatencyMs 格式错误
|
||||
|
||||
#### Scenario: Raw icmp expect 不被校验阶段修改
|
||||
- **WHEN** YAML 中 icmp target 配置 `expect.durationMs: 5000`
|
||||
- **THEN** 语义校验 SHALL 接受该 Raw primitive 简写且 MUST NOT 将输入对象原地改写为 `{equals: 5000}`
|
||||
|
||||
### Requirement: icmp detail 摘要
|
||||
系统 SHALL 在 icmp API 序列化时从 observation 动态生成结构化 detail 摘要,展示关键指标。API registry type SHALL 仍为 `icmp`。
|
||||
|
||||
|
||||
@@ -108,18 +108,18 @@ LLM checker SHALL 在 SDK 调用结果和 expect 断言之间构建 `LlmCheckObs
|
||||
- **THEN** LLM checker SHALL 返回 `phase: "request"` 的 error failure
|
||||
|
||||
### Requirement: LLM Expect 断言
|
||||
LLM checker SHALL 支持 `expect.status`、`expect.headers`、`expect.output`、`expect.finishReason`、`expect.rawFinishReason`、`expect.usage.inputTokens`、`expect.usage.outputTokens`、`expect.usage.totalTokens`、`expect.stream.completed`、`expect.stream.firstTokenMs` 和 `expect.durationMs`。`expect.status` SHALL 保持 HTTP 状态码数组语义并复用 HTTP status 断言。`expect.headers` SHALL 使用共享 `KeyValueExpect` 且 header key 大小写不敏感。`expect.output` MUST 使用共享 `ContentRules`。`expect.finishReason` 和 `expect.rawFinishReason` SHALL 使用共享 `ValueMatcher`。`expect.usage.*`、`expect.stream.firstTokenMs` 和 `expect.durationMs` SHALL 使用共享 `ValueMatcher`。LLM checker MUST 按固定顺序快速失败,非流式顺序为 status、headers、output、finishReason、rawFinishReason、usage、durationMs;流式顺序为 status、headers、stream.completed、stream.firstTokenMs、output、finishReason、rawFinishReason、usage、durationMs。
|
||||
LLM checker SHALL 支持 `expect.status`、`expect.headers`、`expect.output`、`expect.finishReason`、`expect.rawFinishReason`、`expect.usage.inputTokens`、`expect.usage.outputTokens`、`expect.usage.totalTokens`、`expect.stream.completed`、`expect.stream.firstTokenMs` 和 `expect.durationMs`。`expect.status` SHALL 保持 HTTP 状态码数组语义并复用共享 status code 断言,未配置时在 Resolved expect 中物化默认 `[200]`。`expect.headers` SHALL 使用共享 `RawKeyedExpectations` 输入并在运行期使用 `KeyedExpectations`,header key 大小写不敏感。`expect.output` MUST 使用共享 `RawContentExpectations` 输入并在运行期使用 `ContentExpectations`。`expect.finishReason` 和 `expect.rawFinishReason` SHALL 使用共享 `RawValueExpectation` 输入并在运行期使用 `ValueExpectation`。`expect.usage.*`、`expect.stream.firstTokenMs` 和 `expect.durationMs` SHALL 使用共享 `RawValueExpectation` 输入并在运行期使用 `ValueExpectation`。LLM checker MUST 按固定顺序快速失败,非流式顺序为 status、headers、output、finishReason、rawFinishReason、usage、durationMs;流式顺序为 status、headers、stream.completed、stream.firstTokenMs、output、finishReason、rawFinishReason、usage、durationMs。
|
||||
|
||||
#### Scenario: 默认 status 断言
|
||||
- **WHEN** LLM target 未配置 `expect.status`
|
||||
- **THEN** LLM checker SHALL 使用默认 `status: [200]` 语义
|
||||
- **THEN** LLM checker SHALL 在 Resolved expect 中使用默认 `status: [200]` 语义
|
||||
|
||||
#### Scenario: expect headers 通过
|
||||
- **WHEN** observing fetch 捕获的响应 headers 满足 `expect.headers` 配置
|
||||
- **THEN** LLM checker SHALL 判定 headers 断言通过
|
||||
- **THEN** LLM checker SHALL 通过共享 header expectation 包装函数判定 headers 断言通过
|
||||
|
||||
#### Scenario: output ContentRules 通过
|
||||
- **WHEN** LLM 输出文本满足 `expect.output` 中配置的全部 ContentRules
|
||||
#### Scenario: output ContentExpectations 通过
|
||||
- **WHEN** LLM 输出文本满足 `expect.output` 中配置的全部 ContentExpectations
|
||||
- **THEN** LLM checker SHALL 判定 output 阶段通过
|
||||
|
||||
#### Scenario: finishReason ValueMatcher 通过
|
||||
@@ -175,18 +175,22 @@ LLM checker SHALL 使用共享 `ContentRules` 校验 `expect.output`。每个 ou
|
||||
|
||||
#### Scenario: output JSONPath 存在性默认语义
|
||||
- **WHEN** `outputText` 是 JSON 字符串且 target 配置 `expect.output: [{json: {path: "$.status"}}]`
|
||||
- **THEN** LLM checker SHALL 将该规则按 `exists: true` 语义执行
|
||||
- **THEN** resolve 阶段 SHALL 将该 expectation 的 matcher 物化为 `{exists: true}`,运行期按存在性语义执行
|
||||
|
||||
#### Scenario: output 规则按顺序快速失败
|
||||
- **WHEN** `expect.output` 包含多个规则且第一条规则失败
|
||||
- **THEN** LLM checker SHALL 返回第一条失败规则的 mismatch failure,不继续校验后续 output 规则
|
||||
- **WHEN** `expect.output` 包含多个 expectation 且第一条 expectation 失败
|
||||
- **THEN** LLM checker SHALL 返回第一条失败 expectation 的 mismatch failure,不继续校验后续 output expectation
|
||||
|
||||
### Requirement: LLM Stream 断言
|
||||
LLM checker SHALL 仅允许 `mode: stream` 使用 `expect.stream`。`expect.stream.completed` 未配置时,LLM checker SHALL 在 stream observation 路径使用默认 `true` 语义。`expect.stream.firstTokenMs` SHALL 使用共享 `ValueMatcher`,并仅统计第一个非空 `text-delta` 事件耗时,不统计 reasoning、tool call 或 source 事件。
|
||||
LLM checker SHALL 仅允许 `mode: stream` 使用 `expect.stream`。仅当用户配置了 `expect.stream` 且未配置 `expect.stream.completed` 时,resolve 阶段 SHALL 在 Resolved expect 中物化默认 `completed: true`;LLM checker MUST NOT 因为 `llm.mode: stream` 自动添加 `stream.completed` 断言。`expect.stream.firstTokenMs` SHALL 使用共享 `RawValueExpectation` 输入并在运行期使用 `ValueExpectation`,且仅统计第一个非空 `text-delta` 事件耗时,不统计 reasoning、tool call 或 source 事件。
|
||||
|
||||
#### Scenario: stream completed 默认值
|
||||
- **WHEN** target 配置 `llm.mode: stream` 且未配置 `expect.stream.completed`
|
||||
- **THEN** LLM checker SHALL 要求 SDK stream 正常完成
|
||||
- **WHEN** target 配置 `llm.mode: stream` 且配置 `expect.stream: {}`
|
||||
- **THEN** resolve 阶段 SHALL 在 Resolved expect 中物化 `stream.completed: true` 并要求 SDK stream 正常完成
|
||||
|
||||
#### Scenario: 未配置 expect.stream 不添加 completed
|
||||
- **WHEN** target 配置 `llm.mode: stream` 但未配置 `expect.stream`
|
||||
- **THEN** resolve 阶段 SHALL NOT 自动添加 `stream.completed` 断言
|
||||
|
||||
#### Scenario: stream error
|
||||
- **WHEN** `fullStream` 产生 error part
|
||||
|
||||
@@ -101,7 +101,7 @@
|
||||
|
||||
除 `headers`、`env`、`variables` 等明确声明为动态键值表的对象外,配置中的未知字段 SHALL 导致启动期配置错误。系统 MUST NOT 静默忽略未知字段。
|
||||
|
||||
所有 ValueMatcher 类型的 expect 字段 SHALL 在 JSON Schema 契约中声明为 `anyOf: [primitiveValue, matcherObject]` 联合类型,同时接受 primitive 原始值(string / number / boolean / null)和 ValueMatcher 对象。语义 validator SHALL 在校验 ValueMatcher 字段之前执行归一化,将 primitive 原始值转换为 `{equals: value}` 对象形式。数组和对象 MUST NOT 作为 ValueMatcher 原始值简写;需要对数组或对象执行 equals 匹配时,配置 MUST 显式写成 `{equals: value}`。
|
||||
所有 `RawValueExpectation` 类型的 expect 字段 SHALL 在 JSON Schema 契约中声明为 `anyOf: [primitiveValue, matcherObject]` 联合类型,同时接受 primitive 原始值(string / number / boolean / null)和 `ValueMatcher` 对象。语义 validator SHALL 在不修改输入的前提下校验 `RawValueExpectation`;primitive 原始值 SHALL 被视为合法 Raw 输入,并在 checker resolve 阶段转换为 `{equals: value}` 对象形式。数组和对象 MUST NOT 作为 `RawValueExpectation` 原始值简写;需要对数组或对象执行 equals 匹配时,配置 MUST 显式写成 `{equals: value}`。`RawKeyedExpectations` 的动态键值 schema SHALL 复用 `RawValueExpectation`,MUST NOT 通过额外 JSON value 分支接受数组或对象简写。
|
||||
|
||||
#### Scenario: target 缺少必填字段
|
||||
- **WHEN** YAML 中某个 target 缺少 id 或 type 字段
|
||||
@@ -196,20 +196,20 @@
|
||||
- **THEN** 系统 SHALL 以错误退出,提示该 target 的 status 数字不合法
|
||||
|
||||
#### Scenario: durationMs matcher 非法
|
||||
- **WHEN** YAML 中某个 target 的 `expect.durationMs` 不是合法 `ValueMatcher` 也不是 primitive 原始值
|
||||
- **WHEN** YAML 中某个 target 的 `expect.durationMs` 不是合法 `RawValueExpectation`
|
||||
- **THEN** 系统 SHALL 以错误退出,提示该 target 的 expect.durationMs 格式错误
|
||||
|
||||
#### Scenario: durationMs 原始值简写合法
|
||||
- **WHEN** YAML 中某个 target 配置 `expect.durationMs: 5000`
|
||||
- **THEN** 系统 SHALL 接受该配置,归一化为 `{equals: 5000}` 后校验通过
|
||||
- **THEN** 语义校验 SHALL 接受该 Raw primitive 简写且 MUST NOT 修改输入;checker resolve 阶段 SHALL 将其解析为 `{equals: 5000}`
|
||||
|
||||
#### Scenario: ValueMatcher 字段字符串简写合法
|
||||
- **WHEN** YAML 中某个 target 配置 `expect.finishReason: "stop"`
|
||||
- **THEN** 系统 SHALL 接受该配置,归一化为 `{equals: "stop"}` 后校验通过
|
||||
- **THEN** 语义校验 SHALL 接受该 Raw primitive 简写且 MUST NOT 修改输入;checker resolve 阶段 SHALL 将其解析为 `{equals: "stop"}`
|
||||
|
||||
#### Scenario: ValueMatcher 字段 null 简写合法
|
||||
- **WHEN** YAML 中某个 target 配置 ValueMatcher 字段值为 `null`
|
||||
- **THEN** 系统 SHALL 接受该配置,归一化为 `{equals: null}` 后校验通过
|
||||
- **THEN** 语义校验 SHALL 接受该 Raw primitive 简写且 MUST NOT 修改输入;checker resolve 阶段 SHALL 将其解析为 `{equals: null}`
|
||||
|
||||
#### Scenario: ValueMatcher 字段数组简写非法
|
||||
- **WHEN** YAML 中某个 target 配置 ValueMatcher 字段值为数组 `[1, 2]`
|
||||
@@ -223,40 +223,40 @@
|
||||
- **WHEN** YAML 中某个 target 配置 `type: icmp` 但缺少 `icmp.host`
|
||||
- **THEN** 系统 SHALL 以错误退出,提示该 target 缺少 icmp.host 字段
|
||||
|
||||
#### Scenario: ping expect 未知字段
|
||||
- **WHEN** YAML 中 ping target 的 expect 包含非 ping expect 字段
|
||||
#### Scenario: icmp expect 未知字段
|
||||
- **WHEN** YAML 中 icmp target 的 expect 包含非 icmp expect 字段
|
||||
- **THEN** 系统 SHALL 以配置错误退出,提示 expect 包含未知字段
|
||||
|
||||
#### Scenario: HTTP expect headers 非法
|
||||
- **WHEN** YAML 中某个 HTTP target 的 `expect.headers` 不是对象,或某个 header 期望既不是字符串也不是合法 operator
|
||||
- **WHEN** YAML 中某个 HTTP target 的 `expect.headers` 不是对象,或某个 header 期望不是合法 `RawValueExpectation`
|
||||
- **THEN** 系统 SHALL 以错误退出,提示该 target 的 expect.headers 格式错误
|
||||
|
||||
#### Scenario: HTTP expect body 必须为数组
|
||||
- **WHEN** YAML 中某个 HTTP target 的 `expect.body` 已配置但不是数组
|
||||
- **THEN** 系统 SHALL 以错误退出,提示该 target 的 expect.body 必须为数组
|
||||
|
||||
#### Scenario: HTTP body rule 缺少支持字段
|
||||
#### Scenario: HTTP body expectation 缺少支持字段
|
||||
- **WHEN** YAML 中某个 HTTP target 的 `expect.body` 数组项未包含 contains、regex、json、css、xpath 任一支持字段
|
||||
- **THEN** 系统 SHALL 以错误退出,提示该 body rule 缺少支持的规则类型
|
||||
- **THEN** 系统 SHALL 以错误退出,提示该 body expectation 缺少支持的 expectation 类型
|
||||
|
||||
#### Scenario: HTTP body rule 同时配置多个支持字段
|
||||
- **WHEN** YAML 中某个 HTTP target 的同一条 body rule 同时包含 contains、regex、json、css、xpath 中的多个支持字段
|
||||
- **THEN** 系统 SHALL 以错误退出,提示每条 body rule 只能配置一种规则类型
|
||||
#### Scenario: HTTP body expectation 同时配置多个支持字段
|
||||
- **WHEN** YAML 中某个 HTTP target 的同一条 body expectation 同时包含 contains、regex、json、css、xpath 中的多个支持字段
|
||||
- **THEN** 系统 SHALL 以错误退出,提示每条 body expectation 只能配置一种 expectation 类型
|
||||
|
||||
#### Scenario: HTTP body regex 非法
|
||||
- **WHEN** YAML 中某个 HTTP target 的 body regex 规则不是字符串或不是可编译正则表达式
|
||||
- **WHEN** YAML 中某个 HTTP target 的 body regex expectation 不是字符串或不是可编译正则表达式
|
||||
- **THEN** 系统 SHALL 以错误退出,提示该 body regex 不合法
|
||||
|
||||
#### Scenario: HTTP body json path 非法
|
||||
- **WHEN** YAML 中某个 HTTP target 的 body json 规则缺少 path,或 path 不符合系统支持的 JSONPath 子集
|
||||
- **WHEN** YAML 中某个 HTTP target 的 body json expectation 缺少 path,或 path 不符合系统支持的 JSONPath 子集
|
||||
- **THEN** 系统 SHALL 以错误退出,提示该 body json path 不合法
|
||||
|
||||
#### Scenario: HTTP body css selector 非法
|
||||
- **WHEN** YAML 中某个 HTTP target 的 body css 规则缺少 selector,或 selector 不是非空字符串
|
||||
- **WHEN** YAML 中某个 HTTP target 的 body css expectation 缺少 selector,或 selector 不是非空字符串
|
||||
- **THEN** 系统 SHALL 以错误退出,提示该 body css selector 不合法
|
||||
|
||||
#### Scenario: HTTP body xpath path 非法
|
||||
- **WHEN** YAML 中某个 HTTP target 的 body xpath 规则缺少 path,或 path 不是非空字符串,或可被现有 XPath 库静态判定为语法错误
|
||||
- **WHEN** YAML 中某个 HTTP target 的 body xpath expectation 缺少 path,或 path 不是非空字符串,或可被现有 XPath 库静态判定为语法错误
|
||||
- **THEN** 系统 SHALL 以错误退出,提示该 body xpath path 不合法
|
||||
|
||||
#### Scenario: expect matcher 类型非法
|
||||
@@ -293,23 +293,27 @@
|
||||
|
||||
#### Scenario: 导出配置 JSON Schema
|
||||
- **WHEN** 仓库生成或检查配置契约
|
||||
- **THEN** 根目录 SHALL 存在 draft-07 `probe-config.schema.json`,且其内容 SHALL 与当前公共 fragments 和已注册 checker fragments 组装出的完整 schema 一致(包含 variables 段和 target 的 id/name 字段)。所有 ValueMatcher 字段的 schema SHALL 声明为 `anyOf: [primitiveValue, matcherObject]` 联合类型
|
||||
- **THEN** 根目录 SHALL 存在 draft-07 `probe-config.schema.json`,且其内容 SHALL 与当前公共 fragments 和已注册 checker fragments 组装出的完整 schema 一致(包含 variables 段和 target 的 id/name 字段)。所有 `RawValueExpectation` 字段的 schema SHALL 声明为 `anyOf: [primitiveValue, matcherObject]` 联合类型,`RawKeyedExpectations` 的 dynamic value schema SHALL 复用 `RawValueExpectation`
|
||||
|
||||
#### Scenario: JSON Schema ValueMatcher 接受原始值
|
||||
- **WHEN** 使用 JSON Schema 校验配置文件中 ValueMatcher 字段值为数字 `5000`
|
||||
- **THEN** JSON Schema 校验 SHALL 通过,因为 ValueMatcher schema 声明为 `anyOf: [primitiveValue, matcherObject]`
|
||||
#### Scenario: JSON Schema RawValueExpectation 接受原始值
|
||||
- **WHEN** 使用 JSON Schema 校验配置文件中 RawValueExpectation 字段值为数字 `5000`
|
||||
- **THEN** JSON Schema 校验 SHALL 通过,因为 RawValueExpectation schema 声明为 `anyOf: [primitiveValue, matcherObject]`
|
||||
|
||||
#### Scenario: JSON Schema ValueMatcher 接受 matcher 对象
|
||||
- **WHEN** 使用 JSON Schema 校验配置文件中 ValueMatcher 字段值为 `{lte: 5000}`
|
||||
#### Scenario: JSON Schema RawValueExpectation 接受 matcher 对象
|
||||
- **WHEN** 使用 JSON Schema 校验配置文件中 RawValueExpectation 字段值为 `{lte: 5000}`
|
||||
- **THEN** JSON Schema 校验 SHALL 通过
|
||||
|
||||
#### Scenario: JSON Schema ValueMatcher 拒绝数组原始值
|
||||
- **WHEN** 使用 JSON Schema 校验配置文件中 ValueMatcher 字段值为数组 `[1, 2]`
|
||||
#### Scenario: JSON Schema RawValueExpectation 拒绝数组原始值
|
||||
- **WHEN** 使用 JSON Schema 校验配置文件中 RawValueExpectation 字段值为数组 `[1, 2]`
|
||||
- **THEN** JSON Schema 校验 SHALL 失败,因为数组不属于 primitive 原始值或 matcher 对象
|
||||
|
||||
#### Scenario: JSON Schema ValueMatcher 接受 equals 数组对象
|
||||
- **WHEN** 使用 JSON Schema 校验配置文件中 ValueMatcher 字段值为 `{equals: [1, 2]}` 或 `{equals: {status: "ok"}}`
|
||||
#### Scenario: JSON Schema RawValueExpectation 接受 equals 数组对象
|
||||
- **WHEN** 使用 JSON Schema 校验配置文件中 RawValueExpectation 字段值为 `{equals: [1, 2]}` 或 `{equals: {status: "ok"}}`
|
||||
- **THEN** JSON Schema 校验 SHALL 通过,因为 `equals` 支持任意 JSON value
|
||||
|
||||
#### Scenario: JSON Schema RawKeyedExpectations 拒绝数组对象简写
|
||||
- **WHEN** 使用 JSON Schema 校验配置文件中 `expect.headers.X` 或 DB row 列值为数组 `["a"]` 或对象 `{nested: "ok"}`
|
||||
- **THEN** JSON Schema 校验 SHALL 失败,除非该值显式写在 `{equals: ...}` 下
|
||||
系统 SHALL 支持使用单位字符串配置读取上限,单位包括 `B`、`KB`、`MB` 和 `GB`。
|
||||
|
||||
#### Scenario: 解析 MB
|
||||
@@ -339,43 +343,45 @@
|
||||
- **THEN** 系统 SHALL 调用 `Bun.YAML.parse()` 将内容解析为配置对象
|
||||
|
||||
### Requirement: expect 配置增强
|
||||
系统 SHALL 支持 typed target 的领域专用 expect 配置,并通过共享 `ValueMatcher`、`ContentRules` 和 `KeyValueExpect` 表达可复用断言能力。状态类字段 SHALL 保持枚举或布尔语义,包括 HTTP/LLM 的 `status`(支持精确数字和范围模式)、cmd 的 `exitCode`、tcp 的 `connected`、ping 的 `alive` 和 udp 的 `responded`。数字指标字段 SHALL 使用 `ValueMatcher`,包括通用 `durationMs`、db 的 `rowCount`、udp 的 `responseSize`/`sourceHost`/`sourcePort`、ping 的 `packetLossPercent`/`avgLatencyMs`/`maxLatencyMs`、llm 的 usage token 与 stream 首 token 耗时。内容类字段 MUST 使用 `ContentRules` 数组表达配置顺序,包括 HTTP `body`、cmd `stdout`/`stderr`、tcp `banner`、udp `response`、llm `output` 和 db `result`。LLM `finishReason` 和 `rawFinishReason` SHALL 使用 `ValueMatcher`(非 ContentRules),因为它们是单值字符串元数据。键值类字段 SHALL 使用 `KeyValueExpect`,包括 HTTP/LLM `headers` 和 db `rows` 中的列值断言(db `rows` 的类型为 `Array<KeyValueExpect>`,外层数组按行索引,内层每个元素为 KeyValueExpect)。
|
||||
系统 SHALL 支持 typed target 的领域专用 expect 配置,并通过共享 `ValueMatcher`、`ContentExpectations` 和 `KeyedExpectations` 表达可复用断言能力。状态类字段 SHALL 保持枚举或布尔语义,包括 HTTP/LLM 的 `status`(支持精确数字和范围模式)、cmd 的 `exitCode`、tcp 的 `connected`、icmp 的 `alive` 和 udp 的 `responded`。value 类指标字段 SHALL 使用 `RawValueExpectation` 输入,并在 resolve 阶段归一化为运行期 `ValueExpectation`,包括通用 `durationMs`、db 的 `rowCount`、udp 的 `responseSize`/`sourceHost`/`sourcePort`、icmp 的 `packetLossPercent`/`avgLatencyMs`/`maxLatencyMs`、llm 的 usage token 与 stream 首 token 耗时。内容类字段 MUST 使用 `RawContentExpectations` 数组表达配置顺序,包括 HTTP `body`、cmd `stdout`/`stderr`、tcp `banner`、udp `response`、llm `output` 和 db `result`。LLM `finishReason` 和 `rawFinishReason` SHALL 使用 `RawValueExpectation`(非 ContentExpectations),因为它们是单值字符串元数据。键值类字段 SHALL 使用 `RawKeyedExpectations`,包括 HTTP/LLM `headers` 和 db `rows` 中的列值断言(db `rows` 的类型为 `Array<RawKeyedExpectations>`,外层数组按行索引,内层每个元素表达该行的列值断言)。
|
||||
|
||||
配置加载流程 SHALL 保留变量替换后的 Raw expect 作为用户配置快照,同时生成 Resolved expect 作为运行期执行计划。语义校验 SHALL 只读取 Raw expect 并报告问题,MUST NOT 原地归一化或修改 Raw expect。Store 持久化 SHALL 写入 Raw expect;checker execute SHALL 只消费 Resolved expect。
|
||||
|
||||
#### Scenario: 解析 HTTP expect 配置
|
||||
- **WHEN** YAML 配置文件中 HTTP target 的 expect 包含 status、headers、body 规则数组和 durationMs matcher
|
||||
- **THEN** 系统 SHALL 正确解析并存储为 HTTP target 的 expect 字段
|
||||
- **WHEN** YAML 配置文件中 HTTP target 的 expect 包含 status、headers、body expectation 数组和 durationMs matcher
|
||||
- **THEN** 系统 SHALL 保留 Raw HTTP expect 快照,并生成包含默认 status、resolved keyed headers、resolved content body 和 resolved durationMs 的 HTTP Resolved expect
|
||||
|
||||
#### Scenario: 解析 cmd expect 配置
|
||||
- **WHEN** YAML 配置文件中 cmd target 的 expect 包含 exitCode、stdout、stderr 和 durationMs matcher
|
||||
- **THEN** 系统 SHALL 正确解析并存储为 cmd target 的 expect 字段
|
||||
- **THEN** 系统 SHALL 保留 Raw cmd expect 快照,并生成包含默认 exitCode、resolved stdout/stderr content expectations 和 resolved durationMs 的 cmd Resolved expect
|
||||
|
||||
#### Scenario: 解析 db expect 配置
|
||||
- **WHEN** YAML 配置文件中 db target 的 expect 包含 durationMs、rowCount、rows 和 result
|
||||
- **THEN** 系统 SHALL 正确解析并存储为 db target 的 expect 字段
|
||||
- **THEN** 系统 SHALL 保留 Raw db expect 快照,并生成包含 resolved rowCount、rows keyed expectations 和 result content expectations 的 db Resolved expect
|
||||
|
||||
#### Scenario: 解析 tcp expect 配置
|
||||
- **WHEN** YAML 配置文件中 tcp target 的 expect 包含 connected、banner 规则数组和 durationMs matcher
|
||||
- **THEN** 系统 SHALL 正确解析并存储为 tcp target 的 expect 字段
|
||||
- **WHEN** YAML 配置文件中 tcp target 的 expect 包含 connected、banner expectation 数组和 durationMs matcher
|
||||
- **THEN** 系统 SHALL 保留 Raw tcp expect 快照,并生成包含默认 connected、resolved banner content expectations 和 resolved durationMs 的 tcp Resolved expect
|
||||
|
||||
#### Scenario: 解析 ping expect 配置
|
||||
- **WHEN** YAML 配置文件中 ping target 的 expect 包含 alive、packetLossPercent、avgLatencyMs、maxLatencyMs 和 durationMs matcher
|
||||
- **THEN** 系统 SHALL 正确解析并存储为 ping target 的 expect 字段
|
||||
#### Scenario: 解析 icmp expect 配置
|
||||
- **WHEN** YAML 配置文件中 icmp target 的 expect 包含 alive、packetLossPercent、avgLatencyMs、maxLatencyMs 和 durationMs matcher
|
||||
- **THEN** 系统 SHALL 保留 Raw icmp expect 快照,并生成包含默认 alive 和 resolved 数值 expectations 的 icmp Resolved expect
|
||||
|
||||
#### Scenario: 解析 udp expect 配置
|
||||
- **WHEN** YAML 配置文件中 udp target 的 expect 包含 responded、response、responseSize、sourceHost、sourcePort 和 durationMs matcher
|
||||
- **THEN** 系统 SHALL 正确解析并存储为 udp target 的 expect 字段
|
||||
- **THEN** 系统 SHALL 保留 Raw udp expect 快照,并生成包含默认 responded、resolved response content expectations 和 resolved value expectations 的 udp Resolved expect
|
||||
|
||||
#### Scenario: 解析 llm expect 配置
|
||||
- **WHEN** YAML 配置文件中 llm target 的 expect 包含 status、headers、output、finishReason、rawFinishReason、usage、stream 和 durationMs matcher
|
||||
- **THEN** 系统 SHALL 正确解析并存储为 llm target 的 expect 字段,并保留 output 内容规则数组顺序
|
||||
- **THEN** 系统 SHALL 保留 Raw llm expect 快照,并生成包含默认 status、resolved headers、output、finishReason、rawFinishReason、usage、stream 和 durationMs 的 llm Resolved expect,并保留 output 内容 expectation 数组顺序
|
||||
|
||||
#### Scenario: 解析有序 ContentRules 数组
|
||||
#### Scenario: 解析有序 ContentExpectations 数组
|
||||
- **WHEN** YAML 中任一内容类 expect 配置 contains、json、regex 三个数组项
|
||||
- **THEN** 系统 SHALL 保留数组顺序,供执行阶段按配置顺序快速失败
|
||||
- **THEN** 系统 SHALL 在 Raw expect 中保留数组顺序,并在 Resolved expect 中保留执行顺序,供执行阶段按配置顺序快速失败
|
||||
|
||||
#### Scenario: 不配置 HTTP status
|
||||
- **WHEN** HTTP target 未配置 `expect.status`
|
||||
- **THEN** 系统 SHALL 在执行 expect 时使用默认 `status: [200]` 语义
|
||||
- **THEN** 系统 SHALL 在 Resolved HTTP expect 中物化默认 `status: [200]` 语义
|
||||
|
||||
#### Scenario: 配置 HTTP status 范围模式
|
||||
- **WHEN** HTTP target 配置 `expect.status: ["2xx"]`
|
||||
@@ -383,19 +389,23 @@
|
||||
|
||||
#### Scenario: 不配置 cmd exitCode
|
||||
- **WHEN** cmd target 未配置 `expect.exitCode`
|
||||
- **THEN** 系统 SHALL 在执行 expect 时使用默认 `exitCode: [0]` 语义
|
||||
- **THEN** 系统 SHALL 在 Resolved cmd expect 中物化默认 `exitCode: [0]` 语义
|
||||
|
||||
#### Scenario: 不配置 expect
|
||||
- **WHEN** target 未配置任何 expect 规则
|
||||
- **THEN** 系统 SHALL 正常处理,expect 字段为 undefined,并由各 checker 使用自身默认状态语义
|
||||
- **THEN** 系统 SHALL 正常处理,Raw expect 快照为 undefined,Resolved expect 由各 checker 物化自身默认状态语义
|
||||
|
||||
#### Scenario: Raw expect 不被语义校验修改
|
||||
- **WHEN** YAML 中配置 `expect.durationMs: 1000`
|
||||
- **THEN** 语义校验 SHALL 接受该 Raw primitive 简写且 MUST NOT 将 Raw 输入原地修改为 `{equals: 1000}`
|
||||
|
||||
#### Scenario: 旧 maxDurationMs 字段不再支持
|
||||
- **WHEN** YAML 中任一 target 配置 `expect.maxDurationMs`
|
||||
- **THEN** 系统 SHALL 在启动期配置校验失败,提示该字段未知,并要求使用 `expect.durationMs`
|
||||
|
||||
#### Scenario: 旧 match 字段不再支持
|
||||
- **WHEN** YAML 中任一 matcher 或内容规则配置 `match`
|
||||
- **THEN** 系统 SHALL 在启动期配置校验失败,提示该字段未知,并要求使用 `regex`
|
||||
- **WHEN** YAML 中任一 matcher 或内容 expectation 配置 `match`
|
||||
- **THEN** 系统 SHALL 在启动期配置校验失败,提示该字段未知或不支持,并要求使用 `regex`
|
||||
|
||||
#### Scenario: durationMs matcher 配置
|
||||
- **WHEN** YAML 中任一 target 配置 `expect.durationMs: {lte: 1000}`
|
||||
@@ -405,14 +415,18 @@
|
||||
- **WHEN** YAML 中 `http.headers`、`defaults.http.headers`、`llm.headers`、`defaults.llm.headers` 或 `expect.headers` 包含任意 header 名称,且对应值符合契约
|
||||
- **THEN** 系统 SHALL 接受这些动态 header 名称
|
||||
|
||||
#### Scenario: ContentRules 字段必须为数组
|
||||
#### Scenario: ContentExpectations 字段必须为数组
|
||||
- **WHEN** YAML 中任一内容类 expect 字段配置为非数组
|
||||
- **THEN** 系统 SHALL 在启动期配置校验失败,提示该字段必须为规则数组
|
||||
- **THEN** 系统 SHALL 在启动期配置校验失败,提示该字段必须为 expectation 数组
|
||||
|
||||
#### Scenario: regex 字段非法
|
||||
- **WHEN** YAML 中任一 `regex` 不是字符串、不是可编译正则或存在 ReDoS 风险
|
||||
- **THEN** 系统 SHALL 在启动期配置校验失败,提示对应 regex 不合法
|
||||
|
||||
#### Scenario: Store 持久化 Raw expect
|
||||
- **WHEN** 系统同步已解析 target 到 targets 表
|
||||
- **THEN** `targets.expect` SHALL 存储变量替换后的 Raw expect JSON,而不是包含 `kind` 或 resolved matcher 的运行期执行计划
|
||||
|
||||
### Requirement: 数据保留配置字段
|
||||
配置 schema 的 `runtime` 段 SHALL 支持 `retention` 字段,类型为字符串,格式为 `<数字><单位>`(单位:`d` 天、`h` 小时、`m` 分钟),用于指定历史数据保留时长。
|
||||
|
||||
@@ -498,7 +512,7 @@
|
||||
- **THEN** target schema SHALL 声明 `id` 的 minLength 为 1、maxLength 为 30,并声明 `name` 为可选字段,类型为 string 或 null,字符串的 minLength 为 1、maxLength 为 30
|
||||
|
||||
### Requirement: TCP 配置校验
|
||||
系统 SHALL 在启动期对 tcp checker 的配置契约和语义执行严格校验。Tcp target 的 `tcp` 分组 SHALL 只允许 `host`、`port`、`readBanner`、`bannerReadTimeout` 和 `maxBannerBytes` 字段;Tcp expect SHALL 只允许 `connected`、`durationMs` 和 `banner` 字段。`banner` MUST 为 `ContentRules` 数组。未知字段、非法类型、非法端口、非法 size 和不可编译正则 MUST 导致启动期配置错误。
|
||||
系统 SHALL 在启动期对 tcp checker 的配置契约和语义执行严格校验。Tcp target 的 `tcp` 分组 SHALL 只允许 `host`、`port`、`readBanner`、`bannerReadTimeout` 和 `maxBannerBytes` 字段;Tcp expect SHALL 只允许 `connected`、`durationMs` 和 `banner` 字段。`banner` MUST 为 `RawContentExpectations` 数组,`durationMs` SHALL 为 `RawValueExpectation`。未知字段、非法类型、非法端口、非法 size、非法 ContentExpectations 和不可编译正则 MUST 导致启动期配置错误。语义校验 MUST NOT 修改 Raw tcp expect 输入。
|
||||
|
||||
#### Scenario: tcp host 类型非法
|
||||
- **WHEN** YAML 中 tcp target 的 `tcp.host` 不是非空字符串
|
||||
@@ -525,7 +539,7 @@
|
||||
- **THEN** 系统 SHALL 以配置错误退出,提示 expect.connected 必须为布尔值
|
||||
|
||||
#### Scenario: tcp expect banner 非法
|
||||
- **WHEN** YAML 中 tcp target 的 `expect.banner` 不是合法 ContentRules 数组
|
||||
- **WHEN** YAML 中 tcp target 的 `expect.banner` 不是合法 `RawContentExpectations` 数组
|
||||
- **THEN** 系统 SHALL 以配置错误退出,提示 expect.banner 格式错误
|
||||
|
||||
#### Scenario: tcp expect banner regex 正则非法
|
||||
@@ -541,7 +555,7 @@
|
||||
- **THEN** 系统 SHALL 以配置错误退出,提示 defaults.tcp 包含未知字段
|
||||
|
||||
### Requirement: LLM 配置校验
|
||||
系统 SHALL 在启动期对 llm checker 的配置契约和语义执行严格校验。LLM target 的 `llm` 分组 SHALL 只允许 `provider`、`url`、`model`、`prompt`、`mode`、`key`、`authToken`、`headers`、`ignoreSSL`、`options` 和 `providerOptions` 字段。`defaults.llm` 分组 SHALL 只允许 `mode`、`headers`、`ignoreSSL`、`options` 和 `providerOptions` 字段。LLM expect SHALL 只允许 `status`、`headers`、`output`、`finishReason`、`rawFinishReason`、`usage`、`stream` 和 `durationMs` 字段。`expect.output` MUST 为 `ContentRules` 数组。`expect.finishReason` 和 `expect.rawFinishReason` SHALL 使用 `ValueMatcher`。`expect.usage.*` 和 `expect.stream.firstTokenMs` SHALL 使用 `ValueMatcher`。未知字段、非法 provider、非法 URL、非法 mode、非法认证组合、非法 options、非法 output 规则和 `mode: http` 下配置 `expect.stream` MUST 导致启动期配置错误。
|
||||
系统 SHALL 在启动期对 llm checker 的配置契约和语义执行严格校验。LLM target 的 `llm` 分组 SHALL 只允许 `provider`、`url`、`model`、`prompt`、`mode`、`key`、`authToken`、`headers`、`ignoreSSL`、`options` 和 `providerOptions` 字段。`defaults.llm` 分组 SHALL 只允许 `mode`、`headers`、`ignoreSSL`、`options` 和 `providerOptions` 字段。LLM expect SHALL 只允许 `status`、`headers`、`output`、`finishReason`、`rawFinishReason`、`usage`、`stream` 和 `durationMs` 字段。`expect.output` MUST 为 `RawContentExpectations` 数组。`expect.finishReason`、`expect.rawFinishReason`、`expect.usage.*`、`expect.stream.firstTokenMs` 和 `expect.durationMs` SHALL 使用 `RawValueExpectation`。未知字段、非法 provider、非法 URL、非法 mode、非法认证组合、非法 options、非法 output expectation 和 `mode: http` 下配置 `expect.stream` MUST 导致启动期配置错误。语义校验 MUST NOT 修改 Raw llm expect 输入。
|
||||
|
||||
#### Scenario: llm provider 非法
|
||||
- **WHEN** YAML 中 llm target 的 `llm.provider` 不是 `openai`、`openai-responses` 或 `anthropic`
|
||||
@@ -591,24 +605,24 @@
|
||||
- **WHEN** YAML 中 llm target 配置 `api`、`providerName`、`baseURL`、`apiKey`、`messages`、`maxRetries`、`request`、`maxBodyBytes` 或 `maxStreamBytes`
|
||||
- **THEN** 系统 SHALL 以配置错误退出,提示 llm 分组包含未知字段
|
||||
|
||||
#### Scenario: llm output 规则缺少支持字段
|
||||
#### Scenario: llm output expectation 缺少支持字段
|
||||
- **WHEN** YAML 中 llm target 的 `expect.output` 数组项未包含任何合法 ValueMatcher 字段或 extractor
|
||||
- **THEN** 系统 SHALL 以配置错误退出,提示 output rule 缺少支持的规则类型
|
||||
- **THEN** 系统 SHALL 以配置错误退出,提示 output expectation 缺少支持的 expectation 类型
|
||||
|
||||
#### Scenario: llm output 规则同时配置多个 extractor
|
||||
- **WHEN** YAML 中 llm target 的同一条 output rule 同时包含 json、css、xpath 中的多个 extractor
|
||||
- **THEN** 系统 SHALL 以配置错误退出,提示每条 output rule 只能配置一种 extractor
|
||||
#### Scenario: llm output expectation 同时配置多个 extractor
|
||||
- **WHEN** YAML 中 llm target 的同一条 output expectation 同时包含 json、css、xpath 中的多个 extractor
|
||||
- **THEN** 系统 SHALL 以配置错误退出,提示每条 output expectation 只能配置一种 extractor
|
||||
|
||||
#### Scenario: llm output regex 非法
|
||||
- **WHEN** YAML 中 llm target 的 output regex 规则不是字符串、不是可编译正则表达式或存在 ReDoS 风险
|
||||
- **WHEN** YAML 中 llm target 的 output regex expectation 不是字符串、不是可编译正则表达式或存在 ReDoS 风险
|
||||
- **THEN** 系统 SHALL 以配置错误退出,提示该 output regex 不合法
|
||||
|
||||
#### Scenario: llm output json path 非法
|
||||
- **WHEN** YAML 中 llm target 的 output json 规则缺少 path,或 path 不符合系统支持的 JSONPath 子集
|
||||
- **WHEN** YAML 中 llm target 的 output json expectation 缺少 path,或 path 不符合系统支持的 JSONPath 子集
|
||||
- **THEN** 系统 SHALL 以配置错误退出,提示该 output json path 不合法
|
||||
|
||||
#### Scenario: llm expect usage 非法
|
||||
- **WHEN** YAML 中 llm target 的 `expect.usage.inputTokens`、`expect.usage.outputTokens` 或 `expect.usage.totalTokens` 不是合法 `ValueMatcher`
|
||||
- **WHEN** YAML 中 llm target 的 `expect.usage.inputTokens`、`expect.usage.outputTokens` 或 `expect.usage.totalTokens` 不是合法 `RawValueExpectation`
|
||||
- **THEN** 系统 SHALL 以配置错误退出,提示 expect.usage 格式错误
|
||||
|
||||
#### Scenario: llm expect stream 仅允许 stream mode
|
||||
@@ -616,5 +630,5 @@
|
||||
- **THEN** 系统 SHALL 以配置错误退出,提示 expect.stream 仅支持 stream mode
|
||||
|
||||
#### Scenario: llm expect stream firstTokenMs 非法
|
||||
- **WHEN** YAML 中 llm target 的 `expect.stream.firstTokenMs` 不是合法 `ValueMatcher`
|
||||
- **WHEN** YAML 中 llm target 的 `expect.stream.firstTokenMs` 不是合法 `RawValueExpectation`
|
||||
- **THEN** 系统 SHALL 以配置错误退出,提示 expect.stream.firstTokenMs 格式错误
|
||||
|
||||
@@ -30,11 +30,11 @@
|
||||
- **THEN** check_results 表的外键约束 SHALL 使用 `ON DELETE RESTRICT`,确保删除 target 时数据库层面阻止操作而非级联删除关联记录
|
||||
|
||||
### Requirement: targets 表同步
|
||||
系统 SHALL 在启动时将 YAML 配置中的目标列表同步到 SQLite targets 表,并持久化 target 类型、展示名称元信息、展示摘要、领域配置、调度配置、expect 配置、分组信息和目标说明。配置中不存在的 target SHALL 被标记为非活跃而非删除。
|
||||
系统 SHALL 在启动时将 YAML 配置中的目标列表同步到 SQLite targets 表,并持久化 target 类型、展示名称元信息、展示摘要、领域配置、调度配置、变量替换后的 Raw expect 配置快照、分组信息和目标说明。配置中不存在的 target SHALL 被标记为非活跃而非删除。`targets.expect` SHALL 存储 Raw expect JSON;系统 MUST NOT 将 Resolved expect 执行计划、`ContentExpectation.kind` union 或已归一化 matcher 包装结构写入该列。
|
||||
|
||||
#### Scenario: 首次同步目标
|
||||
- **WHEN** 数据库为空且 YAML 中定义了 N 个 typed target
|
||||
- **THEN** 系统 SHALL 将所有目标插入 targets 表,包含 name、description、type、target、config、interval_ms、timeout_ms、expect、grp 和 active=1,其中 name 和 description 均可为 NULL
|
||||
- **THEN** 系统 SHALL 将所有目标插入 targets 表,包含 name、description、type、target、config、interval_ms、timeout_ms、expect、grp 和 active=1,其中 name 和 description 均可为 NULL,expect 列保存变量替换后的 Raw expect JSON
|
||||
|
||||
#### Scenario: 配置变更后重新同步
|
||||
- **WHEN** YAML 配置发生变更(新增、删除或修改目标)后重启
|
||||
@@ -64,6 +64,14 @@
|
||||
- **WHEN** YAML target 配置 `description: null`
|
||||
- **THEN** targets 表 SHALL 将该目标的 description 存储为 NULL
|
||||
|
||||
#### Scenario: expect 列保存 Raw expect
|
||||
- **WHEN** target 配置 `expect.body: [{json: {path: "$.status"}}]` 和 `expect.durationMs: 1000`
|
||||
- **THEN** targets 表的 expect 列 SHALL 保存变量替换后的 Raw JSON,包含原始 `json.path` 和 `durationMs: 1000`,MUST NOT 保存 resolved `kind` 字段或 `{equals: 1000}` 执行计划
|
||||
|
||||
#### Scenario: 未配置 expect 写入 NULL
|
||||
- **WHEN** target 未配置任何 expect
|
||||
- **THEN** targets 表的 expect 列 SHALL 写入 NULL,即使 Resolved expect 中存在 checker 默认状态语义
|
||||
|
||||
### Requirement: check_results 表追加写入
|
||||
系统 SHALL 将每次检查结果追加写入 check_results 表,不更新或删除已有记录。
|
||||
|
||||
|
||||
@@ -121,11 +121,11 @@
|
||||
- **THEN** 系统 SHALL 正常记录执行结果并进入 expect 校验
|
||||
|
||||
### Requirement: expect 校验
|
||||
系统 SHALL 在 checker 执行完成后根据目标类型的 expect 配置校验观测结果,校验结果和首个失败原因记入 check result。HTTP checker 的 `durationMs` SHALL 表示完整 checker 执行耗时,包括重定向、响应体读取、响应体解码和 expect 校验。
|
||||
系统 SHALL 在 checker 执行完成后根据目标类型的 Resolved expect 执行计划校验观测结果,校验结果和首个失败原因记入 check result。HTTP checker 的 `durationMs` SHALL 表示完整 checker 执行耗时,包括重定向、响应体读取、响应体解码和 expect 校验。HTTP `expect.durationMs` SHALL 使用 `RawValueExpectation` 输入并在 resolve 阶段转换为运行期 `ValueExpectation`;旧 `expect.maxDurationMs` MUST NOT 再作为运行期耗时阈值使用。
|
||||
|
||||
#### Scenario: HTTP 默认状态码
|
||||
- **WHEN** HTTP target 未配置 `expect.status`
|
||||
- **THEN** 系统 SHALL 按默认 `status: [200]` 校验响应状态码
|
||||
- **THEN** 系统 SHALL 在 Resolved HTTP expect 中物化默认 `status: [200]` 并按该语义校验响应状态码
|
||||
|
||||
#### Scenario: 校验 HTTP 状态码精确值
|
||||
- **WHEN** HTTP target 配置了 `expect.status: [200, 201]`
|
||||
@@ -144,39 +144,39 @@
|
||||
- **THEN** 系统 SHALL 检查响应头是否符合指定规则,全部匹配时继续后续阶段
|
||||
|
||||
#### Scenario: 校验 HTTP 响应体
|
||||
- **WHEN** HTTP target 配置了有序 `expect.body` 规则数组
|
||||
- **THEN** 系统 SHALL 按数组顺序执行 body 规则,任一失败立即记录 failure 并停止后续规则
|
||||
- **WHEN** HTTP target 配置了有序 `expect.body` ContentExpectations 数组
|
||||
- **THEN** 系统 SHALL 按数组顺序执行 body expectations,任一失败立即记录 failure 并停止后续 expectation
|
||||
|
||||
#### Scenario: 校验 HTTP 完整耗时阈值
|
||||
- **WHEN** 目标配置了 `expect.maxDurationMs`,且 HTTP checker 完整执行(含重定向、body 读取、解码和 expect)后的 durationMs 超过阈值
|
||||
- **WHEN** 目标配置了 `expect.durationMs: {lte: 1000}`,且 HTTP checker 完整执行(含重定向、body 读取、解码和 expect)后的 durationMs 超过阈值
|
||||
- **THEN** 系统 SHALL 判定 duration 不匹配,记录完整 durationMs 和 duration failure
|
||||
|
||||
#### Scenario: HTTP body 前耗时已超阈值
|
||||
- **WHEN** HTTP target 配置了 body 校验和 `expect.maxDurationMs`,且进入 body 读取前的已耗时已超过阈值
|
||||
#### Scenario: HTTP body 前耗时已不可能满足 durationMs 上界
|
||||
- **WHEN** HTTP target 配置了 body 校验和 `expect.durationMs` 上界 matcher(如 `{lte: 1000}`),且进入 body 读取前的已耗时已使该 matcher 不可能通过
|
||||
- **THEN** 系统 SHALL 直接返回 duration failure,且 MUST NOT 读取 response body
|
||||
|
||||
#### Scenario: HTTP body 失败优先于后续 duration 检查
|
||||
- **WHEN** HTTP target 配置了 body 校验和 `expect.maxDurationMs`,body 阶段存在失败,且完整执行后 duration 也超过阈值
|
||||
- **WHEN** HTTP target 配置了 body 校验和 `expect.durationMs: {lte: 1000}`,body 阶段存在失败,且完整执行后 duration 也超过阈值
|
||||
- **THEN** 系统 SHALL 返回 body 阶段的失败(首个失败为准),durationMs SHALL 记录完整耗时
|
||||
|
||||
#### Scenario: HTTP 慢响应体计入耗时
|
||||
- **WHEN** HTTP target 配置了 body 校验和 `expect.maxDurationMs`,且响应头很快返回但响应体读取导致完整执行耗时超过阈值
|
||||
- **WHEN** HTTP target 配置了 body 校验和 `expect.durationMs: {lte: 1000}`,且响应头很快返回但响应体读取导致完整执行耗时超过阈值
|
||||
- **THEN** 系统 SHALL 判定 duration 不匹配并记录完整 durationMs
|
||||
|
||||
#### Scenario: 多条 expect 规则
|
||||
- **WHEN** 目标同时配置状态、duration、元数据和内容规则
|
||||
- **THEN** 系统 SHALL 所有规则全部通过时 matched 为 true,任一不通过则为 false 并记录首个失败原因
|
||||
- **WHEN** 目标同时配置状态、duration、元数据和内容 expectations
|
||||
- **THEN** 系统 SHALL 所有 expectations 全部通过时 matched 为 true,任一不通过则为 false 并记录首个失败原因
|
||||
|
||||
#### Scenario: cmd 默认 exitCode
|
||||
- **WHEN** cmd target 未配置 `expect.exitCode`
|
||||
- **THEN** 系统 SHALL 按默认 `exitCode: [0]` 校验命令退出码
|
||||
- **THEN** 系统 SHALL 在 Resolved cmd expect 中物化默认 `exitCode: [0]` 并按该语义校验命令退出码
|
||||
|
||||
#### Scenario: 校验 cmd stdout
|
||||
- **WHEN** cmd target 配置了有序 `expect.stdout` 规则数组
|
||||
- **THEN** 系统 SHALL 按数组顺序执行 stdout 规则,任一失败立即记录 failure 并停止后续规则
|
||||
- **WHEN** cmd target 配置了有序 `expect.stdout` ContentExpectations 数组
|
||||
- **THEN** 系统 SHALL 按数组顺序执行 stdout expectations,任一失败立即记录 failure 并停止后续 expectation
|
||||
|
||||
### Requirement: Body 校验按需解析
|
||||
系统 SHALL 仅在 HTTP target 配置了 body 校验,且 status、headers 阶段均通过,并且进入 body 前未确定 duration 已失败时才读取并解析响应体,避免不必要的读取和解析开销。HTTP target 未配置 body 校验时,系统 SHALL NOT 读取 response body。
|
||||
系统 SHALL 仅在 HTTP target 配置了 body 校验,且 status、headers 阶段均通过,并且进入 body 前未确定 `expect.durationMs` 已失败时才读取并解析响应体,避免不必要的读取和解析开销。HTTP target 未配置 body 校验时,系统 SHALL NOT 读取 response body。仅当 Resolved `durationMs` 包含上界 matcher 且当前已耗时已经使其不可能通过时,系统 MAY 在读取 body 前返回 duration failure;其他 duration matcher SHALL 在完整执行耗时可用后校验。
|
||||
|
||||
#### Scenario: status 失败时不读取 body
|
||||
- **WHEN** HTTP target 的 status 阶段不匹配
|
||||
@@ -186,17 +186,17 @@
|
||||
- **WHEN** HTTP target 的 status 阶段匹配但 headers 阶段不匹配
|
||||
- **THEN** 系统 SHALL 立即返回 matched=false,且 MUST NOT 读取 response body
|
||||
|
||||
#### Scenario: 进入 body 前 duration 已失败时不读取 body
|
||||
- **WHEN** HTTP target 已配置 `expect.maxDurationMs`,且进入 body 读取前的已耗时已经超过阈值
|
||||
#### Scenario: 进入 body 前 durationMs 上界已失败时不读取 body
|
||||
- **WHEN** HTTP target 已配置 `expect.durationMs` 上界 matcher,且进入 body 读取前的已耗时已经使该 matcher 不可能通过
|
||||
- **THEN** 系统 SHALL 返回 duration failure,且 MUST NOT 读取 response body
|
||||
|
||||
#### Scenario: 仅配置 contains 时不解析 JSON
|
||||
- **WHEN** HTTP target 仅配置 body contains 规则而未配置 json/css/xpath 规则
|
||||
- **WHEN** HTTP target 仅配置 body contains expectation 而未配置 json/css/xpath expectation
|
||||
- **THEN** 系统 SHALL 不执行 JSON.parse 或 HTML/XML 解析
|
||||
|
||||
#### Scenario: 配置 json 时解析 JSON 失败
|
||||
- **WHEN** HTTP target 配置了 body json 规则但响应体不是合法 JSON
|
||||
- **THEN** 系统 SHALL 判定 matched 为 false,并记录 json 规则对应的 failure.path
|
||||
- **WHEN** HTTP target 配置了 body json expectation 但响应体不是合法 JSON
|
||||
- **THEN** 系统 SHALL 判定 matched 为 false,并记录 json expectation 对应的 failure.path
|
||||
|
||||
### Requirement: HTTP 运行期错误归属
|
||||
HTTP checker SHALL 将运行期失败归属到实际失败阶段。请求、网络、TLS 和 timeout 错误 SHALL 记录为 request 阶段错误;body 超限、响应体解码失败、响应内容解析失败 SHALL 记录为 body 阶段错误;expect 不匹配 SHALL 记录为对应 mismatch 阶段。
|
||||
|
||||
@@ -86,27 +86,27 @@
|
||||
- **THEN** observation.banner SHALL 保存截断后的 banner 摘要,API detail SHALL 展示截断后的 banner 摘要,避免 UI 展示过长文本
|
||||
|
||||
### Requirement: tcp expect 校验
|
||||
系统 SHALL 支持 tcp 专属 expect,包括 `connected`、`banner` 和 `durationMs`,并按 connected、banner、durationMs 的阶段顺序快速失败。`connected` SHALL 保持布尔状态语义,未配置时默认 `true`。`banner` MUST 使用共享 `ContentRules` 数组,并仅在 `tcp.readBanner: true` 时允许配置。`durationMs` SHALL 使用共享 `ValueMatcher` 校验包含连接和 banner 读取在内的完整执行耗时。
|
||||
系统 SHALL 支持 tcp 专属 expect,包括 `connected`、`banner` 和 `durationMs`,并按 connected、banner、durationMs 的阶段顺序快速失败。`connected` SHALL 保持布尔状态语义,未配置时在 Resolved expect 中默认 `true`。`banner` MUST 使用共享 `RawContentExpectations` 数组输入并在运行期使用 `ContentExpectations`,且仅在 `tcp.readBanner: true` 时允许配置。`durationMs` SHALL 使用共享 `RawValueExpectation` 输入并在运行期使用 `ValueExpectation` 校验包含连接和 banner 读取在内的完整执行耗时。
|
||||
|
||||
#### Scenario: 默认 connected 成功语义
|
||||
- **WHEN** tcp target 未显式配置 `expect.connected`
|
||||
- **THEN** 系统 SHALL 使用默认 `expect.connected: true` 进行校验
|
||||
- **THEN** 系统 SHALL 在 Resolved tcp expect 中使用默认 `connected: true` 进行校验
|
||||
|
||||
#### Scenario: durationMs 校验
|
||||
- **WHEN** tcp target 配置 `expect.durationMs: {lte: 100}`,且完整执行耗时超过 100ms
|
||||
- **THEN** 系统 SHALL 返回 `matched=false`,failure 的 phase 为 `duration`
|
||||
|
||||
#### Scenario: banner ContentRules 校验通过
|
||||
#### Scenario: banner ContentExpectations 校验通过
|
||||
- **WHEN** tcp target 配置 `readBanner: true`、`expect.banner: [{contains: "ESMTP"}]`,且实际 banner 包含 `ESMTP`
|
||||
- **THEN** 系统 SHALL 判定 banner 阶段通过
|
||||
|
||||
#### Scenario: banner regex 校验失败
|
||||
- **WHEN** tcp target 配置 `readBanner: true`、`expect.banner: [{regex: "^SSH-2\\.0"}]`,且实际 banner 不匹配该正则
|
||||
- **THEN** 系统 SHALL 返回 `matched=false`,failure 的 kind 为 `mismatch`,phase 为 `banner`,path 指向失败的 banner 规则
|
||||
- **THEN** 系统 SHALL 返回 `matched=false`,failure 的 kind 为 `mismatch`,phase 为 `banner`,path 指向失败的 banner expectation
|
||||
|
||||
#### Scenario: banner 多规则快速失败
|
||||
- **WHEN** tcp target 配置两条 banner 规则且第一条失败
|
||||
- **THEN** 系统 SHALL 返回第一条失败规则的 failure,并 MUST NOT 执行第二条规则
|
||||
- **WHEN** tcp target 配置两条 banner expectation 且第一条失败
|
||||
- **THEN** 系统 SHALL 返回第一条失败 expectation 的 failure,并 MUST NOT 执行第二条 expectation
|
||||
|
||||
#### Scenario: expect.banner 未开启 readBanner
|
||||
- **WHEN** tcp target 配置 `expect.banner`,但 `tcp.readBanner` 未配置为 `true`
|
||||
|
||||
@@ -133,13 +133,13 @@
|
||||
- **THEN** 系统 SHALL 以配置错误退出,提示 maxResponseBytes 格式错误
|
||||
|
||||
### Requirement: udp expect 校验
|
||||
系统 SHALL 支持 udp 专属 expect,包括 `responded`、`response`、`responseSize`、`sourceHost`、`sourcePort` 和 `durationMs`,并按 responded、responseSize、response、sourceHost、sourcePort、durationMs 的阶段顺序快速失败。`responded` SHALL 保持布尔状态语义,未配置时默认 `true`。`response` MUST 使用共享 `ContentRules` 数组,并作用于按 `udp.responseEncoding` 转换后的响应文本。`responseSize`、`sourceHost`、`sourcePort` 和 `durationMs` SHALL 使用共享 `ValueMatcher`。
|
||||
系统 SHALL 支持 udp 专属 expect,包括 `responded`、`response`、`responseSize`、`sourceHost`、`sourcePort` 和 `durationMs`,并按 responded、responseSize、response、sourceHost、sourcePort、durationMs 的阶段顺序快速失败。`responded` SHALL 保持布尔状态语义,未配置时在 Resolved expect 中默认 `true`。`response` MUST 使用共享 `RawContentExpectations` 数组输入并在运行期使用 `ContentExpectations`,且作用于按 `udp.responseEncoding` 转换后的响应文本。`responseSize`、`sourceHost`、`sourcePort` 和 `durationMs` SHALL 使用共享 `RawValueExpectation` 输入并在运行期使用 `ValueExpectation`。
|
||||
|
||||
#### Scenario: 默认 responded 成功语义
|
||||
- **WHEN** udp target 未显式配置 `expect.responded`
|
||||
- **THEN** 系统 SHALL 使用默认 `expect.responded: true` 进行校验
|
||||
- **THEN** 系统 SHALL 在 Resolved udp expect 中使用默认 `responded: true` 进行校验
|
||||
|
||||
#### Scenario: response ContentRules 校验通过
|
||||
#### Scenario: response ContentExpectations 校验通过
|
||||
- **WHEN** udp target 配置 `expect.response: [{ contains: "PONG" }]`,且按 `responseEncoding` 转换后的响应文本包含 `PONG`
|
||||
- **THEN** 系统 SHALL 判定 response 阶段通过
|
||||
|
||||
@@ -147,13 +147,13 @@
|
||||
- **WHEN** udp target 收到文本响应 `{"status":"ok"}` 且配置 `expect.response: [{json: {path: "$.status", equals: "ok"}}]`
|
||||
- **THEN** 系统 SHALL 判定 response 阶段通过
|
||||
|
||||
#### Scenario: response ContentRules 校验失败
|
||||
#### Scenario: response ContentExpectations 校验失败
|
||||
- **WHEN** udp target 配置 `expect.response: [{ contains: "PONG" }]`,但按 `responseEncoding` 转换后的响应文本不包含 `PONG`
|
||||
- **THEN** 系统 SHALL 返回 `matched=false`,failure 的 kind 为 `mismatch`,phase 为 `response`,path 指向失败的 response 规则
|
||||
- **THEN** 系统 SHALL 返回 `matched=false`,failure 的 kind 为 `mismatch`,phase 为 `response`,path 指向失败的 response expectation
|
||||
|
||||
#### Scenario: responseEncoding 为 hex
|
||||
- **WHEN** udp target 配置 `udp.responseEncoding: "hex"` 且收到字节内容 `PONG`
|
||||
- **THEN** 系统 SHALL 将响应转换为小写 hex 字符串 `504f4e47` 后执行 `expect.response` 规则
|
||||
- **THEN** 系统 SHALL 将响应转换为小写 hex 字符串 `504f4e47` 后执行 `expect.response` expectation
|
||||
|
||||
#### Scenario: responseSize matcher 校验通过
|
||||
- **WHEN** udp target 配置 `expect.responseSize: { gte: 4 }`,且实际响应为 4 字节
|
||||
|
||||
Reference in New Issue
Block a user