1
0

refactor: 重命名 command checker 为 cmd checker 并适配跨平台测试

将 type/configKey 从 "command" 统一为 "cmd",源码目录 runner/command/ → runner/cmd/,
spec 目录 command-checker/ → cmd-checker/,测试全部改用 bun -e 替代 Unix 系统命令,
归档 cmd-checker-enhancement 变更并同步 delta spec 到主 spec。
This commit is contained in:
2026-05-14 09:23:10 +08:00
parent 0fa2c0c811
commit e983e5d75d
40 changed files with 522 additions and 773 deletions

View File

@@ -58,7 +58,7 @@
- **THEN** `isEqual(actual, expected)` SHALL 递归比较所有属性值,而非引用比较
### Requirement: 使用 es-toolkit 进行错误类型判断
系统 SHALL 使用 es-toolkit 的 `isError` 替代 `error instanceof Error`,用于 HTTP runner 和 command runner 中的错误类型判断。
系统 SHALL 使用 es-toolkit 的 `isError` 替代 `error instanceof Error`,用于 HTTP runner 和 cmd runner 中的错误类型判断。
#### Scenario: Error 实例识别
- **WHEN** 错误对象为 `new Error("msg")`

View File

@@ -11,8 +11,8 @@
- **WHEN** 开发者查看 `src/server/checker/runner/http/` 目录
- **THEN** 该目录 SHALL 包含 `index.ts``types.ts``schema.ts``execute.ts``expect.ts``body.ts``validate.ts`
#### Scenario: Command checker 目录完整性
- **WHEN** 开发者查看 `src/server/checker/runner/command/` 目录
#### Scenario: Cmd checker 目录完整性
- **WHEN** 开发者查看 `src/server/checker/runner/cmd/` 目录
- **THEN** 该目录 SHALL 包含 `index.ts``types.ts``schema.ts``execute.ts``expect.ts``text.ts``validate.ts`
#### Scenario: 新增 checker 最小改动
@@ -146,7 +146,7 @@ checker 系统内的模块依赖 SHALL 遵循严格的分层方向。
#### Scenario: DefaultsConfig 为宽松 base 形式
- **WHEN** 开发者查看顶层 `types.ts` 中的 `DefaultsConfig`
- **THEN** 该 interface SHALL 仅包含公共字段(`interval?``timeout?`)和 index signature`[checkerKey: string]: unknown`SHALL NOT 包含 `command?``http?` 等 checker 专属字段
- **THEN** 该 interface SHALL 仅包含公共字段(`interval?``timeout?`)和 index signature`[checkerKey: string]: unknown`SHALL NOT 包含 `cmd?``http?` 等 checker 专属字段
#### Scenario: 各 checker validate 自行 narrow defaults
- **WHEN** checker 的 `validate()` 方法需要访问自身的 defaults 配置

View File

@@ -11,9 +11,9 @@
- **WHEN** HTTP checker 被注册
- **THEN** registry SHALL 能提供 HTTP defaults、HTTP target 和 HTTP expect 的 TypeBox 契约片段
#### Scenario: Command checker 提供契约片段
- **WHEN** Command checker 被注册
- **THEN** registry SHALL 能提供 Command defaults、Command target 和 Command expect 的 TypeBox 契约片段
#### Scenario: Cmd checker 提供契约片段
- **WHEN** Cmd checker 被注册
- **THEN** registry SHALL 能提供 Cmd defaults、Cmd target 和 Cmd expect 的 TypeBox 契约片段
#### Scenario: 新 checker 只维护自身契约
- **WHEN** 开发者新增一个 checker 类型
@@ -96,8 +96,8 @@
- **THEN** 系统 SHALL 抛出错误,提示该 type 已注册
#### Scenario: 查询支持的 type 列表
- **WHEN** 注册了 "http" 和 "command" 两个 checker 后查询 `registry.supportedTypes`
- **THEN** 返回的数组 SHALL 包含 `["http", "command"]`(按注册顺序)
- **WHEN** 注册了 "http" 和 "cmd" 两个 checker 后查询 `registry.supportedTypes`
- **THEN** 返回的数组 SHALL 包含 `["http", "cmd"]`(按注册顺序)
### Requirement: 引擎通过 registry 调度 checker
系统 SHALL 在 `ProbeEngine.runCheck()` 中通过 `checkerRegistry.get(target.type).execute(target, ctx)` 调度检查,替代原有的 `switch/case` 分支。
@@ -118,8 +118,8 @@
- **THEN** config-loader SHALL 从 `checkerRegistry` 获取已注册 checker 的契约片段,并用于校验 defaults 与 targets 中对应 checker 的配置形状
#### Scenario: 配置解析委托 checker
- **WHEN** config-loader 解析一个 type 为 "command" 的 target
- **THEN** config-loader SHALL 调用 `checkerRegistry.get("command")` 获取对应 checker并委托该 checker 执行语义校验和 resolve
- **WHEN** config-loader 解析一个 type 为 "cmd" 的 target
- **THEN** config-loader SHALL 调用 `checkerRegistry.get("cmd")` 获取对应 checker并委托该 checker 执行语义校验和 resolve
#### Scenario: 通用字段校验保留在 config-loader
- **WHEN** YAML 配置中某个 target 缺少 name 或 type 字段
@@ -163,26 +163,26 @@
- **WHEN** HTTP checker 需要对响应体执行 contains/regex/json/css/xpath 规则校验
- **THEN** SHALL 调用 `runner/http/body.ts` 中的 `checkBodyExpect(body, rules)`
#### Scenario: Command text 断言位于 Command 目录
- **WHEN** Command checker 需要对 stdout/stderr 执行文本规则校验
- **THEN** SHALL 调用 `runner/command/text.ts` 中的 `checkTextRules(text, rules, phase)`
#### Scenario: Cmd text 断言位于 Cmd 目录
- **WHEN** Cmd checker 需要对 stdout/stderr 执行文本规则校验
- **THEN** SHALL 调用 `runner/cmd/text.ts` 中的 `checkTextRules(text, rules, phase)`
#### Scenario: HTTP 专用 expect
- **WHEN** HTTP checker 需要校验响应状态码和响应头
- **THEN** SHALL 调用 `runner/http/expect.ts` 中的 `checkStatus()``checkHeaders()`
#### Scenario: Command 专用 expect
- **WHEN** Command checker 需要校验退出码
- **THEN** SHALL 调用 `runner/command/expect.ts` 中的 `checkExitCode()`
#### Scenario: Cmd 专用 expect
- **WHEN** Cmd checker 需要校验退出码
- **THEN** SHALL 调用 `runner/cmd/expect.ts` 中的 `checkExitCode()`
### Requirement: 超时控制由引擎注入 signal
Checker 实现的 `execute()` MUST 使用 `ctx.signal` 感知超时,不得自行创建 `AbortController``setTimeout` 用于超时控制。仅 command checker 可在 signal abort 时 `proc.kill()` 以确保子进程被终止。
Checker 实现的 `execute()` MUST 使用 `ctx.signal` 感知超时,不得自行创建 `AbortController``setTimeout` 用于超时控制。仅 cmd checker 可在 signal abort 时 `proc.kill()` 以确保子进程被终止。
#### Scenario: HTTP checker 使用 signal
- **WHEN** HttpChecker 执行 HTTP 请求
- **THEN** SHALL 将 `ctx.signal` 传入 `fetch()``signal` 选项,不自行创建 `AbortController`
#### Scenario: Command checker 响应 signal
#### Scenario: Cmd checker 响应 signal
- **WHEN** CommandChecker 执行命令且 signal 被 abort
- **THEN** SHALL 调用 `proc.kill()` 终止子进程,并在 CheckResult 中记录超时错误
@@ -190,7 +190,7 @@ Checker 实现的 `execute()` MUST 使用 `ctx.signal` 感知超时,不得自
`shared/api.ts``CheckFailure.phase` 的类型 SHALL 定义为 `string`,替代原有的硬编码联合类型 `"status" | "duration" | "headers" | "body" | "exitCode" | "stdout" | "stderr"`
#### Scenario: phase 支持 checker 专用值
- **WHEN** command checker 在执行失败spawn error时生成 failure
- **WHEN** cmd checker 在执行失败spawn error时生成 failure
- **THEN** `failure.phase` SHALL 可以是 `"spawn"` 等任意字符串值,类型系统 SHALL 不报错
#### Scenario: 前端展示 phase 不依赖硬编码类型

View File

@@ -0,0 +1,141 @@
## Purpose
定义 Cmd 类型拨测目标:通过 `type: cmd` 配置执行本地命令(如进程检查、脚本健康检测),捕获 exit code、stdout、stderr按 expect 规则校验并生成 matched 判定。
## Requirements
### Requirement: cmd target 配置
系统 SHALL 支持 `type: cmd` 的 target 配置,通过 `cmd.exec``cmd.args` 描述本地命令,并使用 cmd 专用字段配置工作目录、环境变量和输出限制。
#### Scenario: 解析 cmd target
- **WHEN** YAML 中 target 配置 `type: cmd``cmd.exec: "pgrep"``cmd.args: ["nginx"]`
- **THEN** 系统 SHALL 将其解析为 cmd checker并保留 exec、args、cwd、env、maxOutputBytes、interval、timeout 和 expect 配置
#### Scenario: cmd target 缺少 exec
- **WHEN** YAML 中 target 配置 `type: cmd` 但缺少 `cmd.exec`
- **THEN** 系统 SHALL 以配置错误退出,并提示该 target 缺少 cmd.exec 字段
#### Scenario: cwd 相对配置文件目录解析
- **WHEN** cmd target 配置 `cmd.cwd: "scripts"` 且配置文件位于 `/opt/checker/probes.yaml`
- **THEN** 系统 SHALL 将 cwd 解析为 `/opt/checker/scripts`
#### Scenario: cmd 不使用 shell
- **WHEN** cmd target 配置 `exec``args`
- **THEN** 系统 MUST 直接执行该程序和参数,不通过 shell 解释整段命令字符串
#### Scenario: env 默认继承并允许覆盖
- **WHEN** cmd target 配置 `cmd.env: {LANG: "C"}` 且当前进程环境包含 `PATH`
- **THEN** 系统 SHALL 继承当前进程的全部环境变量,并将 `LANG` 覆盖为 `"C"`
#### Scenario: 不支持 stdin
- **WHEN** cmd target 配置并执行命令
- **THEN** 系统 MUST NOT 向子进程 stdin 写入数据,避免命令因等待输入而阻塞
### Requirement: cmd checker 执行
系统 SHALL 按 cmd target 配置执行本地命令记录执行耗时、退出码、stdout 和 stderr并在执行失败时产生结构化错误信息。
#### Scenario: 命令正常退出
- **WHEN** cmd target 执行的进程正常退出且 exit code 为 0
- **THEN** 系统 SHALL 记录 `durationMs``statusDetail="exitCode=0"`,并进入 expect 校验
#### Scenario: 命令非零退出
- **WHEN** cmd target 执行的进程正常退出但 exit code 为 1
- **THEN** 系统 SHALL 记录 `statusDetail="exitCode=1"`,并由 expect.exitCode 决定 matched 结果
#### Scenario: 命令启动失败
- **WHEN** cmd target 的 exec 不存在或无法启动
- **THEN** 系统 SHALL 记录 `matched=false`,并在 failure 中写入 kind=`error` 和可读错误信息
#### Scenario: 命令超时
- **WHEN** cmd target 在 timeout 时间内未结束
- **THEN** 系统 MUST 终止该子进程,记录 `matched=false`,并在 failure 中写入命令超时信息
#### Scenario: 命令输出超限
- **WHEN** cmd target 的 stdout 和 stderr 合计输出超过 `maxOutputBytes`
- **THEN** 系统 MUST 停止收集输出并终止该检查,记录 `matched=false`,并在 failure 中写入输出超限信息
### Requirement: cmd expect 校验
系统 SHALL 支持 cmd 专用 expect包括 `exitCode``stdout``stderr`,并按 exitCode、duration、stdout、stderr 的阶段顺序快速失败。
#### Scenario: 默认 exitCode 成功语义
- **WHEN** cmd target 未显式配置 `expect.exitCode`
- **THEN** 系统 SHALL 使用默认 `expect.exitCode: [0]` 进行校验
#### Scenario: 显式 exitCode 校验
- **WHEN** cmd target 配置 `expect.exitCode: [0, 2]` 且实际 exit code 为 2
- **THEN** 系统 SHALL 判定 exitCode 阶段通过,并继续后续 expect 阶段
#### Scenario: exitCode 不匹配快速失败
- **WHEN** cmd target 配置 `expect.exitCode: [0]` 且实际 exit code 为 1
- **THEN** 系统 SHALL 立即返回 `matched=false`,并在 failure 中写入 phase=`exitCode`、path=`expect.exitCode`、expected 和 actual
#### Scenario: stdout 按配置顺序校验
- **WHEN** cmd target 配置 `expect.stdout` 为两个规则,第一条通过且第二条失败
- **THEN** 系统 SHALL 先执行第一条 stdout 规则,再执行第二条,并将 failure.path 指向失败的 `expect.stdout[1]`
#### Scenario: stderr 校验为空
- **WHEN** cmd target 配置 `expect.stderr: [{empty: true}]` 且实际 stderr 为空字符串
- **THEN** 系统 SHALL 判定 stderr 阶段通过
#### Scenario: stdout 失败后不检查 stderr
- **WHEN** cmd target 同时配置 stdout 和 stderr 规则,且 stdout 规则失败
- **THEN** 系统 SHALL 快速失败并 MUST NOT 继续执行 stderr 规则
### Requirement: cmd checker 启动期配置校验
系统 SHALL 在启动期对 cmd checker 的配置契约和语义执行严格校验。Cmd target 的 `cmd` 分组 SHALL 只允许 `exec``args``cwd``env``maxOutputBytes` 字段Cmd expect SHALL 只允许 `exitCode``maxDurationMs``stdout``stderr` 字段。未知字段、非法类型和不可编译正则 MUST 导致启动期配置错误。`expect.exitCode` SHALL 保留原有有限整数数组语义,不限制到特定平台范围。
#### Scenario: cmd args 类型非法
- **WHEN** YAML 中 cmd target 配置 `cmd.args` 不是字符串数组
- **THEN** 系统 SHALL 以配置错误退出,提示 cmd.args 格式错误
#### Scenario: cmd cwd 类型非法
- **WHEN** YAML 中 cmd target 配置 `cmd.cwd` 不是字符串
- **THEN** 系统 SHALL 以配置错误退出,提示 cmd.cwd 必须为字符串
#### Scenario: cmd env 值类型非法
- **WHEN** YAML 中 cmd target 配置 `cmd.env`,且任一环境变量值不是字符串
- **THEN** 系统 SHALL 以配置错误退出,提示 cmd.env 对应变量值必须为字符串
#### Scenario: cmd maxOutputBytes 非法
- **WHEN** YAML 中 cmd target 或 defaults.cmd 配置的 `maxOutputBytes` 不是合法 size 值
- **THEN** 系统 SHALL 以配置错误退出,提示 maxOutputBytes 格式错误
#### Scenario: cmd 分组未知字段失败
- **WHEN** YAML 中 cmd target 的 `cmd` 分组包含 `shell: true` 等未知字段
- **THEN** 系统 SHALL 以配置错误退出,提示 cmd 分组包含未知字段
#### Scenario: cmd expect exitCode 类型非法
- **WHEN** YAML 中 cmd target 配置 `expect.exitCode` 不是整数数组
- **THEN** 系统 SHALL 以配置错误退出,提示 expect.exitCode 必须为整数数组
#### Scenario: cmd expect exitCode 不限制平台范围
- **WHEN** YAML 中 cmd target 配置 `expect.exitCode` 为有限整数数组
- **THEN** 系统 SHALL 接受该数组,不额外限制为 0-255 等平台相关范围
#### Scenario: cmd expect maxDurationMs 非法
- **WHEN** YAML 中 cmd target 配置 `expect.maxDurationMs` 不是非负有限数字
- **THEN** 系统 SHALL 以配置错误退出,提示 expect.maxDurationMs 格式错误
#### Scenario: stdout 必须为规则数组
- **WHEN** YAML 中 cmd target 配置 `expect.stdout` 但其值不是数组
- **THEN** 系统 SHALL 以配置错误退出,提示 expect.stdout 必须为数组
#### Scenario: stderr 必须为规则数组
- **WHEN** YAML 中 cmd target 配置 `expect.stderr` 但其值不是数组
- **THEN** 系统 SHALL 以配置错误退出,提示 expect.stderr 必须为数组
#### Scenario: stdout text rule 空对象非法
- **WHEN** YAML 中 cmd target 配置 `expect.stdout: [{}]`
- **THEN** 系统 SHALL 以配置错误退出,提示 stdout 规则必须包含至少一个合法 operator
#### Scenario: stderr text rule 未知字段非法
- **WHEN** YAML 中 cmd target 配置 `expect.stderr: [{foo: "bar"}]`
- **THEN** 系统 SHALL 以配置错误退出,提示 stderr 规则包含未知 operator
#### Scenario: stdout match 正则非法
- **WHEN** YAML 中 cmd target 配置 `expect.stdout: [{match: "[invalid"}]`
- **THEN** 系统 SHALL 在启动期配置校验失败,而不是延迟到运行期抛错
#### Scenario: cmd expect 未知字段失败
- **WHEN** YAML 中 cmd target 的 expect 包含 `status: [200]` 或其他非 cmd expect 字段
- **THEN** 系统 SHALL 以配置错误退出,提示 expect 包含未知字段

View File

@@ -1,141 +0,0 @@
## Purpose
定义 Command 类型拨测目标:通过 `type: command` 配置执行本地命令(如进程检查、脚本健康检测),捕获 exit code、stdout、stderr按 expect 规则校验并生成 matched 判定。
## Requirements
### Requirement: command target 配置
系统 SHALL 支持 `type: command` 的 target 配置,通过 `command.exec``command.args` 描述本地命令,并使用 command 专用字段配置工作目录、环境变量和输出限制。
#### Scenario: 解析 command target
- **WHEN** YAML 中 target 配置 `type: command``command.exec: "pgrep"``command.args: ["nginx"]`
- **THEN** 系统 SHALL 将其解析为 command checker并保留 exec、args、cwd、env、maxOutputBytes、interval、timeout 和 expect 配置
#### Scenario: command target 缺少 exec
- **WHEN** YAML 中 target 配置 `type: command` 但缺少 `command.exec`
- **THEN** 系统 SHALL 以配置错误退出,并提示该 target 缺少 command.exec 字段
#### Scenario: cwd 相对配置文件目录解析
- **WHEN** command target 配置 `command.cwd: "scripts"` 且配置文件位于 `/opt/checker/probes.yaml`
- **THEN** 系统 SHALL 将 cwd 解析为 `/opt/checker/scripts`
#### Scenario: command 不使用 shell
- **WHEN** command target 配置 `exec``args`
- **THEN** 系统 MUST 直接执行该程序和参数,不通过 shell 解释整段命令字符串
#### Scenario: env 默认继承并允许覆盖
- **WHEN** command target 配置 `command.env: {LANG: "C"}` 且当前进程环境包含 `PATH`
- **THEN** 系统 SHALL 继承当前进程的全部环境变量,并将 `LANG` 覆盖为 `"C"`
#### Scenario: 不支持 stdin
- **WHEN** command target 配置并执行命令
- **THEN** 系统 MUST NOT 向子进程 stdin 写入数据,避免命令因等待输入而阻塞
### Requirement: command checker 执行
系统 SHALL 按 command target 配置执行本地命令记录执行耗时、退出码、stdout 和 stderr并在执行失败时产生结构化错误信息。
#### Scenario: 命令正常退出
- **WHEN** command target 执行的进程正常退出且 exit code 为 0
- **THEN** 系统 SHALL 记录 `durationMs``statusDetail="exitCode=0"`,并进入 expect 校验
#### Scenario: 命令非零退出
- **WHEN** command target 执行的进程正常退出但 exit code 为 1
- **THEN** 系统 SHALL 记录 `statusDetail="exitCode=1"`,并由 expect.exitCode 决定 matched 结果
#### Scenario: 命令启动失败
- **WHEN** command target 的 exec 不存在或无法启动
- **THEN** 系统 SHALL 记录 `matched=false`,并在 failure 中写入 kind=`error` 和可读错误信息
#### Scenario: 命令超时
- **WHEN** command target 在 timeout 时间内未结束
- **THEN** 系统 MUST 终止该子进程,记录 `matched=false`,并在 failure 中写入命令超时信息
#### Scenario: 命令输出超限
- **WHEN** command target 的 stdout 和 stderr 合计输出超过 `maxOutputBytes`
- **THEN** 系统 MUST 停止收集输出并终止该检查,记录 `matched=false`,并在 failure 中写入输出超限信息
### Requirement: command expect 校验
系统 SHALL 支持 command 专用 expect包括 `exitCode``stdout``stderr`,并按 exitCode、duration、stdout、stderr 的阶段顺序快速失败。
#### Scenario: 默认 exitCode 成功语义
- **WHEN** command target 未显式配置 `expect.exitCode`
- **THEN** 系统 SHALL 使用默认 `expect.exitCode: [0]` 进行校验
#### Scenario: 显式 exitCode 校验
- **WHEN** command target 配置 `expect.exitCode: [0, 2]` 且实际 exit code 为 2
- **THEN** 系统 SHALL 判定 exitCode 阶段通过,并继续后续 expect 阶段
#### Scenario: exitCode 不匹配快速失败
- **WHEN** command target 配置 `expect.exitCode: [0]` 且实际 exit code 为 1
- **THEN** 系统 SHALL 立即返回 `matched=false`,并在 failure 中写入 phase=`exitCode`、path=`expect.exitCode`、expected 和 actual
#### Scenario: stdout 按配置顺序校验
- **WHEN** command target 配置 `expect.stdout` 为两个规则,第一条通过且第二条失败
- **THEN** 系统 SHALL 先执行第一条 stdout 规则,再执行第二条,并将 failure.path 指向失败的 `expect.stdout[1]`
#### Scenario: stderr 校验为空
- **WHEN** command target 配置 `expect.stderr: [{empty: true}]` 且实际 stderr 为空字符串
- **THEN** 系统 SHALL 判定 stderr 阶段通过
#### Scenario: stdout 失败后不检查 stderr
- **WHEN** command target 同时配置 stdout 和 stderr 规则,且 stdout 规则失败
- **THEN** 系统 SHALL 快速失败并 MUST NOT 继续执行 stderr 规则
### Requirement: command checker 启动期配置校验
系统 SHALL 在启动期对 command checker 的配置契约和语义执行严格校验。Command target 的 `command` 分组 SHALL 只允许 `exec``args``cwd``env``maxOutputBytes` 字段Command expect SHALL 只允许 `exitCode``maxDurationMs``stdout``stderr` 字段。未知字段、非法类型和不可编译正则 MUST 导致启动期配置错误。`expect.exitCode` SHALL 保留原有有限整数数组语义,不限制到特定平台范围。
#### Scenario: command args 类型非法
- **WHEN** YAML 中 command target 配置 `command.args` 不是字符串数组
- **THEN** 系统 SHALL 以配置错误退出,提示 command.args 格式错误
#### Scenario: command cwd 类型非法
- **WHEN** YAML 中 command target 配置 `command.cwd` 不是字符串
- **THEN** 系统 SHALL 以配置错误退出,提示 command.cwd 必须为字符串
#### Scenario: command env 值类型非法
- **WHEN** YAML 中 command target 配置 `command.env`,且任一环境变量值不是字符串
- **THEN** 系统 SHALL 以配置错误退出,提示 command.env 对应变量值必须为字符串
#### Scenario: command maxOutputBytes 非法
- **WHEN** YAML 中 command target 或 defaults.command 配置的 `maxOutputBytes` 不是合法 size 值
- **THEN** 系统 SHALL 以配置错误退出,提示 maxOutputBytes 格式错误
#### Scenario: command 分组未知字段失败
- **WHEN** YAML 中 command target 的 `command` 分组包含 `shell: true` 等未知字段
- **THEN** 系统 SHALL 以配置错误退出,提示 command 分组包含未知字段
#### Scenario: command expect exitCode 类型非法
- **WHEN** YAML 中 command target 配置 `expect.exitCode` 不是整数数组
- **THEN** 系统 SHALL 以配置错误退出,提示 expect.exitCode 必须为整数数组
#### Scenario: command expect exitCode 不限制平台范围
- **WHEN** YAML 中 command target 配置 `expect.exitCode` 为有限整数数组
- **THEN** 系统 SHALL 接受该数组,不额外限制为 0-255 等平台相关范围
#### Scenario: command expect maxDurationMs 非法
- **WHEN** YAML 中 command target 配置 `expect.maxDurationMs` 不是非负有限数字
- **THEN** 系统 SHALL 以配置错误退出,提示 expect.maxDurationMs 格式错误
#### Scenario: stdout 必须为规则数组
- **WHEN** YAML 中 command target 配置 `expect.stdout` 但其值不是数组
- **THEN** 系统 SHALL 以配置错误退出,提示 expect.stdout 必须为数组
#### Scenario: stderr 必须为规则数组
- **WHEN** YAML 中 command target 配置 `expect.stderr` 但其值不是数组
- **THEN** 系统 SHALL 以配置错误退出,提示 expect.stderr 必须为数组
#### Scenario: stdout text rule 空对象非法
- **WHEN** YAML 中 command target 配置 `expect.stdout: [{}]`
- **THEN** 系统 SHALL 以配置错误退出,提示 stdout 规则必须包含至少一个合法 operator
#### Scenario: stderr text rule 未知字段非法
- **WHEN** YAML 中 command target 配置 `expect.stderr: [{foo: "bar"}]`
- **THEN** 系统 SHALL 以配置错误退出,提示 stderr 规则包含未知 operator
#### Scenario: stdout match 正则非法
- **WHEN** YAML 中 command target 配置 `expect.stdout: [{match: "[invalid"}]`
- **THEN** 系统 SHALL 在启动期配置校验失败,而不是延迟到运行期抛错
#### Scenario: command expect 未知字段失败
- **WHEN** YAML 中 command target 的 expect 包含 `status: [200]` 或其他非 command expect 字段
- **THEN** 系统 SHALL 以配置错误退出,提示 expect 包含未知字段

View File

@@ -9,7 +9,7 @@
#### Scenario: 获取 checker 类型列表
- **WHEN** 客户端请求 `GET /api/meta`
- **THEN** 系统 SHALL 返回 JSON `{ checkerTypes: string[] }`,包含所有已注册的 checker 类型标识符(如 `["http", "command"]`
- **THEN** 系统 SHALL 返回 JSON `{ checkerTypes: string[] }`,包含所有已注册的 checker 类型标识符(如 `["http", "cmd"]`
#### Scenario: 类型列表来源
- **WHEN** 系统启动并注册了 checker

View File

@@ -5,7 +5,7 @@
## Requirements
### Requirement: YAML 配置文件格式
系统 SHALL 支持通过 YAML 配置文件定义全部运行参数,包括 server 配置、runtime 配置、checker 默认值和 typed target 列表(含可选 group 字段。target MUST 使用 `type` 字段声明 checker 类型HTTP 领域字段 MUST 放在 `http` 分组command 领域字段 MUST 放在 `command` 分组。HTTP target 的 `http` 分组 SHALL 支持可选的 `ignoreSSL`(布尔值)和 `maxRedirects`(非负整数)字段。
系统 SHALL 支持通过 YAML 配置文件定义全部运行参数,包括 server 配置、runtime 配置、checker 默认值和 typed target 列表(含可选 group 字段。target MUST 使用 `type` 字段声明 checker 类型HTTP 领域字段 MUST 放在 `http` 分组cmd 领域字段 MUST 放在 `cmd` 分组。HTTP target 的 `http` 分组 SHALL 支持可选的 `ignoreSSL`(布尔值)和 `maxRedirects`(非负整数)字段。
#### Scenario: 完整配置文件解析
- **WHEN** 系统启动并读取包含 server、runtime、defaults、targets含 group 字段)的 YAML 配置文件
@@ -15,9 +15,9 @@
- **WHEN** 系统读取只包含一个 `type: http` target 和 `http.url` 的 YAML 配置文件(省略 server、runtime、defaults 和 expect
- **THEN** 系统 SHALL 使用内置默认值填充未指定的字段host=127.0.0.1, port=3000, dir=./data, interval=30s, timeout=10s, runtime.maxConcurrentChecks=20, http.method=GET, http.maxBodyBytes=100MB, http.ignoreSSL=false, http.maxRedirects=0, group="default"
#### Scenario: 最简 command 配置文件解析
- **WHEN** 系统读取只包含一个 `type: command` target 和 `command.exec` 的 YAML 配置文件
- **THEN** 系统 SHALL 使用内置默认值填充未指定的字段interval=30s, timeout=10s, command.cwd 为配置文件所在目录, command.maxOutputBytes=100MB
#### Scenario: 最简 cmd 配置文件解析
- **WHEN** 系统读取只包含一个 `type: cmd` target 和 `cmd.exec` 的 YAML 配置文件
- **THEN** 系统 SHALL 使用内置默认值填充未指定的字段interval=30s, timeout=10s, cmd.cwd 为配置文件所在目录, cmd.maxOutputBytes=100MB
#### Scenario: per-target 配置覆盖全局默认值
- **WHEN** 某个 target 指定 interval、timeout 或对应领域分组中的默认字段
@@ -63,9 +63,9 @@
- **WHEN** YAML 中某个 target 配置 `type: http` 但缺少 `http.url`
- **THEN** 系统 SHALL 以错误退出,提示该 target 缺少 http.url 字段
#### Scenario: command target 缺少 exec
- **WHEN** YAML 中某个 target 配置 `type: command` 但缺少 `command.exec`
- **THEN** 系统 SHALL 以错误退出,提示该 target 缺少 command.exec 字段
#### Scenario: cmd target 缺少 exec
- **WHEN** YAML 中某个 target 配置 `type: cmd` 但缺少 `cmd.exec`
- **THEN** 系统 SHALL 以错误退出,提示该 target 缺少 cmd.exec 字段
#### Scenario: target type 非法
- **WHEN** YAML 中某个 target 的 type 不是已注册 checker 类型
@@ -196,7 +196,7 @@
- **THEN** 系统 SHALL 接受这些动态 header 名称
#### Scenario: 动态 env 字段允许
- **WHEN** YAML 中 `command.env` 包含任意环境变量名称,且对应值为字符串
- **WHEN** YAML 中 `cmd.env` 包含任意环境变量名称,且对应值为字符串
- **THEN** 系统 SHALL 接受这些动态 env 名称
#### Scenario: JSON Schema 不修改输入
@@ -243,15 +243,15 @@
- **THEN** 系统 SHALL 调用 `Bun.YAML.parse()` 将内容解析为配置对象
### Requirement: expect 配置增强
系统 SHALL 支持 typed target 的领域专用 expect 配置,包括 HTTP 的 `status`(支持精确数字和范围模式)、`headers``body` 和 command 的 `exitCode``stdout``stderr`。内容类 expect MUST 使用数组表达配置顺序。
系统 SHALL 支持 typed target 的领域专用 expect 配置,包括 HTTP 的 `status`(支持精确数字和范围模式)、`headers``body` 和 cmd 的 `exitCode``stdout``stderr`。内容类 expect MUST 使用数组表达配置顺序。
#### Scenario: 解析 HTTP expect 配置
- **WHEN** YAML 配置文件中 HTTP target 的 expect 包含 status、headers、body 规则数组及内部方法
- **THEN** 系统 SHALL 正确解析并存储为 HTTP target 的 expect 字段
#### Scenario: 解析 command expect 配置
- **WHEN** YAML 配置文件中 command target 的 expect 包含 exitCode、stdout 和 stderr 规则数组
- **THEN** 系统 SHALL 正确解析并存储为 command target 的 expect 字段
#### Scenario: 解析 cmd expect 配置
- **WHEN** YAML 配置文件中 cmd target 的 expect 包含 exitCode、stdout 和 stderr 规则数组
- **THEN** 系统 SHALL 正确解析并存储为 cmd target 的 expect 字段
#### Scenario: 解析 body 有序规则数组
- **WHEN** YAML 中 HTTP target 配置 `expect.body` 为 contains、json、regex 三个数组项
@@ -269,8 +269,8 @@
- **WHEN** HTTP target 配置 `expect.status: ["2xx", 301]`
- **THEN** 系统 SHALL 在执行 expect 时匹配所有 200-299 状态码或精确匹配 301
#### Scenario: 不配置 command exitCode
- **WHEN** command target 未配置 `expect.exitCode`
#### Scenario: 不配置 cmd exitCode
- **WHEN** cmd target 未配置 `expect.exitCode`
- **THEN** 系统 SHALL 在执行 expect 时使用默认 `exitCode: [0]` 语义
#### Scenario: 不配置 expect

View File

@@ -115,16 +115,16 @@
- **WHEN** 同步 HTTP target
- **THEN** targets.target SHALL 存储该 target 的 URL
#### Scenario: command target 展示摘要
- **WHEN** 同步 command target
#### Scenario: cmd target 展示摘要
- **WHEN** 同步 cmd target
- **THEN** targets.target SHALL 存储由 exec 和 args 组成的命令摘要
#### Scenario: HTTP target config 序列化
- **WHEN** 同步 HTTP target
- **THEN** targets.config SHALL 存储 JSON包含 url、method、headers、body、maxBodyBytes、ignoreSSL、maxRedirects
#### Scenario: command target config 序列化
- **WHEN** 同步 command target
#### Scenario: cmd target config 序列化
- **WHEN** 同步 cmd target
- **THEN** targets.config SHALL 存储 JSON包含 exec、args、cwd、env、maxOutputBytes
### Requirement: 数据清理方法

View File

@@ -112,8 +112,8 @@
- **WHEN** HTTP 请求在 timeout 时间内未收到响应
- **THEN** 系统 SHALL 中止该请求,记录为失败并标注超时错误
#### Scenario: command 执行超时
- **WHEN** command 进程在 timeout 时间内未退出
#### Scenario: cmd 执行超时
- **WHEN** cmd 进程在 timeout 时间内未退出
- **THEN** 系统 MUST 终止该子进程,记录为失败并标注超时错误
#### Scenario: 请求在超时前完成
@@ -167,12 +167,12 @@
- **WHEN** 目标同时配置状态、duration、元数据和内容规则
- **THEN** 系统 SHALL 所有规则全部通过时 matched 为 true任一不通过则为 false 并记录首个失败原因
#### Scenario: command 默认 exitCode
- **WHEN** command target 未配置 `expect.exitCode`
#### Scenario: cmd 默认 exitCode
- **WHEN** cmd target 未配置 `expect.exitCode`
- **THEN** 系统 SHALL 按默认 `exitCode: [0]` 校验命令退出码
#### Scenario: 校验 command stdout
- **WHEN** command target 配置了有序 `expect.stdout` 规则数组
#### Scenario: 校验 cmd stdout
- **WHEN** cmd target 配置了有序 `expect.stdout` 规则数组
- **THEN** 系统 SHALL 按数组顺序执行 stdout 规则,任一失败立即记录 failure 并停止后续规则
### Requirement: Body 校验按需解析
@@ -235,9 +235,9 @@ HTTP checker SHALL 将运行期失败归属到实际失败阶段。请求、网
- **WHEN** target.type 为 `http`
- **THEN** 系统 SHALL 使用 HTTP runner 执行该目标
#### Scenario: 选择 command runner
- **WHEN** target.type 为 `command`
- **THEN** 系统 SHALL 使用 command runner 执行该目标
#### Scenario: 选择 cmd runner
- **WHEN** target.type 为 `cmd`
- **THEN** 系统 SHALL 使用 cmd runner 执行该目标
### Requirement: 定期数据清理
ProbeEngine SHALL 在启动时注册数据清理定时器,定期调用 ProbeStore.prune() 清理过期数据。

View File

@@ -2,7 +2,7 @@
## Purpose
确保测试在 Windows 平台上的兼容性,包括文件句柄释放后的目录清理重试机制和跨平台命令测试约定。
确保测试在 Windows、macOS、Linux 平台上的兼容性,包括文件句柄释放后的目录清理重试机制和跨平台命令测试约定。
## Requirements
@@ -16,10 +16,39 @@
- **THEN** 删除操作 SHALL 自动重试(最多 3 次,间隔 200ms直到成功或耗尽重试次数
### Requirement: 命令检测器测试 SHALL 使用跨平台命令
命令检测器的测试 SHALL 使用 `bun -e` 脚本替代所有系统命令(包括 `true``false``sleep``bash``echo``yes | head`),确保测试在 Windows、macOS、Linux 三平台上行为一致。
命令检测器的测试 SHALL 使用 `bun -e` 脚本替代系统 `echo` 命令,确保测试断言在所有平台上行为一致。
#### Scenario: 进程退出码 0
- **WHEN** 测试需要一个正常退出的命令
- **THEN** 测试 SHALL 使用 `bun -e "process.exit(0)"` 替代 `true`
#### Scenario: 进程退出码非零
- **WHEN** 测试需要一个失败退出的命令
- **THEN** 测试 SHALL 使用 `bun -e "process.exit(1)"` 替代 `false`
#### Scenario: stdout 输出
- **WHEN** 测试需要一个输出文本到 stdout 的命令
- **THEN** 测试 SHALL 使用 `bun -e "console.log('text')"` 替代 `echo text`
#### Scenario: stderr 输出
- **WHEN** 测试需要一个输出文本到 stderr 的命令
- **THEN** 测试 SHALL 使用 `bun -e "process.stderr.write('error\n')"` 替代 `bash -c "echo error >&2"`
#### Scenario: 长时间运行命令
- **WHEN** 测试需要一个超时场景的长时间运行命令
- **THEN** 测试 SHALL 使用 `bun -e "await Bun.sleep(10000)"` 替代 `sleep 10`
#### Scenario: 大量输出
- **WHEN** 测试需要一个产生大量输出的命令
- **THEN** 测试 SHALL 使用 `bun -e "process.stdout.write('y\n'.repeat(N))"` 替代 `bash -c "yes | head -N"`
#### Scenario: 验证非 shell 模式下特殊字符不被展开
- **WHEN** 通过 `Bun.spawn` 执行 `bun -e "console.log('*')"` 并检查 stdout 包含 `*`
- **THEN** 测试 SHALL 在 Windows 和 Linux 上均返回 `matched: true`
- **THEN** 测试 SHALL 在 Windows、macOS 和 Linux 上均返回 `matched: true`
### Requirement: probes.example.yaml 使用跨平台示例
probes.example.yaml 中的 cmd 类型示例 SHALL 使用跨平台命令(如 `bun -e "..."``bun --version`),不使用 Unix 专属命令(如 `uname``ls /tmp``date`)。
#### Scenario: 示例命令跨平台可执行
- **WHEN** 用户在 Windows、macOS 或 Linux 上直接使用 probes.example.yaml 中的 cmd 示例
- **THEN** 所有 cmd 示例 SHALL 能正常执行,不依赖平台特定命令