1
0
Files
DiAL/openspec/specs/tcp-checker/spec.md
lanyuanxiaoyao 60a54b483f 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)
2026-05-20 16:12:48 +08:00

118 lines
7.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
## Purpose
定义 TCP checker 的配置格式、连接执行、banner 读取、expect 校验、失败结构和状态摘要。
## Requirements
### Requirement: tcp target 配置
系统 SHALL 支持 `type: tcp` 的 target 配置,通过 `tcp.host``tcp.port` 描述目标 TCP 地址,并通过可选字段控制 banner 读取行为。
#### Scenario: 解析最简 tcp target
- **WHEN** YAML 中 target 配置 `type: tcp``tcp.host: "127.0.0.1"``tcp.port: 6379`
- **THEN** 系统 SHALL 将其解析为 tcp checker并填充 `readBanner=false``bannerReadTimeout=2000``maxBannerBytes=4096`、interval、timeout、group 和 expect 配置
#### Scenario: tcp target 缺少 host
- **WHEN** YAML 中 target 配置 `type: tcp` 但缺少 `tcp.host`
- **THEN** 系统 SHALL 以配置错误退出,并提示该 target 缺少 tcp.host 字段
#### Scenario: tcp target 缺少 port
- **WHEN** YAML 中 target 配置 `type: tcp` 但缺少 `tcp.port`
- **THEN** 系统 SHALL 以配置错误退出,并提示该 target 缺少 tcp.port 字段
#### Scenario: tcp port 范围非法
- **WHEN** YAML 中 tcp target 的 `tcp.port` 不是 1 到 65535 之间的整数
- **THEN** 系统 SHALL 以配置错误退出,并提示 tcp.port 必须为合法 TCP 端口
#### Scenario: tcp defaults 覆盖 banner 参数
- **WHEN** YAML 中配置 `defaults.tcp.bannerReadTimeout: 1000``defaults.tcp.maxBannerBytes: "8KB"`
- **THEN** 未显式配置对应字段的 tcp target SHALL 使用 defaults.tcp 中的值
#### Scenario: per-target banner 参数覆盖 defaults
- **WHEN** defaults.tcp 配置了 banner 参数,且某个 tcp target 显式配置 `tcp.bannerReadTimeout``tcp.maxBannerBytes`
- **THEN** 该 target SHALL 使用自身 tcp 分组中的值
#### Scenario: tcp 序列化展示摘要
- **WHEN** 系统同步 tcp target 到 targets 表
- **THEN** `target` 展示摘要 SHALL 为 `<host>:<port>``config` JSON SHALL 包含 resolved 后的 host、port、readBanner、bannerReadTimeout 和 maxBannerBytes
### Requirement: tcp checker 执行
系统 SHALL 按 tcp target 配置建立 TCP 连接,记录完整执行耗时和 TCP observation并在连接失败、超时或资源超限时产生结构化失败信息。
#### Scenario: TCP 连接成功
- **WHEN** tcp target 指向可连接的 TCP 服务,且未配置 expect 或 `expect.connected``true`
- **THEN** 系统 SHALL 记录 `matched=true``durationMs` 和包含 connected、connectTimeMs、banner 的 observation并关闭 socket
#### Scenario: TCP 连接失败
- **WHEN** tcp target 指向不可连接的 host/port且未配置 expect 或 `expect.connected``true`
- **THEN** 系统 SHALL 记录 `matched=false`observation SHALL 包含 connected=false 和错误信息failure 的 kind 为 `error`phase 为 `connect`message 包含可读连接失败原因
#### Scenario: 期望端口不可达且连接失败
- **WHEN** tcp target 配置 `expect.connected: false`,且 TCP 连接失败
- **THEN** 系统 SHALL 记录 `matched=true`observation SHALL 包含 connected=false 和实际连接失败原因API detail SHALL 展示实际连接失败原因摘要
#### Scenario: 期望端口不可达但连接成功
- **WHEN** tcp target 配置 `expect.connected: false`,但 TCP 连接成功
- **THEN** 系统 SHALL 记录 `matched=false`observation SHALL 包含 connected=truefailure 的 kind 为 `mismatch`phase 为 `connected`
#### Scenario: TCP 执行超时
- **WHEN** 引擎注入的 `ctx.signal` 在 TCP 连接或 banner 读取过程中 abort
- **THEN** 系统 SHALL best-effort 关闭 socket记录 `matched=false`failure 的 kind 为 `error`phase 为 `connect``banner`message 包含超时信息,并在可收集时记录 observation
#### Scenario: duration 包含 banner 读取
- **WHEN** tcp target 开启 `readBanner` 且服务端延迟发送 banner
- **THEN** 结果中的 `durationMs` SHALL 覆盖连接建立、banner 等待、banner 读取和 expect 校验的完整耗时
### Requirement: tcp banner 读取
系统 SHALL 仅在 `tcp.readBanner: true` 时读取服务端主动发送的 banner 数据,并同时受 `bannerReadTimeout``maxBannerBytes` 限制。
#### Scenario: 默认不读取 banner
- **WHEN** tcp target 未配置 `readBanner` 或配置为 `false`
- **THEN** 系统 SHALL 在连接建立后立即进入 connected 和 duration 校验,不等待服务端数据
#### Scenario: 读取服务端 banner
- **WHEN** tcp target 配置 `readBanner: true`,且服务端连接后发送 `220 smtp.example.com ESMTP`
- **THEN** 系统 SHALL 收集 banner 文本,并允许后续 `expect.banner` 对该文本执行 operator 断言
#### Scenario: banner 等待超时无数据
- **WHEN** tcp target 配置 `readBanner: true`,但服务端在 `bannerReadTimeout` 内未发送任何数据
- **THEN** 系统 SHALL 将 banner 视为空字符串并继续执行 expect 校验,不将无 banner 本身作为连接错误
#### Scenario: banner 读取超过最大字节数
- **WHEN** 服务端发送的 banner 数据超过 `maxBannerBytes`
- **THEN** 系统 SHALL 停止读取并记录 `matched=false`、failure.kind=`error`、failure.phase=`banner` 的结构化错误
#### Scenario: banner detail 截断展示
- **WHEN** tcp target 成功读取到较长 banner
- **THEN** observation.banner SHALL 保存截断后的 banner 摘要API detail SHALL 展示截断后的 banner 摘要,避免 UI 展示过长文本
### Requirement: tcp expect 校验
系统 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 在 Resolved tcp expect 中使用默认 `connected: true` 进行校验
#### Scenario: durationMs 校验
- **WHEN** tcp target 配置 `expect.durationMs: {lte: 100}`,且完整执行耗时超过 100ms
- **THEN** 系统 SHALL 返回 `matched=false`failure 的 phase 为 `duration`
#### 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 expectation
#### Scenario: banner 多规则快速失败
- **WHEN** tcp target 配置两条 banner expectation 且第一条失败
- **THEN** 系统 SHALL 返回第一条失败 expectation 的 failure并 MUST NOT 执行第二条 expectation
#### Scenario: expect.banner 未开启 readBanner
- **WHEN** tcp target 配置 `expect.banner`,但 `tcp.readBanner` 未配置为 `true`
- **THEN** 系统 SHALL 在启动期配置校验失败,提示 banner 断言需要启用 tcp.readBanner
#### Scenario: tcp expect 未知字段失败
- **WHEN** YAML 中 tcp target 的 expect 包含 `status: [200]``maxDurationMs: 1000` 或其他非 tcp expect 字段
- **THEN** 系统 SHALL 以配置错误退出,提示 expect 包含未知字段