## Purpose 定义 ICMP/Ping checker 的配置格式、命令执行、跨平台输出解析、expect 校验、失败结构和状态摘要。 ## Requirements ### Requirement: ping target 配置 系统 SHALL 支持 `type: ping` 的 target 配置,通过 `ping.host` 描述目标主机地址,并通过可选字段控制探测行为。 #### Scenario: 解析最简 ping target - **WHEN** YAML 中 target 配置 `type: ping` 和 `ping.host: "10.0.0.1"` - **THEN** 系统 SHALL 将其解析为 ping checker,并填充 `count=3`、`packetSize=56`、interval、timeout、group 和 expect 配置 #### Scenario: ping target 缺少 host - **WHEN** YAML 中 target 配置 `type: ping` 但缺少 `ping.host` - **THEN** 系统 SHALL 以配置错误退出,并提示该 target 缺少 ping.host 字段 #### Scenario: ping host 类型非法 - **WHEN** YAML 中 ping target 的 `ping.host` 不是非空字符串 - **THEN** 系统 SHALL 以配置错误退出,提示 ping.host 必须为非空字符串 #### Scenario: ping count 配置 - **WHEN** YAML 中 ping target 配置 `ping.count: 5` - **THEN** 系统 SHALL 使用 5 作为 ICMP 包发送数量 #### Scenario: ping count 非法 - **WHEN** YAML 中 ping target 的 `ping.count` 不是 1 到 100 之间的正整数 - **THEN** 系统 SHALL 以配置错误退出,提示 ping.count 必须为 1-100 的正整数 #### Scenario: ping packetSize 配置 - **WHEN** YAML 中 ping target 配置 `ping.packetSize: 1472` - **THEN** 系统 SHALL 使用 1472 作为 ICMP 包大小(bytes) #### Scenario: ping packetSize 非法 - **WHEN** YAML 中 ping target 的 `ping.packetSize` 不是 1 到 65500 之间的正整数 - **THEN** 系统 SHALL 以配置错误退出,提示 ping.packetSize 必须为 1-65500 的正整数 #### Scenario: ping 分组未知字段失败 - **WHEN** YAML 中 ping target 的 `ping` 分组包含 `timeout: 5` 等未知字段 - **THEN** 系统 SHALL 以配置错误退出,提示 ping 分组包含未知字段 #### Scenario: ping 序列化展示摘要 - **WHEN** 系统同步 ping target 到 targets 表 - **THEN** `target` 展示摘要 SHALL 为 `ping `,`config` JSON SHALL 包含 resolved 后的 host、count 和 packetSize ### Requirement: ping checker 执行 系统 SHALL 通过调用系统 `ping` 命令执行 ICMP 探测,记录完整执行耗时,并在命令不可用、超时或解析失败时产生结构化失败信息。 #### Scenario: ping 命令构建(Linux) - **WHEN** 系统平台为 linux,ping target 配置 host="10.0.0.1"、count=3、packetSize=56,且外层 timeoutMs=10000 - **THEN** 系统 SHALL 执行 `ping -c 3 -s 56 -W 10 10.0.0.1`(-W 单位为秒,向上取整) #### Scenario: ping 命令构建(macOS) - **WHEN** 系统平台为 darwin,ping target 配置 host="10.0.0.1"、count=3、packetSize=56,且外层 timeoutMs=10000 - **THEN** 系统 SHALL 执行 `ping -c 3 -s 56 -W 10000 10.0.0.1`(-W 单位为毫秒) #### Scenario: ping 命令构建(Windows) - **WHEN** 系统平台为 win32,ping target 配置 host="10.0.0.1"、count=3、packetSize=56,且外层 timeoutMs=10000 - **THEN** 系统 SHALL 执行 `ping -n 3 -l 56 -w 10000 10.0.0.1`(-w 单位为毫秒) #### Scenario: ping 命令不存在 - **WHEN** 系统未安装 `ping` 命令(spawn 抛出 ENOENT) - **THEN** 系统 SHALL 记录 `matched=false`,failure 的 kind 为 `error`,phase 为 `ping`,path 为 `spawn`,message 包含 "ping 命令不可用" 和原始错误信息 #### Scenario: ping 执行超时 - **WHEN** 引擎注入的 `ctx.signal` 在 ping 命令执行过程中 abort - **THEN** 系统 SHALL 调用 `proc.kill()` 终止子进程,记录 `matched=false`,failure 的 kind 为 `error`,phase 为 `ping`,message 包含超时信息 #### Scenario: ping 目标可达 - **WHEN** ping target 指向可达主机,且 ping 命令正常返回 - **THEN** 系统 SHALL 解析 stdout 获取统计数据,并按断言链执行 expect 校验 #### Scenario: ping 目标不可达 - **WHEN** ping target 指向不可达主机,且 ping 命令返回 100% packet loss - **THEN** 系统 SHALL 解析 stdout 获取统计数据,`alive` 为 false,延迟字段为 null #### Scenario: duration 覆盖完整执行 - **WHEN** ping 命令执行完成 - **THEN** 结果中的 `durationMs` SHALL 覆盖从 spawn 到进程退出的完整耗时 ### Requirement: 跨平台 ping 输出解析 系统 SHALL 实现跨平台 ping 输出解析器,支持 Linux、macOS 和 Windows(含多语言 locale),从 stdout 中提取 transmitted、received、packetLoss、minLatencyMs、avgLatencyMs、maxLatencyMs。 #### Scenario: 解析 Linux ping 输出 - **WHEN** 平台为 linux,stdout 包含 "3 packets transmitted, 3 received, 0% packet loss" 和 "rtt min/avg/max/mdev = 1.234/2.345/3.456/0.567 ms" - **THEN** 系统 SHALL 解析为 transmitted=3, received=3, packetLoss=0, minLatencyMs=1.234, avgLatencyMs=2.345, maxLatencyMs=3.456 #### Scenario: 解析 macOS ping 输出 - **WHEN** 平台为 darwin,stdout 包含 "3 packets transmitted, 3 packets received, 0.0% packet loss" 和 "round-trip min/avg/max/stddev = 1.234/2.345/3.456/0.567 ms" - **THEN** 系统 SHALL 解析为 transmitted=3, received=3, packetLoss=0, minLatencyMs=1.234, avgLatencyMs=2.345, maxLatencyMs=3.456 #### Scenario: 解析 Windows 英文 ping 输出 - **WHEN** 平台为 win32,stdout 包含 "Packets: Sent = 3, Received = 3, Lost = 0 (0% loss)" 和 "Minimum = 1ms, Maximum = 3ms, Average = 2ms" - **THEN** 系统 SHALL 解析为 transmitted=3, received=3, packetLoss=0, minLatencyMs=1, avgLatencyMs=2, maxLatencyMs=3 #### Scenario: 解析 Windows 中文 ping 输出 - **WHEN** 平台为 win32,stdout 包含 "数据包: 已发送 = 3,已接收 = 3,丢失 = 0 (0% 丢失)" 和 "最短 = 1ms,最长 = 3ms,平均 = 2ms" - **THEN** 系统 SHALL 解析为 transmitted=3, received=3, packetLoss=0, minLatencyMs=1, avgLatencyMs=2, maxLatencyMs=3 #### Scenario: 解析全部丢包(无延迟行) - **WHEN** stdout 包含丢包统计行但无延迟统计行(100% packet loss) - **THEN** 系统 SHALL 解析为 alive=false,延迟字段(min/avg/max)均为 null #### Scenario: 输出无法解析 - **WHEN** stdout 不匹配任何已知的统计行格式 - **THEN** 系统 SHALL 记录 `matched=false`,failure 的 kind 为 `error`,phase 为 `ping`,path 为 `parse`,message 包含 "无法解析 ping 输出" ### Requirement: ping expect 校验 系统 SHALL 支持 ping 专属 expect,包括 `alive`、`packetLossPercent`、`avgLatencyMs`、`maxLatencyMs` 和 `durationMs`,并按 alive、packetLossPercent、avgLatencyMs、maxLatencyMs、durationMs 的阶段顺序快速失败。`alive` SHALL 保持布尔状态语义,未配置时默认 `true`。`packetLossPercent` SHALL 表示 0 到 100 的丢包率百分比,并使用共享 `ValueMatcher`。`avgLatencyMs`、`maxLatencyMs` 和 `durationMs` SHALL 使用共享 `ValueMatcher`。 #### Scenario: 默认 alive 成功语义 - **WHEN** ping target 未显式配置 `expect.alive` - **THEN** 系统 SHALL 使用默认 `expect.alive: true` 进行校验 #### Scenario: alive 校验通过 - **WHEN** ping target 配置 `expect.alive: true`,且目标主机可达 - **THEN** 系统 SHALL 判定 alive 阶段通过 #### Scenario: alive 校验失败 - **WHEN** ping target 配置 `expect.alive: true`,且目标主机不可达 - **THEN** 系统 SHALL 返回 `matched=false`,failure 的 kind 为 `mismatch`,phase 为 `alive` #### Scenario: 反向 alive 断言 - **WHEN** ping target 配置 `expect.alive: false`,且目标主机不可达 - **THEN** 系统 SHALL 判定 alive 阶段通过(`matched=true`) #### Scenario: packetLossPercent 校验通过 - **WHEN** ping target 配置 `expect.packetLossPercent: {lte: 10}`,且实际丢包率为 0% - **THEN** 系统 SHALL 判定 packetLossPercent 阶段通过 #### Scenario: packetLossPercent 校验失败 - **WHEN** ping target 配置 `expect.packetLossPercent: {lte: 10}`,且实际丢包率为 33% - **THEN** 系统 SHALL 返回 `matched=false`,failure 的 kind 为 `mismatch`,phase 为 `packetLoss` #### Scenario: avgLatencyMs 校验通过 - **WHEN** ping target 配置 `expect.avgLatencyMs: {lte: 200}`,且实际平均延迟为 12ms - **THEN** 系统 SHALL 判定 avgLatency 阶段通过 #### Scenario: avgLatencyMs 校验失败 - **WHEN** ping target 配置 `expect.avgLatencyMs: {lte: 100}`,且实际平均延迟为 156ms - **THEN** 系统 SHALL 返回 `matched=false`,failure 的 kind 为 `mismatch`,phase 为 `avgLatency` #### Scenario: maxLatencyMs 校验通过 - **WHEN** ping target 配置 `expect.maxLatencyMs: {lte: 500}`,且实际最大延迟为 340ms - **THEN** 系统 SHALL 判定 maxLatency 阶段通过 #### Scenario: maxLatencyMs 校验失败 - **WHEN** ping target 配置 `expect.maxLatencyMs: {lte: 200}`,且实际最大延迟为 340ms - **THEN** 系统 SHALL 返回 `matched=false`,failure 的 kind 为 `mismatch`,phase 为 `maxLatency` #### Scenario: durationMs 校验 - **WHEN** ping target 配置 `expect.durationMs: {lte: 5000}`,且完整执行耗时超过 5000ms - **THEN** 系统 SHALL 返回 `matched=false`,failure 的 phase 为 `duration` #### Scenario: alive=false 时跳过延迟断言 - **WHEN** ping target 配置 `expect.alive: true` 和 `expect.avgLatencyMs: {lte: 100}`,且目标不可达 - **THEN** 系统 SHALL 在 alive 阶段即返回失败,不执行后续延迟断言 #### Scenario: ping expect 未知字段失败 - **WHEN** YAML 中 ping target 的 expect 包含 `status: [200]`、`maxPacketLoss`、`maxAvgLatencyMs`、`maxMaxLatencyMs`、`maxDurationMs` 或其他非 ping expect 字段 - **THEN** 系统 SHALL 以配置错误退出,提示 expect 包含未知字段 #### Scenario: packetLossPercent 类型非法 - **WHEN** YAML 中 ping target 的 `expect.packetLossPercent` 不是合法 `ValueMatcher`,或其数值范围无法用于 0 到 100 的百分比断言 - **THEN** 系统 SHALL 以配置错误退出,提示 expect.packetLossPercent 格式错误 #### Scenario: avgLatencyMs 类型非法 - **WHEN** YAML 中 ping target 的 `expect.avgLatencyMs` 不是合法 `ValueMatcher` - **THEN** 系统 SHALL 以配置错误退出,提示 expect.avgLatencyMs 格式错误 #### Scenario: maxLatencyMs 类型非法 - **WHEN** YAML 中 ping target 的 `expect.maxLatencyMs` 不是合法 `ValueMatcher` - **THEN** 系统 SHALL 以配置错误退出,提示 expect.maxLatencyMs 格式错误 ### Requirement: ping statusDetail 摘要 系统 SHALL 在 ping 执行成功后生成结构化 statusDetail 摘要,展示关键指标。 #### Scenario: 目标可达无丢包 - **WHEN** ping 结果为 alive=true, avg=12ms, packetLoss=0%, transmitted=3, received=3 - **THEN** statusDetail SHALL 为 `alive, avg 12ms, loss 0% (3/3)` #### Scenario: 目标可达有丢包 - **WHEN** ping 结果为 alive=true, avg=156ms, max=340ms, packetLoss=33%, transmitted=3, received=2 - **THEN** statusDetail SHALL 包含 avg、max 和 loss 信息 #### Scenario: 目标不可达 - **WHEN** ping 结果为 alive=false, transmitted=3, received=0 - **THEN** statusDetail SHALL 为 `unreachable (0/3 received)`