将 type/configKey 从 "command" 统一为 "cmd",源码目录 runner/command/ → runner/cmd/, spec 目录 command-checker/ → cmd-checker/,测试全部改用 bun -e 替代 Unix 系统命令, 归档 cmd-checker-enhancement 变更并同步 delta spec 到主 spec。
4.9 KiB
Purpose
定义后端代码中 es-toolkit 和 Bun 内置 API 的使用规范:类型判断、空值检测、深度比较、错误判断、并发控制、集合分组和 Web API 标准方法,替代手写实现落实库使用优先级规则。
Requirements
Requirement: 使用 es-toolkit 进行类型判断
系统 SHALL 使用 es-toolkit 的 isPlainObject 替代手写的对象类型判断函数,用于 expect 校验中区分纯值(原始值)和操作符对象。
Scenario: 识别纯对象为操作符
- WHEN body 校验规则中 expected 配置为
{ equals: "value" }(纯对象操作符) - THEN
isPlainObject(expected)SHALL 返回 true,系统按操作符语义处理
Scenario: 排除非纯对象作为操作符
- WHEN body 校验规则中 expected 为原始值如
"value"或数字200 - THEN
isPlainObject(expected)SHALL 返回 false,系统按 equals 默认操作符处理
Requirement: 使用 es-toolkit 进行空值检测
系统 SHALL 使用 es-toolkit 的 isNil 替代手写的 actual === null || actual === undefined 检测,用于 expect 中 empty 操作符的空值判断。
Scenario: null 值判定为空
- WHEN 校验值为 null
- THEN
isNil(null)SHALL 返回 true
Scenario: undefined 值判定为空
- WHEN 校验值为 undefined
- THEN
isNil(undefined)SHALL 返回 true
Scenario: 非空值判定为非空
- WHEN 校验值为 0、"false"、空数组
[]等非 nil 值 - THEN
isNil(value)SHALL 返回 false
Requirement: 使用 es-toolkit 进行空对象检测
系统 SHALL 使用 es-toolkit 的 isEmptyObject 替代手写的 typeof actual === "object" && Object.keys(actual).length === 0 检测,用于 expect 中 empty 操作符的空对象判断。
Scenario: 空对象判定为空
- WHEN 校验值为
{} - THEN
isEmptyObject({})SHALL 返回 true
Scenario: 非空对象判定为非空
- WHEN 校验值为
{ key: "val" } - THEN
isEmptyObject({ key: "val" })SHALL 返回 false
Scenario: null 不是空对象
- WHEN 校验值为 null
- THEN
isEmptyObject(null)SHALL 返回 false(空值由 isNil 前置处理)
Requirement: 使用 es-toolkit 进行深度相等比较
系统 SHALL 使用 es-toolkit 的 isEqual 替代 !== 浅比较,用于 expect 中 equals 操作符的值比较,支持对象和数组的深度比较。
Scenario: 原始值浅比较
- WHEN expected 和 actual 均为原始值(字符串、数字、布尔值、null)
- THEN
isEqual(actual, expected)的行为 SHALL 与actual === expected一致
Scenario: 对象深度比较
- WHEN expected 和 actual 均为对象(如从 JSONPath 提取的结构化数据)
- THEN
isEqual(actual, expected)SHALL 递归比较所有属性值,而非引用比较
Requirement: 使用 es-toolkit 进行错误类型判断
系统 SHALL 使用 es-toolkit 的 isError 替代 error instanceof Error,用于 HTTP runner 和 cmd runner 中的错误类型判断。
Scenario: Error 实例识别
- WHEN 错误对象为
new Error("msg") - THEN
isError(error)SHALL 返回 true
Scenario: Error 子类识别
- WHEN 错误对象为继承 Error 的自定义类型
- THEN
isError(error)SHALL 返回 true
Scenario: 非 Error 对象识别
- WHEN 错误对象为字符串或普通对象
- THEN
isError(error)SHALL 返回 false
Requirement: 使用 es-toolkit Semaphore 实现并发控制
系统 SHALL 使用 es-toolkit 的 Semaphore 类替代手写的信号量实现(计数器 + Promise 队列),用于 ProbeEngine 中的组内并发拨测控制。
Scenario: 获取并发槽位
- WHEN 当前并发数未达上限
- THEN
semaphore.acquire()SHALL 立即返回,不阻塞
Scenario: 等待并发槽位
- WHEN 当前并发数已达上限 maxConcurrentChecks
- THEN
semaphore.acquire()SHALL 阻塞等待,直到其他任务调用semaphore.release()
Scenario: 释放并发槽位
- WHEN 调用
semaphore.release() - THEN 系统 SHALL 唤醒一个等待中的 acquire() 调用
Requirement: 使用 es-toolkit groupBy 实现 target 分组
系统 SHALL 使用 es-toolkit 的 groupBy 函数替代手写的 Map 循环分组,用于 ProbeEngine 中按 interval 分组拨测目标。
Scenario: 按 interval 分组
- WHEN 输入包含不同 intervalMs 值的多个 target
- THEN
groupBy(targets, t => t.intervalMs)SHALL 返回 key 为 intervalMs 值的分组对象,值为对应 target 数组
Requirement: 使用 Bun 内置 API 进行 Headers 转换
系统 SHALL 使用 Object.fromEntries(headers) 标准 Web API 替代手写的 headersToRecord 函数,用于将 Fetch API 的 Headers 对象转换为键值对。
Scenario: 转换响应头
- WHEN HTTP runner 获取到 response headers
- THEN
Object.fromEntries(response.headers)SHALL 返回以 header 名称为 key、header 值为 value 的对象