refactor: 统一 expect 断言体系,引入共享 ValueMatcher/ContentRules/KeyValueExpect 模型
- 引入共享 ValueMatcher(equals/contains/regex/exists/empty/gt/gte/lt/lte) - 引入共享 ContentRules 数组(direct/json/css/xpath 提取器) - 引入共享 KeyValueExpect(动态键值断言,字面量等价 equals) - maxDurationMs → durationMs: ValueMatcher(所有 checker) - match → regex(固定无 flags) - Ping max* → packetLossPercent/avgLatencyMs/maxLatencyMs(ValueMatcher) - LLM finishReason/rawFinishReason → ValueMatcher - DB 新增 result: ContentRules - TCP banner → ContentRules 数组 - 删除旧模块:operator.ts、validate-operator.ts、duration.ts、body.ts、text.ts、output.ts - 更新全部 checker schema/validate/expect/execute - 更新 probe-config.schema.json、probes.example.yaml - 更新 README.md、DEVELOPMENT.md(含 expect 字段选择规范) - 同步 10 个 delta specs 到主 specs,归档 change
This commit is contained in:
@@ -193,9 +193,9 @@
|
||||
- **WHEN** YAML 中某个 HTTP target 的 `expect.status` 包含非整数或不在 100-599 范围内的数字
|
||||
- **THEN** 系统 SHALL 以错误退出,提示该 target 的 status 数字不合法
|
||||
|
||||
#### Scenario: maxDurationMs 非法
|
||||
- **WHEN** YAML 中某个 target 的 `expect.maxDurationMs` 不是非负有限数字
|
||||
- **THEN** 系统 SHALL 以错误退出,提示该 target 的 expect.maxDurationMs 格式错误
|
||||
#### Scenario: durationMs matcher 非法
|
||||
- **WHEN** YAML 中某个 target 的 `expect.durationMs` 不是合法 `ValueMatcher`
|
||||
- **THEN** 系统 SHALL 以错误退出,提示该 target 的 expect.durationMs 格式错误
|
||||
|
||||
#### Scenario: ping target 缺少 host
|
||||
- **WHEN** YAML 中某个 target 配置 `type: ping` 但缺少 `ping.host`
|
||||
@@ -237,13 +237,13 @@
|
||||
- **WHEN** YAML 中某个 HTTP target 的 body xpath 规则缺少 path,或 path 不是非空字符串,或可被现有 XPath 库静态判定为语法错误
|
||||
- **THEN** 系统 SHALL 以错误退出,提示该 body xpath path 不合法
|
||||
|
||||
#### Scenario: expect operator 类型非法
|
||||
- **WHEN** YAML 中某个 HTTP expect operator 的 match 不是可编译正则字符串,empty/exists 不是布尔值,或 gt/gte/lt/lte 不是有限数字
|
||||
- **THEN** 系统 SHALL 以错误退出,提示对应 operator 配置不合法
|
||||
#### Scenario: expect matcher 类型非法
|
||||
- **WHEN** YAML 中某个 expect matcher 的 regex 不是可编译正则字符串,empty/exists 不是布尔值,或 gt/gte/lt/lte 不是有限数字
|
||||
- **THEN** 系统 SHALL 以错误退出,提示对应 matcher 配置不合法
|
||||
|
||||
#### Scenario: expect operator 类型非法
|
||||
- **WHEN** YAML 中某个 expect operator 的 match 不是可编译正则字符串,empty/exists 不是布尔值,或 gt/gte/lt/lte 不是有限数字
|
||||
- **THEN** 系统 SHALL 以错误退出,提示对应 operator 配置不合法
|
||||
#### Scenario: expect match 字段不再支持
|
||||
- **WHEN** YAML 中某个 expect matcher 配置 `match` 字段
|
||||
- **THEN** 系统 SHALL 以错误退出,提示 `match` 是未知字段,请使用 `regex`
|
||||
|
||||
#### Scenario: unknown 字段失败
|
||||
- **WHEN** YAML 中任一结构化配置对象包含契约未声明的字段,且该对象不是明确允许动态键的对象
|
||||
@@ -301,18 +301,38 @@
|
||||
- **THEN** 系统 SHALL 调用 `Bun.YAML.parse()` 将内容解析为配置对象
|
||||
|
||||
### Requirement: expect 配置增强
|
||||
系统 SHALL 支持 typed target 的领域专用 expect 配置,包括 HTTP 的 `status`(支持精确数字和范围模式)、`headers`、`body`,cmd 的 `exitCode`、`stdout`、`stderr`,tcp 的 `connected`、`banner`,ping 的 `alive`、`maxPacketLoss`、`maxAvgLatencyMs`、`maxMaxLatencyMs`,udp 的 `responded`、`response`、`responseSize`、`sourceHost`、`sourcePort` 和 `maxDurationMs`,以及 llm 的 `status`、`headers`、`output`、`finishReason`、`rawFinishReason`、`usage`、`stream` 和 `maxDurationMs`。内容类 expect MUST 使用数组表达配置顺序。
|
||||
系统 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)。
|
||||
|
||||
#### Scenario: 解析 HTTP expect 配置
|
||||
- **WHEN** YAML 配置文件中 HTTP target 的 expect 包含 status、headers、body 规则数组及内部方法
|
||||
- **WHEN** YAML 配置文件中 HTTP target 的 expect 包含 status、headers、body 规则数组和 durationMs matcher
|
||||
- **THEN** 系统 SHALL 正确解析并存储为 HTTP target 的 expect 字段
|
||||
|
||||
#### Scenario: 解析 cmd expect 配置
|
||||
- **WHEN** YAML 配置文件中 cmd target 的 expect 包含 exitCode、stdout 和 stderr 规则数组
|
||||
- **WHEN** YAML 配置文件中 cmd target 的 expect 包含 exitCode、stdout、stderr 和 durationMs matcher
|
||||
- **THEN** 系统 SHALL 正确解析并存储为 cmd target 的 expect 字段
|
||||
|
||||
#### Scenario: 解析 body 有序规则数组
|
||||
- **WHEN** YAML 中 HTTP target 配置 `expect.body` 为 contains、json、regex 三个数组项
|
||||
#### Scenario: 解析 db expect 配置
|
||||
- **WHEN** YAML 配置文件中 db target 的 expect 包含 durationMs、rowCount、rows 和 result
|
||||
- **THEN** 系统 SHALL 正确解析并存储为 db target 的 expect 字段
|
||||
|
||||
#### Scenario: 解析 tcp expect 配置
|
||||
- **WHEN** YAML 配置文件中 tcp target 的 expect 包含 connected、banner 规则数组和 durationMs matcher
|
||||
- **THEN** 系统 SHALL 正确解析并存储为 tcp target 的 expect 字段
|
||||
|
||||
#### Scenario: 解析 ping expect 配置
|
||||
- **WHEN** YAML 配置文件中 ping target 的 expect 包含 alive、packetLossPercent、avgLatencyMs、maxLatencyMs 和 durationMs matcher
|
||||
- **THEN** 系统 SHALL 正确解析并存储为 ping target 的 expect 字段
|
||||
|
||||
#### Scenario: 解析 udp expect 配置
|
||||
- **WHEN** YAML 配置文件中 udp target 的 expect 包含 responded、response、responseSize、sourceHost、sourcePort 和 durationMs matcher
|
||||
- **THEN** 系统 SHALL 正确解析并存储为 udp target 的 expect 字段
|
||||
|
||||
#### Scenario: 解析 llm expect 配置
|
||||
- **WHEN** YAML 配置文件中 llm target 的 expect 包含 status、headers、output、finishReason、rawFinishReason、usage、stream 和 durationMs matcher
|
||||
- **THEN** 系统 SHALL 正确解析并存储为 llm target 的 expect 字段,并保留 output 内容规则数组顺序
|
||||
|
||||
#### Scenario: 解析有序 ContentRules 数组
|
||||
- **WHEN** YAML 中任一内容类 expect 配置 contains、json、regex 三个数组项
|
||||
- **THEN** 系统 SHALL 保留数组顺序,供执行阶段按配置顺序快速失败
|
||||
|
||||
#### Scenario: 不配置 HTTP status
|
||||
@@ -323,41 +343,37 @@
|
||||
- **WHEN** HTTP target 配置 `expect.status: ["2xx"]`
|
||||
- **THEN** 系统 SHALL 在执行 expect 时匹配所有 200-299 状态码
|
||||
|
||||
#### Scenario: 配置 HTTP status 混合模式
|
||||
- **WHEN** HTTP target 配置 `expect.status: ["2xx", 301]`
|
||||
- **THEN** 系统 SHALL 在执行 expect 时匹配所有 200-299 状态码或精确匹配 301
|
||||
|
||||
#### Scenario: 不配置 cmd exitCode
|
||||
- **WHEN** cmd target 未配置 `expect.exitCode`
|
||||
- **THEN** 系统 SHALL 在执行 expect 时使用默认 `exitCode: [0]` 语义
|
||||
|
||||
#### Scenario: 不配置 expect
|
||||
- **WHEN** target 未配置任何 expect 规则
|
||||
- **THEN** 系统 SHALL 正常处理,expect 字段为 undefined
|
||||
- **THEN** 系统 SHALL 正常处理,expect 字段为 undefined,并由各 checker 使用自身默认状态语义
|
||||
|
||||
#### Scenario: 解析 ping expect 配置
|
||||
- **WHEN** YAML 配置文件中 ping target 的 expect 包含 alive、maxPacketLoss、maxAvgLatencyMs、maxMaxLatencyMs 和 maxDurationMs
|
||||
- **THEN** 系统 SHALL 正确解析并存储为 ping target 的 expect 字段
|
||||
#### Scenario: 旧 maxDurationMs 字段不再支持
|
||||
- **WHEN** YAML 中任一 target 配置 `expect.maxDurationMs`
|
||||
- **THEN** 系统 SHALL 在启动期配置校验失败,提示该字段未知,并要求使用 `expect.durationMs`
|
||||
|
||||
#### Scenario: 不配置 ping expect
|
||||
- **WHEN** ping target 未配置任何 expect 规则
|
||||
- **THEN** 系统 SHALL 正常处理,expect 字段为 undefined,执行时使用默认 alive=true 语义
|
||||
#### Scenario: 旧 match 字段不再支持
|
||||
- **WHEN** YAML 中任一 matcher 或内容规则配置 `match`
|
||||
- **THEN** 系统 SHALL 在启动期配置校验失败,提示该字段未知,并要求使用 `regex`
|
||||
|
||||
#### Scenario: 解析 udp expect 配置
|
||||
- **WHEN** YAML 配置文件中 udp target 的 expect 包含 responded、response、responseSize、sourceHost、sourcePort 和 maxDurationMs
|
||||
- **THEN** 系统 SHALL 正确解析并存储为 udp target 的 expect 字段
|
||||
#### Scenario: durationMs matcher 配置
|
||||
- **WHEN** YAML 中任一 target 配置 `expect.durationMs: {lte: 1000}`
|
||||
- **THEN** 系统 SHALL 接受该配置并在运行期使用完整执行耗时进行 matcher 校验
|
||||
|
||||
#### Scenario: 不配置 udp expect
|
||||
- **WHEN** udp target 未配置任何 expect 规则
|
||||
- **THEN** 系统 SHALL 正常处理,expect 字段为 undefined,执行时使用默认 responded=true 语义
|
||||
#### Scenario: 动态 headers 字段允许
|
||||
- **WHEN** YAML 中 `http.headers`、`defaults.http.headers`、`llm.headers`、`defaults.llm.headers` 或 `expect.headers` 包含任意 header 名称,且对应值符合契约
|
||||
- **THEN** 系统 SHALL 接受这些动态 header 名称
|
||||
|
||||
#### Scenario: 解析 llm expect 配置
|
||||
- **WHEN** YAML 配置文件中 llm target 的 expect 包含 status、headers、output、finishReason、rawFinishReason、usage、stream 和 maxDurationMs
|
||||
- **THEN** 系统 SHALL 正确解析并存储为 llm target 的 expect 字段,并保留 output 规则数组顺序
|
||||
#### Scenario: ContentRules 字段必须为数组
|
||||
- **WHEN** YAML 中任一内容类 expect 字段配置为非数组
|
||||
- **THEN** 系统 SHALL 在启动期配置校验失败,提示该字段必须为规则数组
|
||||
|
||||
#### Scenario: 不配置 llm expect
|
||||
- **WHEN** llm target 未配置任何 expect 规则
|
||||
- **THEN** 系统 SHALL 正常处理,expect 字段为 undefined,执行时使用默认 status=[200] 语义
|
||||
#### Scenario: regex 字段非法
|
||||
- **WHEN** YAML 中任一 `regex` 不是字符串、不是可编译正则或存在 ReDoS 风险
|
||||
- **THEN** 系统 SHALL 在启动期配置校验失败,提示对应 regex 不合法
|
||||
|
||||
### Requirement: 数据保留配置字段
|
||||
配置 schema 的 `runtime` 段 SHALL 支持 `retention` 字段,类型为字符串,格式为 `<数字><单位>`(单位:`d` 天、`h` 小时、`m` 分钟),用于指定历史数据保留时长。
|
||||
@@ -444,7 +460,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`、`maxDurationMs` 和 `banner` 字段。未知字段、非法类型、非法端口、非法 size 和不可编译正则 MUST 导致启动期配置错误。
|
||||
系统 SHALL 在启动期对 tcp checker 的配置契约和语义执行严格校验。Tcp target 的 `tcp` 分组 SHALL 只允许 `host`、`port`、`readBanner`、`bannerReadTimeout` 和 `maxBannerBytes` 字段;Tcp expect SHALL 只允许 `connected`、`durationMs` 和 `banner` 字段。`banner` MUST 为 `ContentRules` 数组。未知字段、非法类型、非法端口、非法 size 和不可编译正则 MUST 导致启动期配置错误。
|
||||
|
||||
#### Scenario: tcp host 类型非法
|
||||
- **WHEN** YAML 中 tcp target 的 `tcp.host` 不是非空字符串
|
||||
@@ -471,11 +487,11 @@
|
||||
- **THEN** 系统 SHALL 以配置错误退出,提示 expect.connected 必须为布尔值
|
||||
|
||||
#### Scenario: tcp expect banner 非法
|
||||
- **WHEN** YAML 中 tcp target 的 `expect.banner` 不是合法 operator 对象
|
||||
- **WHEN** YAML 中 tcp target 的 `expect.banner` 不是合法 ContentRules 数组
|
||||
- **THEN** 系统 SHALL 以配置错误退出,提示 expect.banner 格式错误
|
||||
|
||||
#### Scenario: tcp expect banner match 正则非法
|
||||
- **WHEN** YAML 中 tcp target 配置 `expect.banner: { match: "[invalid" }`
|
||||
#### Scenario: tcp expect banner regex 正则非法
|
||||
- **WHEN** YAML 中 tcp target 配置 `expect.banner: [{ regex: "[invalid" }]`
|
||||
- **THEN** 系统 SHALL 在启动期配置校验失败,而不是延迟到运行期抛错
|
||||
|
||||
#### Scenario: tcp 分组未知字段失败
|
||||
@@ -487,7 +503,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` 和 `maxDurationMs` 字段。未知字段、非法 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 为 `ContentRules` 数组。`expect.finishReason` 和 `expect.rawFinishReason` SHALL 使用 `ValueMatcher`。`expect.usage.*` 和 `expect.stream.firstTokenMs` SHALL 使用 `ValueMatcher`。未知字段、非法 provider、非法 URL、非法 mode、非法认证组合、非法 options、非法 output 规则和 `mode: http` 下配置 `expect.stream` MUST 导致启动期配置错误。
|
||||
|
||||
#### Scenario: llm provider 非法
|
||||
- **WHEN** YAML 中 llm target 的 `llm.provider` 不是 `openai`、`openai-responses` 或 `anthropic`
|
||||
@@ -538,12 +554,12 @@
|
||||
- **THEN** 系统 SHALL 以配置错误退出,提示 llm 分组包含未知字段
|
||||
|
||||
#### Scenario: llm output 规则缺少支持字段
|
||||
- **WHEN** YAML 中 llm target 的 `expect.output` 数组项未包含 equals、contains、regex、json 任一支持字段
|
||||
- **WHEN** YAML 中 llm target 的 `expect.output` 数组项未包含任何合法 ValueMatcher 字段或 extractor
|
||||
- **THEN** 系统 SHALL 以配置错误退出,提示 output rule 缺少支持的规则类型
|
||||
|
||||
#### Scenario: llm output 规则同时配置多个支持字段
|
||||
- **WHEN** YAML 中 llm target 的同一条 output rule 同时包含 equals、contains、regex、json 中的多个支持字段
|
||||
- **THEN** 系统 SHALL 以配置错误退出,提示每条 output rule 只能配置一种规则类型
|
||||
#### Scenario: llm output 规则同时配置多个 extractor
|
||||
- **WHEN** YAML 中 llm target 的同一条 output rule 同时包含 json、css、xpath 中的多个 extractor
|
||||
- **THEN** 系统 SHALL 以配置错误退出,提示每条 output rule 只能配置一种 extractor
|
||||
|
||||
#### Scenario: llm output regex 非法
|
||||
- **WHEN** YAML 中 llm target 的 output regex 规则不是字符串、不是可编译正则表达式或存在 ReDoS 风险
|
||||
@@ -554,7 +570,7 @@
|
||||
- **THEN** 系统 SHALL 以配置错误退出,提示该 output json path 不合法
|
||||
|
||||
#### Scenario: llm expect usage 非法
|
||||
- **WHEN** YAML 中 llm target 的 `expect.usage.inputTokens`、`expect.usage.outputTokens` 或 `expect.usage.totalTokens` 不是合法 operator 对象
|
||||
- **WHEN** YAML 中 llm target 的 `expect.usage.inputTokens`、`expect.usage.outputTokens` 或 `expect.usage.totalTokens` 不是合法 `ValueMatcher`
|
||||
- **THEN** 系统 SHALL 以配置错误退出,提示 expect.usage 格式错误
|
||||
|
||||
#### Scenario: llm expect stream 仅允许 stream mode
|
||||
@@ -562,5 +578,5 @@
|
||||
- **THEN** 系统 SHALL 以配置错误退出,提示 expect.stream 仅支持 stream mode
|
||||
|
||||
#### Scenario: llm expect stream firstTokenMs 非法
|
||||
- **WHEN** YAML 中 llm target 的 `expect.stream.firstTokenMs` 不是合法 operator 对象
|
||||
- **WHEN** YAML 中 llm target 的 `expect.stream.firstTokenMs` 不是合法 `ValueMatcher`
|
||||
- **THEN** 系统 SHALL 以配置错误退出,提示 expect.stream.firstTokenMs 格式错误
|
||||
|
||||
Reference in New Issue
Block a user