## Purpose 定义拨测调度引擎的行为:按 interval 分组定时、组内并发拨测、expect 结果校验和结果持久化。 ## Requirements ### Requirement: 按 interval 分组调度 系统 SHALL 将拨测目标按 interval 值分组,每组使用独立的定时器进行调度。 #### Scenario: 相同 interval 的目标共享定时器 - **WHEN** 多个 target 配置了相同的 interval(如 30s) - **THEN** 系统 SHALL 使用同一个 `setInterval` 定时器,每次 tick 并发拨测所有该组目标 #### Scenario: 不同 interval 的目标各自调度 - **WHEN** target A 配置 15s interval,target B 配置 30s interval - **THEN** 系统 SHALL 创建两个独立定时器,分别按各自频率调度 ### Requirement: 组内并发拨测 系统 SHALL 在每次调度 tick 时,使用 `Promise.all` 并发执行同组内所有目标的拨测。 #### Scenario: 同组目标并发执行 - **WHEN** 调度器触发一次 tick,该组有 3 个目标 - **THEN** 系统 SHALL 同时发起 3 个 HTTP 请求,而非顺序执行 #### Scenario: 单个目标失败不影响同组其他目标 - **WHEN** 同组中某个目标的拨测请求超时或失败 - **THEN** 其他目标的拨测 SHALL 正常完成并记录结果 ### Requirement: HTTP 拨测执行 系统 SHALL 对每个目标执行 HTTP 请求,支持 GET、POST、PUT、DELETE、PATCH、HEAD 方法,并携带配置的 headers 和 body。 #### Scenario: 执行 GET 请求 - **WHEN** 目标配置 method 为 GET - **THEN** 系统 SHALL 发送 GET 请求到目标 URL #### Scenario: 执行 POST 请求带 body - **WHEN** 目标配置 method 为 POST 且指定了 body 和 Content-Type header - **THEN** 系统 SHALL 发送带指定 body 的 POST 请求 #### Scenario: 携带自定义 headers - **WHEN** 目标配置了 headers(如 Authorization) - **THEN** 系统 SHALL 在请求中包含所有配置的 headers ### Requirement: 请求超时控制 系统 SHALL 对每次拨测请求实施超时控制,超时时间使用目标配置的 timeout 值。 #### Scenario: 请求超时 - **WHEN** 拨测请求在 timeout 时间内未收到响应 - **THEN** 系统 SHALL 中止该请求,记录为失败并标注超时错误 #### Scenario: 请求在超时前完成 - **WHEN** 拨测请求在 timeout 时间内收到响应 - **THEN** 系统 SHALL 正常记录响应结果 ### Requirement: expect 校验 系统 SHALL 在拨测完成后根据目标的 expect 配置校验响应,校验结果记入 check result。 #### Scenario: 校验状态码 - **WHEN** 目标配置了 `expect.status: [200, 201]` - **THEN** 系统 SHALL 检查响应状态码是否在列表中,将匹配结果记录到 matched 字段 #### Scenario: 校验响应头 - **WHEN** 目标配置了 `expect.headers: {"Content-Type": "application/json"}` - **THEN** 系统 SHALL 检查响应头是否包含指定键值对,全部匹配时将 matched 设为 true #### Scenario: 校验响应体包含 - **WHEN** 目标配置了 `expect.body.contains: "healthy"` - **THEN** 系统 SHALL 检查响应体是否包含该文本,将匹配结果记录到 matched 字段 #### Scenario: 校验响应体正则 - **WHEN** 目标配置了 `expect.body.regex: '"status"\\s*:\\s*"ok"'` - **THEN** 系统 SHALL 检查响应体是否匹配该正则,将匹配结果记录到 matched 字段 #### Scenario: 校验 JSON 响应 - **WHEN** 目标配置了 `expect.body.json: {"$.status": "ok"}` - **THEN** 系统 SHALL 解析 JSON 并检查 JSONPath 对应值是否符合期望,将匹配结果记录到 matched 字段 #### 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: 多条 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 ### Requirement: Body 校验按需解析 系统 SHALL 仅在配置了对应 body 校验方法时才解析响应体为对应格式,避免不必要的解析开销。 #### Scenario: 仅配置 contains 时不解析 JSON - **WHEN** 目标仅配置 `expect.body.contains` 而未配置 json/css/xpath - **THEN** 系统 SHALL 不执行 JSON.parse 或 HTML/XML 解析 #### Scenario: 配置 json 时解析 JSON 失败 - **WHEN** 目标配置了 `expect.body.json` 但响应体不是合法 JSON - **THEN** 系统 SHALL 判定 matched 为 false ### Requirement: 拨测结果记录 系统 SHALL 在每次拨测完成后,将结果写入 SQLite 数据存储,包含 target_id、timestamp、success、status_code、latency_ms、error、matched 字段。 #### Scenario: 成功拨测结果记录 - **WHEN** 拨测请求成功完成(收到 HTTP 响应) - **THEN** 系统 SHALL 记录 success=true、status_code、latency_ms、matched #### Scenario: 失败拨测结果记录 - **WHEN** 拨测请求失败(网络错误、超时等) - **THEN** 系统 SHALL 记录 success=false、error 信息,status_code 和 latency_ms 为 null