1
0
Files
DiAL/openspec/specs/db-checker/spec.md
lanyuanxiaoyao 146cef982e feat: 新增 DB checker — 支持 PostgreSQL/MySQL/SQLite 连接测试与 SQL 查询断言
- 实现 db 类型 checker,使用 Bun 内置 SQL 类
- 支持 db.url 连接字符串和可选 db.query 查询语句
- expect 支持 maxDurationMs、rowCount、rows 逐列校验
- 凭据屏蔽序列化输出
- SQLite 内存数据库测试覆盖
2026-05-16 09:00:15 +08:00

142 lines
7.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
## Purpose
定义 db 类型拨测目标的配置格式、执行逻辑、expect 断言规则和启动期校验。
## Requirements
### Requirement: db target 配置
系统 SHALL 支持 `type: db` 的 target 配置,通过 `db.url` 描述数据库连接字符串(遵循 Bun SQL 支持的格式),通过可选的 `db.query` 描述待执行的 SQL 语句。
#### Scenario: 解析仅连接的 db target
- **WHEN** YAML 中 target 配置 `type: db``db.url: "postgres://user:pass@localhost:5432/mydb"`,未配置 `db.query`
- **THEN** 系统 SHALL 将其解析为 db checker仅执行连通性检测
#### Scenario: 解析带查询的 db target
- **WHEN** YAML 中 target 配置 `type: db``db.url: "mysql://user:pass@host:3306/app"``db.query: "SELECT count(*) as cnt FROM users"`
- **THEN** 系统 SHALL 将其解析为 db checker执行连通性检测后执行指定 SQL 并进入 expect 校验
#### Scenario: db target 缺少 url
- **WHEN** YAML 中 target 配置 `type: db` 但缺少 `db.url`
- **THEN** 系统 SHALL 以配置错误退出,并提示该 target 缺少 db.url 字段
#### Scenario: db.url 为空字符串
- **WHEN** YAML 中 target 配置 `db.url: ""`
- **THEN** 系统 SHALL 以配置错误退出,并提示 db.url 不能为空
#### Scenario: db.query 为空字符串
- **WHEN** YAML 中 target 配置 `db.query: ""`
- **THEN** 系统 SHALL 以配置错误退出,并提示 db.query 不能为空字符串(如不需要查询则不配置该字段)
#### Scenario: db 分组未知字段失败
- **WHEN** YAML 中 db target 的 `db` 分组包含 `timeout: 5` 等未知字段
- **THEN** 系统 SHALL 以配置错误退出,提示 db 分组包含未知字段
#### Scenario: SQLite 连接字符串
- **WHEN** YAML 中 target 配置 `db.url: "sqlite:///data/app.db"`
- **THEN** 系统 SHALL 将其解析为 db checker使用 SQLite 文件数据库
#### Scenario: url 格式由 Bun 运行时校验
- **WHEN** YAML 中 target 配置 `db.url` 为 Bun 不支持的格式
- **THEN** 系统 SHALL 在执行阶段捕获连接错误并作为 phase="connect" 的 failure 返回,而非在启动期校验 URL 格式
### Requirement: db checker 执行
系统 SHALL 按 db target 配置连接数据库并执行查询,每次执行都新建连接并在完成后关闭。连接能力本身作为监控指标。
#### Scenario: 仅连接测试成功
- **WHEN** db target 未配置 `db.query` 且数据库连接成功
- **THEN** 系统 SHALL 内部执行 `SELECT 1` 验证连通性,记录 `matched=true``durationMs`
#### Scenario: 连接失败
- **WHEN** db target 的数据库连接失败(网络不通、认证错误、数据库不存在等)
- **THEN** 系统 SHALL 记录 `matched=false`failure 的 phase 为 `"connect"`message 包含可读错误信息
#### Scenario: 查询执行成功
- **WHEN** db target 配置了 `db.query` 且 SQL 执行成功返回结果集
- **THEN** 系统 SHALL 记录 `durationMs`(从连接开始到查询完成),并进入 expect 校验
#### Scenario: 查询执行失败
- **WHEN** db target 配置了 `db.query` 且 SQL 执行报错(语法错误、权限不足、表不存在等)
- **THEN** 系统 SHALL 记录 `matched=false`failure 的 phase 为 `"query"`message 包含数据库返回的错误信息
#### Scenario: 执行超时
- **WHEN** db target 在 timeout 时间内未完成(连接或查询)
- **THEN** 系统 SHALL 关闭连接,记录 `matched=false`failure 的 phase 为 `"connect"``"query"`取决于超时发生的阶段message 包含超时信息
#### Scenario: 每次执行新建连接
- **WHEN** db target 被引擎调度执行
- **THEN** 系统 SHALL 创建新的 SQL 连接实例max: 1执行完成后立即关闭连接close timeout: 0
#### Scenario: 使用 unsafe 执行用户 SQL
- **WHEN** db target 配置了 `db.query`
- **THEN** 系统 SHALL 使用 `sql.unsafe(query)` 执行用户配置的 SQL 文本,不限制 SQL 类型
#### Scenario: 响应 abort signal
- **WHEN** 引擎注入的 `ctx.signal` 被 abort
- **THEN** 系统 SHALL 立即关闭数据库连接
### Requirement: db expect 校验
系统 SHALL 支持 db 专用 expect包括 `maxDurationMs``rowCount``rows`,按 duration、rowCount、rows 的阶段顺序快速失败。
#### Scenario: maxDurationMs 校验
- **WHEN** db target 配置 `expect.maxDurationMs: 3000` 且实际执行耗时 4000ms
- **THEN** 系统 SHALL 返回 `matched=false`failure 的 phase 为 `"duration"`
#### Scenario: rowCount 校验通过
- **WHEN** db target 配置 `expect.rowCount: { gte: 1 }` 且查询返回 5 行
- **THEN** 系统 SHALL 判定 rowCount 阶段通过,继续后续 expect 阶段
#### Scenario: rowCount 校验失败
- **WHEN** db target 配置 `expect.rowCount: { gte: 1 }` 且查询返回 0 行
- **THEN** 系统 SHALL 返回 `matched=false`failure 的 phase 为 `"rowCount"`path 为 `"rowCount"`expected 为 `{ gte: 1 }`actual 为 0
#### Scenario: rows 按索引匹配列值operator 形式)
- **WHEN** db target 配置 `expect.rows: [{ cnt: { gte: 100 } }]` 且查询首行 cnt 列值为 50
- **THEN** 系统 SHALL 返回 `matched=false`failure 的 phase 为 `"row"`path 为 `"rows[0].cnt"`
#### Scenario: rows 按索引匹配列值(字面量形式)
- **WHEN** db target 配置 `expect.rows: [{ status: "active" }]` 且查询首行 status 列值为 `"active"`
- **THEN** 系统 SHALL 判定该行该列通过(字面量等价于 `{ equals: "active" }`
#### Scenario: rows 只检查声明的列
- **WHEN** db target 配置 `expect.rows: [{ cnt: { gte: 1 } }]` 且查询首行包含 cnt、name、age 三列
- **THEN** 系统 SHALL 仅检查 cnt 列,忽略 name 和 age 列
#### Scenario: rows 结果行数不足
- **WHEN** db target 配置 `expect.rows` 包含 3 个元素但查询仅返回 2 行
- **THEN** 系统 SHALL 返回 `matched=false`failure 的 phase 为 `"row"`message 说明结果行数不足
#### Scenario: 无 query 时 expect 被忽略
- **WHEN** db target 未配置 `db.query` 但配置了 `expect.rowCount`
- **THEN** 系统 SHALL 忽略 expect 中的 rowCount 和 rows 断言(仅 maxDurationMs 生效)
#### Scenario: 快速失败顺序
- **WHEN** db target 同时配置 maxDurationMs、rowCount 和 rows
- **THEN** 系统 SHALL 按 duration → rowCount → rows 顺序校验,任一阶段失败立即返回
### Requirement: db checker 启动期配置校验
系统 SHALL 在启动期对 db checker 的配置契约和语义执行严格校验。Db target 的 `db` 分组 SHALL 只允许 `url``query` 字段。Db expect SHALL 只允许 `maxDurationMs``rowCount``rows` 字段。
#### Scenario: db expect maxDurationMs 非法
- **WHEN** YAML 中 db target 配置 `expect.maxDurationMs` 不是非负有限数字
- **THEN** 系统 SHALL 以配置错误退出,提示 expect.maxDurationMs 格式错误
#### Scenario: db expect rowCount 非法
- **WHEN** YAML 中 db target 配置 `expect.rowCount` 不是合法的 operator 对象
- **THEN** 系统 SHALL 以配置错误退出,提示 expect.rowCount 格式错误
#### Scenario: db expect rows 非法
- **WHEN** YAML 中 db target 配置 `expect.rows` 不是对象数组
- **THEN** 系统 SHALL 以配置错误退出,提示 expect.rows 必须为对象数组
#### Scenario: db expect rows 元素列值非法
- **WHEN** YAML 中 db target 配置 `expect.rows: [{ cnt: { foo: 1 } }]`,其中 foo 不是合法 operator
- **THEN** 系统 SHALL 以配置错误退出,提示 rows 中包含未知 operator
#### Scenario: db expect 未知字段失败
- **WHEN** YAML 中 db target 的 expect 包含 `status: [200]` 或其他非 db expect 字段
- **THEN** 系统 SHALL 以配置错误退出,提示 expect 包含未知字段
#### Scenario: db expect rows 中 match 正则非法
- **WHEN** YAML 中 db target 配置 `expect.rows: [{ name: { match: "[invalid" } }]`
- **THEN** 系统 SHALL 在启动期配置校验失败,而不是延迟到运行期抛错