1
0

feat: 重构为多类型 checker 通用框架,支持 HTTP 与命令检查

- 引入 typed target 判别联合,支持 http 与 command 两种 checker
- expect 重构为有序规则数组,按配置顺序快速失败并生成结构化 failure
- 新增 command runner,支持 exec + args 本地命令执行
- 引入全局并发限制 maxConcurrentChecks 和 size 解析 (KB/MB/GB)
- HTTP/command 各自独立 expect pipeline,应用领域默认成功语义
- SQLite schema、API、Dashboard 全链路调整为 checker 通用契约
- 补充完整测试覆盖(192 tests),更新 README 与示例配置
This commit is contained in:
2026-05-10 22:25:21 +08:00
parent 599d973cbd
commit b8810f1182
46 changed files with 3562 additions and 1062 deletions

View File

@@ -16,18 +16,22 @@
- **THEN** 系统 SHALL 创建两个独立定时器,分别按各自频率调度
### Requirement: 组内并发拨测
系统 SHALL 在每次调度 tick 时,使用 `Promise.all` 并发执行同组内所有目标的拨测
系统 SHALL 在每次调度 tick 时并发执行同组内目标的检查,但实际同时运行的检查数 MUST 受全局 `runtime.maxConcurrentChecks` 限制
#### Scenario: 同组目标并发执行
- **WHEN** 调度器触发一次 tick该组有 3 个目标
- **THEN** 系统 SHALL 同时发起 3 个 HTTP 请求,而非顺序执行
- **WHEN** 调度器触发一次 tick该组有 3 个目标,且全局并发余量至少为 3
- **THEN** 系统 SHALL 同时执行 3 个 checker,而非顺序执行
#### Scenario: 单个目标失败不影响同组其他目标
- **WHEN** 同组中某个目标的拨测请求超时或失败
- **THEN** 其他目标的拨测 SHALL 正常完成并记录结果
- **WHEN** 同组中某个目标的检查请求超时或失败
- **THEN** 其他目标的检查 SHALL 正常完成并记录结果
#### Scenario: 全局并发限制生效
- **WHEN** 调度器同时触发 10 个目标且 runtime.maxConcurrentChecks 为 3
- **THEN** 系统 MUST 同时最多运行 3 个检查,其余检查等待并发槽位释放
### Requirement: HTTP 拨测执行
系统 SHALL 对每个目标执行 HTTP 请求,支持 GET、POST、PUT、DELETE、PATCH、HEAD 方法,并携带配置的 headers 和 body。
系统 SHALL 对 `type: http`目标执行 HTTP 请求,支持 GET、POST、PUT、DELETE、PATCH、HEAD 方法,并携带 `http.headers``http.body`
#### Scenario: 执行 GET 请求
- **WHEN** 目标配置 method 为 GET
@@ -41,82 +45,97 @@
- **WHEN** 目标配置了 headers如 Authorization
- **THEN** 系统 SHALL 在请求中包含所有配置的 headers
### Requirement: 请求超时控制
系统 SHALL 对每次拨测请求实施超时控制,超时时间使用目标配置的 timeout 值。
#### Scenario: HTTP body 读取上限
- **WHEN** HTTP response body 超过该 target 的 maxBodyBytes
- **THEN** 系统 MUST 停止读取并记录 `success=false``matched=false` 和结构化输出超限错误
#### Scenario: 请求超时
- **WHEN** 拨测请求在 timeout 时间内未收到响应
### Requirement: 请求超时控制
系统 SHALL 对每次 checker 执行实施超时控制,超时时间使用目标配置的 timeout 值。
#### Scenario: HTTP 请求超时
- **WHEN** HTTP 请求在 timeout 时间内未收到响应
- **THEN** 系统 SHALL 中止该请求,记录为失败并标注超时错误
#### Scenario: command 执行超时
- **WHEN** command 进程在 timeout 时间内未退出
- **THEN** 系统 MUST 终止该子进程,记录为失败并标注超时错误
#### Scenario: 请求在超时前完成
- **WHEN** 拨测请求在 timeout 时间内收到响应
- **THEN** 系统 SHALL 正常记录响应结果
- **WHEN** checker 在超时前完成执行
- **THEN** 系统 SHALL 正常记录执行结果并进入 expect 校验
### Requirement: expect 校验
系统 SHALL 在拨测完成后根据目标的 expect 配置校验响应,校验结果记入 check result。
系统 SHALL 在 checker 执行完成后根据目标类型的 expect 配置校验观测结果,校验结果和首个失败原因记入 check result。
#### Scenario: 校验状态码
- **WHEN** 目标配置 `expect.status: [200, 201]`
#### Scenario: HTTP 默认状态码
- **WHEN** HTTP target 未配置 `expect.status`
- **THEN** 系统 SHALL 按默认 `status: [200]` 校验响应状态码
#### Scenario: 校验 HTTP 状态码
- **WHEN** HTTP target 配置了 `expect.status: [200, 201]`
- **THEN** 系统 SHALL 检查响应状态码是否在列表中,将匹配结果记录到 matched 字段
#### Scenario: 校验响应头
- **WHEN** 目标配置了 `expect.headers: {"Content-Type": "application/json"}`
- **THEN** 系统 SHALL 检查响应头是否包含指定键值对,全部匹配时将 matched 设为 true
#### Scenario: 校验 HTTP 响应头
- **WHEN** HTTP target 配置了 `expect.headers: {"Content-Type": {contains: "application/json"}}`
- **THEN** 系统 SHALL 检查响应头是否符合指定规则,全部匹配时继续后续阶段
#### Scenario: 校验响应体包含
- **WHEN** 目标配置了 `expect.body.contains: "healthy"`
- **THEN** 系统 SHALL 检查响应体是否包含该文本,将匹配结果记录到 matched 字段
#### Scenario: 校验 HTTP 响应体
- **WHEN** HTTP target 配置了有序 `expect.body` 规则数组
- **THEN** 系统 SHALL 按数组顺序执行 body 规则,任一失败立即记录 failure 并停止后续规则
#### Scenario: 校验响应体正则
- **WHEN** 目标配置 `expect.body.regex: '"status"\\s*:\\s*"ok"'`
- **THEN** 系统 SHALL 检查响应体是否匹配该正则,将匹配结果记录到 matched 字段
#### Scenario: command 默认 exitCode
- **WHEN** command target 未配置 `expect.exitCode`
- **THEN** 系统 SHALL 按默认 `exitCode: [0]` 校验命令退出码
#### Scenario: 校验 JSON 响应
- **WHEN** 目标配置了 `expect.body.json: {"$.status": "ok"}`
- **THEN** 系统 SHALL 解析 JSON 并检查 JSONPath 对应值是否符合期望,将匹配结果记录到 matched 字段
#### Scenario: 校验 command stdout
- **WHEN** command target 配置了有序 `expect.stdout` 规则数组
- **THEN** 系统 SHALL 按数组顺序执行 stdout 规则,任一失败立即记录 failure 并停止后续规则
#### Scenario: 校验 HTML 响应CSS 选择器)
- **WHEN** 目标配置了 `expect.body.css: {"div#health": "OK"}`
- **THEN** 系统 SHALL 解析 HTML 并用 CSS 选择器提取元素文本进行比较,将匹配结果记录到 matched 字段
#### Scenario: 校验 HTML/XML 响应XPath
- **WHEN** 目标配置了 `expect.body.xpath: {"/root/status/text()": "ok"}`
- **THEN** 系统 SHALL 解析文档并用 XPath 提取节点文本进行比较,将匹配结果记录到 matched 字段
#### Scenario: 校验延迟阈值
- **WHEN** 目标配置了 `expect.maxLatencyMs: 3000`
- **THEN** 系统 SHALL 检查实际延迟是否超过阈值,将匹配结果记录到 matched 字段
#### Scenario: 无 expect 配置
- **WHEN** 目标未配置任何 expect 规则
- **THEN** 系统 SHALL 将 matched 字段设为 true
#### Scenario: 校验耗时阈值
- **WHEN** 目标配置了 `expect.maxDurationMs`
- **THEN** 系统 SHALL 检查实际 durationMs 是否超过阈值,将匹配结果记录到 matched 字段
#### Scenario: 多条 expect 规则
- **WHEN** 目标同时配置了 status、headers、body.contains、body.json 和 maxLatencyMs
- **THEN** 系统 SHALL 所有规则全部通过时 matched 为 true任一不通过则为 false
#### Scenario: 多种 body 方法 AND 组合
- **WHEN** 目标在 body 分组下配置了 contains、json、css 多种方法
- **THEN** 系统 SHALL 按 contains → regex → json → css → xpath 顺序执行,任一失败立即返回 false
- **WHEN** 目标同时配置状态、duration、元数据和内容规则
- **THEN** 系统 SHALL 所有规则全部通过时 matched 为 true任一不通过则为 false 并记录首个失败原因
### Requirement: Body 校验按需解析
系统 SHALL 仅在配置了对应 body 校验方法时才解析响应体为对应格式,避免不必要的解析开销。
系统 SHALL 仅在 HTTP target 配置了 body 校验且 status、duration、headers 阶段均通过时才读取并解析响应体,避免不必要的读取和解析开销。
#### Scenario: status 失败时不读取 body
- **WHEN** HTTP target 的 status 阶段不匹配
- **THEN** 系统 SHALL 立即返回 matched=false且 MUST NOT 读取 response body
#### Scenario: 仅配置 contains 时不解析 JSON
- **WHEN** 目标仅配置 `expect.body.contains` 而未配置 json/css/xpath
- **WHEN** HTTP target 仅配置 body contains 规则而未配置 json/css/xpath 规则
- **THEN** 系统 SHALL 不执行 JSON.parse 或 HTML/XML 解析
#### Scenario: 配置 json 时解析 JSON 失败
- **WHEN** 目标配置了 `expect.body.json` 但响应体不是合法 JSON
- **THEN** 系统 SHALL 判定 matched 为 false
- **WHEN** HTTP target 配置了 body json 规则但响应体不是合法 JSON
- **THEN** 系统 SHALL 判定 matched 为 false,并记录 json 规则对应的 failure.path
### Requirement: 拨测结果记录
系统 SHALL 在每次拨测完成后,将结果写入 SQLite 数据存储,包含 target_id、timestamp、success、status_code、latency_ms、error、matched 字段。
系统 SHALL 在每次 checker 完成后,将结果写入 SQLite 数据存储,包含 target_id、timestamp、success、matched、duration_ms、status_detail、failure 字段。
#### Scenario: 成功拨测结果记录
- **WHEN** 拨测请求成功完成(收到 HTTP 响应)
- **THEN** 系统 SHALL 记录 success=true、status_code、latency_ms、matched
#### Scenario: 成功检查结果记录
- **WHEN** checker 成功执行且 expect 全部匹配
- **THEN** 系统 SHALL 记录 success=true、matched=true、duration_ms、status_detailfailure 为 null
#### Scenario: 失败拨测结果记录
- **WHEN** 拨测请求失败(网络错误、超时等)
- **THEN** 系统 SHALL 记录 success=false、error 信息status_code 和 latency_ms 为 null
#### Scenario: 执行失败结果记录
- **WHEN** checker 执行失败(网络错误、超时、命令启动失败、输出超限等)
- **THEN** 系统 SHALL 记录 success=false、matched=false、failure.kind="error" 和具体错误信息
#### Scenario: expect 不匹配结果记录
- **WHEN** checker 执行成功但 expect 不匹配
- **THEN** 系统 SHALL 记录 success=true、matched=false、failure.kind="mismatch" 和具体不匹配信息
### Requirement: runner 选择
系统 SHALL 根据 target.type 选择对应 runner 执行检查。
#### Scenario: 选择 HTTP runner
- **WHEN** target.type 为 `http`
- **THEN** 系统 SHALL 使用 HTTP runner 执行该目标
#### Scenario: 选择 command runner
- **WHEN** target.type 为 `command`
- **THEN** 系统 SHALL 使用 command runner 执行该目标