@@ -4,18 +4,37 @@
## Requirements
### Requirement: 旧配置入口拒绝
系统 SHALL 拒绝旧版顶层 `runtime` 和顶层 `logging` 配置入口。系统 SHALL 拒绝旧版 `server.host` 、`server.port` 和 `server.dataDir` 入口,并要求使用 `server.listen` 与 `server.storage` 下的新路径。
#### Scenario: 顶层 runtime 被拒绝
- **WHEN** 配置文件声明顶层 `runtime`
- **THEN** 系统 SHALL 以配置错误退出并提示存在未知字段 `runtime`
#### Scenario: 顶层 logging 被拒绝
- **WHEN** 配置文件声明顶层 `logging`
- **THEN** 系统 SHALL 以配置错误退出并提示存在未知字段 `logging`
#### Scenario: server 旧监听字段被拒绝
- **WHEN** 配置文件声明 `server.host` 或 `server.port`
- **THEN** 系统 SHALL 以配置错误退出并提示 `server` 中存在未知字段
#### Scenario: server 旧 dataDir 字段被拒绝
- **WHEN** 配置文件声明 `server.dataDir`
- **THEN** 系统 SHALL 以配置错误退出并提示 `server` 中存在未知字段 `dataDir`
### Requirement: YAML 配置文件格式
系统 SHALL 支持通过 YAML 配置文件定义全部运行参数,包括 server 配置、runtime 配置、可选的 variables 段、checker 默认值和 typed target 列表(含可选 group 字段) 。target MUST 使用 `id` 字段作为唯一标识符, MUST 使用 `type` 字段声明 checker 类型, SHALL 支持可选的 `name` 字段作为展示名称元信息, SHALL 支持可选的 `description` 字段作为目标说明。`name` 和 `description` 均 SHALL 允许省略或显式配置为 `null` ;省略或显式 null 时解析结果 SHALL 保留为 null。HTTP 领域字段 MUST 放在 `http` 分组, cmd 领域字段 MUST 放在 `cmd` 分组, db 领域字段 MUST 放在 `db` 分组, tcp 领域字段 MUST 放在 `tcp` 分组, icmp 领域字段 MUST 放在 `icmp` 分组, udp 领域字段 MUST 放在 `udp` 分组, LLM 领域字段 MUST 放在 `llm` 分组。HTTP target 的 `http` 分组 SHALL 支持可选的 `ignoreSSL` (布尔值)和 `maxRedirects` ( 非负整数) 字段。Db target 的 `db` 分组 SHALL 支持 `url` (必填)和 `query` ( 可选) 字段。Tcp target 的 `tcp` 分组 SHALL 支持 `host` (必填)、`port` (必填)、`readBanner` (可选)、`bannerReadTimeout` (可选)和 `maxBannerBytes` ( 可选) 字段。Icmp target 的 `icmp` 分组 SHALL 支持 `host` (必填)、`count` (可选,默认 3) 和 `packetSize` (可选,默认 56) 字段。Udp target 的 `udp` 分组 SHALL 支持 `host` (必填)、`port` (必填)、`payload` (可选,默认空字符串)、`encoding` (可选,默认 `text` )、`responseEncoding` (可选,默认 `text` )和 `maxResponseBytes` (可选,默认 4096) 字段。LLM target 的 `llm` 分组 SHALL 支持 `provider` (必填)、`url` (必填)、`model` (必填)、`prompt` (必填)、`mode` (可选,默认 `http` )、`key` (可选,默认空字符串)、`authToken` (可选)、`headers` (可选)、`ignoreSSL` (可选,默认 `false` )、`options` (可选)和 `providerOptions` (可选)字段。
系统 SHALL 支持通过 YAML 配置文件定义全部运行参数,包括 server 配置、probes 执行 配置、可选的 variables 段、checker 默认值和 typed target 列表(含可选 group 字段)。server 配置 SHALL 将 HTTP 监听参数放在 `server.listen` 分组,将本地数据目录和历史数据保留时长放在 `server.storage` 分组,将运行时日志配置放在 `server.logging` 分组。拨测全局执行策略 SHALL 放在 `probes.execution` 分组。 target MUST 使用 `id` 字段作为唯一标识符, MUST 使用 `type` 字段声明 checker 类型, SHALL 支持可选的 `name` 字段作为展示名称元信息, SHALL 支持可选的 `description` 字段作为目标说明。`name` 和 `description` 均 SHALL 允许省略或显式配置为 `null` ;省略或显式 null 时解析结果 SHALL 保留为 null。HTTP 领域字段 MUST 放在 `http` 分组, cmd 领域字段 MUST 放在 `cmd` 分组, db 领域字段 MUST 放在 `db` 分组, tcp 领域字段 MUST 放在 `tcp` 分组, icmp 领域字段 MUST 放在 `icmp` 分组, udp 领域字段 MUST 放在 `udp` 分组, LLM 领域字段 MUST 放在 `llm` 分组。HTTP target 的 `http` 分组 SHALL 支持可选的 `ignoreSSL` (布尔值)和 `maxRedirects` ( 非负整数) 字段。Db target 的 `db` 分组 SHALL 支持 `url` (必填)和 `query` ( 可选) 字段。Tcp target 的 `tcp` 分组 SHALL 支持 `host` (必填)、`port` (必填)、`readBanner` (可选)、`bannerReadTimeout` (可选)和 `maxBannerBytes` ( 可选) 字段。Icmp target 的 `icmp` 分组 SHALL 支持 `host` (必填)、`count` (可选,默认 3) 和 `packetSize` (可选,默认 56) 字段。Udp target 的 `udp` 分组 SHALL 支持 `host` (必填)、`port` (必填)、`payload` (可选,默认空字符串)、`encoding` (可选,默认 `text` )、`responseEncoding` (可选,默认 `text` )和 `maxResponseBytes` (可选,默认 4096) 字段。LLM target 的 `llm` 分组 SHALL 支持 `provider` (必填)、`url` (必填)、`model` (必填)、`prompt` (必填)、`mode` (可选,默认 `http` )、`key` (可选,默认空字符串)、`authToken` (可选)、`headers` (可选)、`ignoreSSL` (可选,默认 `false` )、`options` (可选)和 `providerOptions` (可选)字段。
`defaults.http` 分组 SHALL 仅支持 `headers` (可选)和 `maxBodyBytes` (可选)字段。`defaults.http` 分组 MUST NOT 支持 `method` 字段。`defaults.tcp` 分组 SHALL 仅支持 `bannerReadTimeout` (可选)和 `maxBannerBytes` (可选)字段。`defaults.icmp` 分组 SHALL 仅支持空对象。`defaults.udp` 分组 SHALL 仅支持 `encoding` (可选)、`responseEncoding` (可选)和 `maxResponseBytes` (可选)字段。`defaults.llm` 分组 SHALL 仅支持 `mode` (可选)、`headers` (可选)、`ignoreSSL` (可选)、`options` (可选)和 `providerOptions` (可选)字段。
#### Scenario: 完整配置文件解析
- **WHEN** 系统启动并读取包含 server、runtime 、variables、defaults、targets( 含 id、group 字段)的 YAML 配置文件
- **WHEN** 系统启动并读取包含 server.listen、server.storage、server.logging、probes.execution 、variables、defaults、targets( 含 id、group 字段)的 YAML 配置文件
- **THEN** 系统 SHALL 正确解析所有字段并用于初始化服务、调度引擎和对应 checker runner
#### Scenario: 最简 HTTP 配置文件解析
- **WHEN** 系统读取只包含一个 `type: http` target( 含 `id` 和 `http.url` )的 YAML 配置文件(省略 server、runtime 、variables、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"),并保留 name=null、description=null
- **WHEN** 系统读取只包含一个 `type: http` target( 含 `id` 和 `http.url` )的 YAML 配置文件(省略 server、probes 、variables、defaults 和 expect)
- **THEN** 系统 SHALL 使用内置默认值填充未指定的字段( host=127.0.0.1, port=3000, dataD ir=./data, interval=30s, timeout=10s, probes.execution .maxConcurrentChecks=20, server.storage.retention=7d, http.method=GET, http.maxBodyBytes=100MB, http.ignoreSSL=false, http.maxRedirects=0, group="default"),并保留 name=null、description=null
#### Scenario: 最简 cmd 配置文件解析
- **WHEN** 系统读取只包含一个 `type: cmd` target( 含 `id` 和 `cmd.exec` )的 YAML 配置文件
@@ -136,8 +155,8 @@
- **THEN** 系统 SHALL 以错误退出并提示格式错误
#### Scenario: maxConcurrentChecks 非法
- **WHEN** runtime .maxConcurrentChecks 不是正整数
- **THEN** 系统 SHALL 以错误退出并提示 runtime .maxConcurrentChecks 格式错误
- **WHEN** probes.execution .maxConcurrentChecks 不是正整数
- **THEN** 系统 SHALL 以错误退出并提示 probes.execution .maxConcurrentChecks 格式错误
#### Scenario: interval 或 timeout 解析结果非法
- **WHEN** interval 或 timeout 解析结果不是正整数毫秒(如 `0ms` 或 `1.5ms` )
@@ -325,14 +344,14 @@
- **THEN** 系统 SHALL 将其解析为 524288 bytes
### Requirement: runtime 并发配置
系统 SHALL 支持 `runtime .maxConcurrentChecks` 配置全局最大并发检查数。
系统 SHALL 支持 `probes.execution .maxConcurrentChecks` 配置全局最大并发检查数。`probes` 和 `probes.execution` 配置段均 SHALL 为可选,省略时使用默认值。
#### Scenario: 使用默认并发限制
- **WHEN** YAML 中未配置 runtime .maxConcurrentChecks
- **WHEN** YAML 中未配置 `probes` 或未配置 `probes.execution .maxConcurrentChecks`
- **THEN** 系统 SHALL 使用默认值 20
#### Scenario: 配置并发限制
- **WHEN** YAML 中配置 `runtime .maxConcurrentChecks: 5`
- **WHEN** YAML 中配置 `probes.execution .maxConcurrentChecks: 5`
- **THEN** 系统 SHALL 将全局最大并发检查数设置为 5
### Requirement: YAML 配置使用 Bun 内置解析
@@ -428,33 +447,33 @@
- **THEN** `targets.expect` SHALL 存储变量替换后的 Raw expect JSON, 而不是包含 `kind` 或 resolved matcher 的运行期执行计划
### Requirement: 数据保留配置字段
配置 schema 的 `runtim e` 段 SHALL 支持 `retention` 字段,类型为字符串,格式为 `<数字><单位>` (单位:`d` 天、`h` 小时、`m` 分钟),用于指定历史数据保留时长。
配置 schema 的 `server.storag e` 段 SHALL 支持 `retention` 字段,类型为字符串,格式为 `<数字><单位>` (单位:`d` 天、`h` 小时、`m` 分钟),用于指定历史数据保留时长。
#### Scenario: retention 字段校验通过
- **WHEN** 配置文件中 `runtim e.retention` 为合法格式(如 `"7d"` 、`"24h"` 、`"30m"` )
- **WHEN** 配置文件中 `server.storag e.retention` 为合法格式(如 `"7d"` 、`"24h"` 、`"30m"` )
- **THEN** 配置校验 SHALL 通过
#### Scenario: retention 字段格式非法
- **WHEN** 配置文件中 `runtim e.retention` 为非法格式(如 `"abc"` 、`"7x"` 、`""` )
- **WHEN** 配置文件中 `server.storag e.retention` 为非法格式(如 `"abc"` 、`"7x"` 、`""` )
- **THEN** 配置校验 SHALL 失败并报告格式错误
#### Scenario: retention 字段缺省
- **WHEN** 配置文件中未指定 `runtim e.retention`
- **WHEN** 配置文件中未指定 `server.storag e.retention`
- **THEN** 系统 SHALL 使用默认值 `"7d"`
### Requirement: 数据目录路径解析
配置加载流程 SHALL 将 `server.dataDir` 相对路径基于配置文件所在目录( configDir) 解析为绝对路径。绝对路径 SHALL 保持不变。
配置加载流程 SHALL 将 `server.storage. dataDir` 相对路径基于配置文件所在目录( configDir) 解析为绝对路径。绝对路径 SHALL 保持不变。
#### Scenario: dataDir 为相对路径
- **WHEN** 配置文件位于 `/opt/dial/probes.yaml` ,且 `server.dataDir` 配置为 `./data`
- **WHEN** 配置文件位于 `/opt/dial/probes.yaml` ,且 `server.storage. dataDir` 配置为 `./data`
- **THEN** 系统 SHALL 将 dataDir 解析为 `/opt/dial/data` ,而非依赖进程 cwd
#### Scenario: dataDir 为绝对路径
- **WHEN** `server.dataDir` 配置为 `/var/lib/dial/data`
- **WHEN** `server.storage. dataDir` 配置为 `/var/lib/dial/data`
- **THEN** 系统 SHALL 直接使用该绝对路径,不做额外解析
#### Scenario: dataDir 使用默认值
- **WHEN** 未配置 `server.dataDir` (使用默认值 `./data` )
- **WHEN** 未配置 `server.storage. dataDir` (使用默认值 `./data` )
- **THEN** 系统 SHALL 将默认值 `./data` 基于 configDir 解析为绝对路径
### Requirement: target 通用元信息字段约束
@@ -634,51 +653,51 @@
- **THEN** 系统 SHALL 以配置错误退出,提示 expect.stream.firstTokenMs 格式错误
### Requirement: 日志配置格式
系统 SHALL 支持可选的顶层 `logging` 配置,用于定义运行时日志等级、命令行日志等级、文件日志等级、文件路径和滚动策略。`logging` 未配置时 SHALL 使用内置默认值。系统 SHALL NOT 支持 `logging.console.enabled` 、`logging.console.format` 、`logging.file.enabled` 、`logging.file.format` 或 `logging.file.rotation.enabled` 字段。
系统 SHALL 支持可选的 `server. logging` 配置,用于定义运行时日志等级、命令行日志等级、文件日志等级、文件路径和滚动策略。`server. logging` 未配置时 SHALL 使用内置默认值。系统 SHALL NOT 支持 `server. logging.console.enabled` 、`server. logging.console.format` 、`server. logging.file.enabled` 、`server. logging.file.format` 或 `server. logging.file.rotation.enabled` 字段。
#### Scenario: 未配置 logging 使用默认值
- **WHEN** 配置文件未声明 `logging`
- **THEN** 系统 SHALL 使用 `logging.level=info` 、`logging.console.level=info` 、`logging.file.level=info` 、`logging.file.path=<resolved dataDir>/logs/dial.log` 、`logging.file.rotation.size=50MB` 、`logging.file.rotation.frequency=daily` 和 `logging.file.rotation.maxFiles=14`
- **WHEN** 配置文件未声明 `server. logging`
- **THEN** 系统 SHALL 使用 `server. logging.level=info` 、`server. logging.console.level=info` 、`server. logging.file.level=info` 、`server. logging.file.path=<resolved dataDir>/logs/dial.log` 、`server. logging.file.rotation.size=50MB` 、`server. logging.file.rotation.frequency=daily` 和 `server. logging.file.rotation.maxFiles=14`
#### Scenario: console 和 file level 继承全局 level
- **WHEN** 配置声明 `logging.level: warn` 且未声明 `logging.console.level` 和 `logging.file.level`
- **WHEN** 配置声明 `server. logging.level: warn` 且未声明 `server. logging.console.level` 和 `server. logging.file.level`
- **THEN** 系统 SHALL 将 console 和 file 的日志等级均解析为 `warn`
#### Scenario: 显式配置文件日志路径
- **WHEN** 配置声明 `logging.file.path`
- **WHEN** 配置声明 `server. logging.file.path`
- **THEN** 系统 SHALL 使用该路径作为文件日志路径,而不是默认 `<resolved dataDir>/logs/dial.log`
#### Scenario: 相对日志路径
- **WHEN** `logging.file.path` 是相对路径
- **WHEN** `server. logging.file.path` 是相对路径
- **THEN** 系统 SHALL 基于配置文件所在目录解析为绝对路径
#### Scenario: 绝对日志路径
- **WHEN** `logging.file.path` 是绝对路径
- **WHEN** `server. logging.file.path` 是绝对路径
- **THEN** 系统 SHALL 原样使用该绝对路径,并允许该路径位于 `dataDir` 之外
#### Scenario: 不支持日志开关和格式字段
- **WHEN** 配置声明 `logging.console.enabled` 、`logging.console.format` 、`logging.file.enabled` 、`logging.file.format` 或 `logging.file.rotation.enabled`
- **WHEN** 配置声明 `server. logging.console.enabled` 、`server. logging.console.format` 、`server. logging.file.enabled` 、`server. logging.file.format` 或 `server. logging.file.rotation.enabled`
- **THEN** 系统 SHALL 以配置错误退出并提示存在未知字段
### Requirement: 日志配置校验
系统 SHALL 在启动期校验 `logging` 配置。日志等级 SHALL 只能是 `trace` 、`debug` 、`info` 、`warn` 、`error` 或 `fatal` 。`rotation.size` SHALL 使用有效 size 格式且解析为正整数字节数。`rotation.frequency` SHALL 只能是 `hourly` 、`daily` 或 `weekly` 。`rotation.maxFiles` SHALL 是正整数。
系统 SHALL 在启动期校验 `server. logging` 配置。日志等级 SHALL 只能是 `trace` 、`debug` 、`info` 、`warn` 、`error` 或 `fatal` 。`rotation.size` SHALL 使用有效 size 格式且解析为正整数字节数。`rotation.frequency` SHALL 只能是 `hourly` 、`daily` 或 `weekly` 。`rotation.maxFiles` SHALL 是正整数。
#### Scenario: 非法日志等级
- **WHEN** 配置声明 `logging.level: verbose`
- **WHEN** 配置声明 `server. logging.level: verbose`
- **THEN** 系统 SHALL 以配置错误退出并提示日志等级非法
#### Scenario: 非法滚动大小
- **WHEN** 配置声明 `logging.file.rotation.size: "large"`
- **WHEN** 配置声明 `server. logging.file.rotation.size: "large"`
- **THEN** 系统 SHALL 以配置错误退出并提示 size 格式非法
#### Scenario: 非法滚动频率
- **WHEN** 配置声明 `logging.file.rotation.frequency: monthly`
- **WHEN** 配置声明 `server. logging.file.rotation.frequency: monthly`
- **THEN** 系统 SHALL 以配置错误退出并提示 frequency 非法
#### Scenario: 非法归档数量
- **WHEN** 配置声明 `logging.file.rotation.maxFiles: 0`
- **WHEN** 配置声明 `server. logging.file.rotation.maxFiles: 0`
- **THEN** 系统 SHALL 以配置错误退出并提示 maxFiles 必须为正整数
#### Scenario: 非法日志路径
- **WHEN** 配置声明 `logging.file.path` 为空字符串或空白字符串
- **WHEN** 配置声明 `server. logging.file.path` 为空字符串或空白字符串
- **THEN** 系统 SHALL 以配置错误退出并提示日志路径非法