## Purpose 定义拨测工具的 YAML 配置文件格式、解析校验规则和 CLI 启动流程。 ## Requirements ### 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` 分组。HTTP target 的 `http` 分组 SHALL 支持可选的 `ignoreSSL`(布尔值)和 `maxRedirects`(非负整数)字段。Db target 的 `db` 分组 SHALL 支持 `url`(必填)和 `query`(可选)字段。Tcp target 的 `tcp` 分组 SHALL 支持 `host`(必填)、`port`(必填)、`readBanner`(可选)、`bannerReadTimeout`(可选)和 `maxBannerBytes`(可选)字段。 `defaults.http` 分组 SHALL 仅支持 `headers`(可选)和 `maxBodyBytes`(可选)字段。`defaults.http` 分组 MUST NOT 支持 `method` 字段。`defaults.tcp` 分组 SHALL 仅支持 `bannerReadTimeout`(可选)和 `maxBannerBytes`(可选)字段。 #### Scenario: 完整配置文件解析 - **WHEN** 系统启动并读取包含 server、runtime、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 #### Scenario: 最简 cmd 配置文件解析 - **WHEN** 系统读取只包含一个 `type: cmd` target(含 `id` 和 `cmd.exec`)的 YAML 配置文件 - **THEN** 系统 SHALL 使用内置默认值填充未指定的字段(interval=30s, timeout=10s, cmd.cwd 为配置文件所在目录, cmd.maxOutputBytes=100MB),并保留 name=null、description=null #### Scenario: per-target 配置覆盖全局默认值 - **WHEN** 某个 target 指定 interval、timeout 或对应领域分组中的默认字段 - **THEN** 该 target SHALL 使用其自身的值,不受 defaults 中对应字段影响 #### Scenario: HTTP target 配置 ignoreSSL - **WHEN** YAML 配置中 HTTP target 设置 `http.ignoreSSL: true` - **THEN** 系统 SHALL 解析该字段并在执行时跳过 SSL 证书校验 #### Scenario: HTTP target 配置 maxRedirects - **WHEN** YAML 配置中 HTTP target 设置 `http.maxRedirects: 5` - **THEN** 系统 SHALL 解析该字段并在执行时允许最多跟随 5 次重定向 #### Scenario: 最简 db 配置文件解析 - **WHEN** 系统读取只包含一个 `type: db` target(含 `id` 和 `db.url`)的 YAML 配置文件 - **THEN** 系统 SHALL 使用内置默认值填充未指定的字段(interval=30s, timeout=10s, group="default"),并保留 name=null、description=null #### Scenario: 最简 tcp 配置文件解析 - **WHEN** 系统读取只包含一个 `type: tcp` target(含 `id`、`tcp.host` 和 `tcp.port`)的 YAML 配置文件 - **THEN** 系统 SHALL 使用内置默认值填充未指定的字段(interval=30s, timeout=10s, group="default", tcp.readBanner=false, tcp.bannerReadTimeout=2000, tcp.maxBannerBytes=4096),并保留 name=null、description=null #### Scenario: defaults.tcp 配置 banner 默认值 - **WHEN** YAML 配置中 defaults.tcp 设置 `bannerReadTimeout` 和 `maxBannerBytes` - **THEN** 未显式覆盖对应字段的 tcp target SHALL 使用 defaults.tcp 中的值 #### Scenario: defaults.http.method 触发校验错误 - **WHEN** 配置文件中出现 `defaults.http.method` 字段 - **THEN** 系统 SHALL 以配置错误退出,提示 defaults.http 中存在未知字段 method #### Scenario: per-target http.method 仍然有效 - **WHEN** HTTP target 配置 `http.method: POST` - **THEN** 系统 SHALL 使用 POST 作为该 target 的请求方法 #### Scenario: 未配置 http.method 使用内置默认值 - **WHEN** HTTP target 未配置 `http.method` 且 defaults.http 中无 method 字段 - **THEN** 系统 SHALL 使用内置默认值 GET 作为该 target 的请求方法 ### Requirement: CLI 参数 系统 SHALL 通过单一命令行参数接受 YAML 配置文件路径。 #### Scenario: 指定配置文件启动 - **WHEN** 用户执行 `./dial-server ./probes.yaml` - **THEN** 系统 SHALL 读取并解析指定路径的 YAML 文件作为配置 #### Scenario: 未提供配置文件路径 - **WHEN** 用户启动程序时未提供任何命令行参数 - **THEN** 系统 SHALL 以错误退出并提示需要指定配置文件路径 #### Scenario: 配置文件不存在 - **WHEN** 用户指定的配置文件路径不存在 - **THEN** 系统 SHALL 以错误退出并提示文件不存在 ### Requirement: 配置校验 系统 SHALL 在启动时对 YAML 配置进行完整校验,校验失败时以非零状态退出并输出清晰的错误信息。系统 SHALL 使用 TypeBox 定义配置契约和 raw config TypeScript 类型,由 Ajv 校验 TypeBox 生成的 JSON Schema,再执行启动期语义 validator。配置加载流程 SHALL 明确区分 `RawProbeConfig`、`ValidatedProbeConfig`、`ResolvedConfig` 三段生命周期,并在 YAML 解析之后、AJV 校验之前执行变量替换阶段。JSON Schema 契约 SHALL 覆盖业务无关的结构规则,包括字段类型、必填字段、枚举、数组与对象形状、数值范围和未知字段。语义 validator SHALL 覆盖契约不适合表达的业务规则,包括 target id 唯一性、id 命名规则校验、checker type 注册状态、时长和大小解析、HTTP URL、正则可编译、JSONPath 子集和 XPath 可编译。 契约校验和语义 validator SHALL 统一产出 `ConfigValidationIssue`,最终由配置加载流程统一渲染为中文错误信息。 系统 SHALL 导出完整 `probe-config.schema.json`,该文件 SHALL 与运行期 TypeBox fragments 生成的 JSON Schema 保持一致,用于用户配置引用和编辑器提示。 除 `headers`、`env`、`variables` 等明确声明为动态键值表的对象外,配置中的未知字段 SHALL 导致启动期配置错误。系统 MUST NOT 静默忽略未知字段。 #### Scenario: target 缺少必填字段 - **WHEN** YAML 中某个 target 缺少 id 或 type 字段 - **THEN** 系统 SHALL 以错误退出,提示哪个 target 缺少哪个字段 #### Scenario: HTTP target 缺少 url - **WHEN** YAML 中某个 target 配置 `type: http` 但缺少 `http.url` - **THEN** 系统 SHALL 以错误退出,提示该 target 缺少 http.url 字段 #### Scenario: cmd target 缺少 exec - **WHEN** YAML 中某个 target 配置 `type: cmd` 但缺少 `cmd.exec` - **THEN** 系统 SHALL 以错误退出,提示该 target 缺少 cmd.exec 字段 #### Scenario: target type 非法 - **WHEN** YAML 中某个 target 的 type 不是已注册 checker 类型 - **THEN** 系统 SHALL 以错误退出,提示不支持的 target type 和当前支持的 type 列表 #### Scenario: target id 重复 - **WHEN** YAML 中存在两个 id 相同的 target - **THEN** 系统 SHALL 以错误退出,提示重复的 id #### Scenario: target id 不合法 - **WHEN** YAML 中某个 target 的 id 不符合 `[a-zA-Z0-9][a-zA-Z0-9_-]*` 规则 - **THEN** 系统 SHALL 以错误退出,提示 id 命名不合法 #### Scenario: group 字段类型校验 - **WHEN** YAML 中某个 target 的 `group` 字段不是字符串 - **THEN** 系统 SHALL 以错误退出并提示 group 字段类型错误 #### Scenario: interval 格式非法 - **WHEN** interval 或 timeout 值不是有效的时长格式(如 `30s`、`5m`、`500ms`) - **THEN** 系统 SHALL 以错误退出并提示格式错误 #### Scenario: maxConcurrentChecks 非法 - **WHEN** runtime.maxConcurrentChecks 不是正整数 - **THEN** 系统 SHALL 以错误退出并提示 runtime.maxConcurrentChecks 格式错误 #### Scenario: interval 或 timeout 解析结果非法 - **WHEN** interval 或 timeout 解析结果不是正整数毫秒(如 `0ms` 或 `1.5ms`) - **THEN** 系统 SHALL 以错误退出并提示必须为正整数毫秒 #### Scenario: size 格式非法 - **WHEN** maxBodyBytes 或 maxOutputBytes 值不是有效的 size 格式 - **THEN** 系统 SHALL 以错误退出并提示支持 B、KB、MB、GB 格式 #### Scenario: size 解析结果非法 - **WHEN** maxBodyBytes 或 maxOutputBytes 解析结果不是非负安全整数字节数(如 `1.5B`) - **THEN** 系统 SHALL 以错误退出并提示必须为非负安全整数字节数 #### Scenario: HTTP method 非法 - **WHEN** YAML 中某个 HTTP target 的 `http.method` 不是 GET、HEAD、POST、PUT、PATCH、DELETE、OPTIONS 之一 - **THEN** 系统 SHALL 以错误退出,提示该 target 的 method 不合法 #### Scenario: HTTP method 小写非法 - **WHEN** YAML 中某个 HTTP target 配置 `http.method: get` - **THEN** 系统 SHALL 以错误退出,提示该 target 的 method 必须为大写枚举值 #### Scenario: URL 格式非法 - **WHEN** YAML 中某个 HTTP target 的 `http.url` 不是合法 URL,或协议不是 `http:` / `https:` - **THEN** 系统 SHALL 以错误退出,提示该 target 的 URL 格式不合法 #### Scenario: maxRedirects 非法 - **WHEN** YAML 中某个 HTTP target 的 `http.maxRedirects` 为负数 - **THEN** 系统 SHALL 以错误退出,提示该 target 的 maxRedirects 必须为非负整数 #### Scenario: maxRedirects 非整数非法 - **WHEN** YAML 中某个 HTTP target 的 `http.maxRedirects` 不是非负整数(如 `1.5` 或 `"5"`) - **THEN** 系统 SHALL 以错误退出,提示该 target 的 maxRedirects 必须为非负整数 #### Scenario: ignoreSSL 类型非法 - **WHEN** YAML 中某个 HTTP target 的 `http.ignoreSSL` 不是布尔值 - **THEN** 系统 SHALL 以错误退出,提示该 target 的 ignoreSSL 必须为布尔值 #### Scenario: HTTP headers 类型非法 - **WHEN** YAML 中某个 HTTP target 的 `http.headers` 不是对象,或任一 header 值不是字符串 - **THEN** 系统 SHALL 以错误退出,提示该 target 的 http.headers 格式错误 #### Scenario: HTTP body 类型非法 - **WHEN** YAML 中某个 HTTP target 的 `http.body` 已配置但不是字符串 - **THEN** 系统 SHALL 以错误退出,提示该 target 的 http.body 必须为字符串 #### Scenario: maxBodyBytes 数字非法 - **WHEN** YAML 中某个 HTTP target 的 `http.maxBodyBytes` 或 defaults.http.maxBodyBytes 是负数、非整数或非安全整数 - **THEN** 系统 SHALL 以错误退出,提示 maxBodyBytes 必须为非负安全整数字节数或合法 size 字符串 #### Scenario: status 模式非法 - **WHEN** YAML 中某个 HTTP target 的 `expect.status` 包含不符合 `1xx` 到 `5xx` 格式的字符串(如 `"abc"`、`"2x"`、`"6xx"`) - **THEN** 系统 SHALL 以错误退出,提示该 target 的 status 模式不合法 #### Scenario: status 数字非法 - **WHEN** YAML 中某个 HTTP target 的 `expect.status` 包含非整数或不在 100-599 范围内的数字 - **THEN** 系统 SHALL 以错误退出,提示该 target 的 status 数字不合法 #### Scenario: maxDurationMs 非法 - **WHEN** YAML 中某个 target 的 `expect.maxDurationMs` 不是非负有限数字 - **THEN** 系统 SHALL 以错误退出,提示该 target 的 expect.maxDurationMs 格式错误 #### Scenario: HTTP expect headers 非法 - **WHEN** YAML 中某个 HTTP target 的 `expect.headers` 不是对象,或某个 header 期望既不是字符串也不是合法 operator - **THEN** 系统 SHALL 以错误退出,提示该 target 的 expect.headers 格式错误 #### Scenario: HTTP expect body 必须为数组 - **WHEN** YAML 中某个 HTTP target 的 `expect.body` 已配置但不是数组 - **THEN** 系统 SHALL 以错误退出,提示该 target 的 expect.body 必须为数组 #### Scenario: HTTP body rule 缺少支持字段 - **WHEN** YAML 中某个 HTTP target 的 `expect.body` 数组项未包含 contains、regex、json、css、xpath 任一支持字段 - **THEN** 系统 SHALL 以错误退出,提示该 body rule 缺少支持的规则类型 #### Scenario: HTTP body rule 同时配置多个支持字段 - **WHEN** YAML 中某个 HTTP target 的同一条 body rule 同时包含 contains、regex、json、css、xpath 中的多个支持字段 - **THEN** 系统 SHALL 以错误退出,提示每条 body rule 只能配置一种规则类型 #### Scenario: HTTP body regex 非法 - **WHEN** YAML 中某个 HTTP target 的 body regex 规则不是字符串或不是可编译正则表达式 - **THEN** 系统 SHALL 以错误退出,提示该 body regex 不合法 #### Scenario: HTTP body json path 非法 - **WHEN** YAML 中某个 HTTP target 的 body json 规则缺少 path,或 path 不符合系统支持的 JSONPath 子集 - **THEN** 系统 SHALL 以错误退出,提示该 body json path 不合法 #### Scenario: HTTP body css selector 非法 - **WHEN** YAML 中某个 HTTP target 的 body css 规则缺少 selector,或 selector 不是非空字符串 - **THEN** 系统 SHALL 以错误退出,提示该 body css selector 不合法 #### Scenario: HTTP body xpath path 非法 - **WHEN** YAML 中某个 HTTP target 的 body xpath 规则缺少 path,或 path 不是非空字符串,或可被现有 XPath 库静态判定为语法错误 - **THEN** 系统 SHALL 以错误退出,提示该 body xpath path 不合法 #### Scenario: expect operator 类型非法 - **WHEN** YAML 中某个 HTTP expect operator 的 match 不是可编译正则字符串,empty/exists 不是布尔值,或 gt/gte/lt/lte 不是有限数字 - **THEN** 系统 SHALL 以错误退出,提示对应 operator 配置不合法 #### Scenario: expect operator 类型非法 - **WHEN** YAML 中某个 expect operator 的 match 不是可编译正则字符串,empty/exists 不是布尔值,或 gt/gte/lt/lte 不是有限数字 - **THEN** 系统 SHALL 以错误退出,提示对应 operator 配置不合法 #### Scenario: unknown 字段失败 - **WHEN** YAML 中任一结构化配置对象包含契约未声明的字段,且该对象不是明确允许动态键的对象 - **THEN** 系统 SHALL 以错误退出,提示未知字段所在路径 #### Scenario: 动态 headers 字段允许 - **WHEN** YAML 中 `http.headers`、`defaults.http.headers` 或 `expect.headers` 包含任意 header 名称,且对应值符合契约 - **THEN** 系统 SHALL 接受这些动态 header 名称 #### Scenario: 动态 env 字段允许 - **WHEN** YAML 中 `cmd.env` 包含任意环境变量名称,且对应值为字符串 - **THEN** 系统 SHALL 接受这些动态 env 名称 #### Scenario: JSON Schema 不修改输入 - **WHEN** 系统执行 JSON Schema 契约校验 - **THEN** 系统 MUST NOT 通过契约校验器强制转换类型、注入默认值或删除未知字段 #### Scenario: 配置生命周期分离 - **WHEN** 系统加载配置文件 - **THEN** 系统 SHALL 按 `unknown -> 变量替换 -> RawProbeConfig -> ValidatedProbeConfig -> ResolvedConfig` 的顺序执行变量替换、契约校验、语义校验和运行期配置解析 #### Scenario: 结构化校验 issue - **WHEN** 契约校验、语义 validator 或变量替换阶段发现非法配置 - **THEN** 系统 SHALL 先生成包含 code、path、message 和可选 targetName 的结构化 `ConfigValidationIssue`,再统一渲染为中文错误信息 #### Scenario: 导出配置 JSON Schema - **WHEN** 仓库生成或检查配置契约 - **THEN** 根目录 SHALL 存在 draft-07 `probe-config.schema.json`,且其内容 SHALL 与当前公共 fragments 和已注册 checker fragments 组装出的完整 schema 一致(包含 variables 段和 target 的 id/name 字段) 系统 SHALL 支持使用单位字符串配置读取上限,单位包括 `B`、`KB`、`MB` 和 `GB`。 #### Scenario: 解析 MB - **WHEN** YAML 中配置 `maxBodyBytes: "100MB"` - **THEN** 系统 SHALL 将其解析为 104857600 bytes #### Scenario: 解析 KB - **WHEN** YAML 中配置 `maxOutputBytes: "512KB"` - **THEN** 系统 SHALL 将其解析为 524288 bytes ### Requirement: runtime 并发配置 系统 SHALL 支持 `runtime.maxConcurrentChecks` 配置全局最大并发检查数。 #### Scenario: 使用默认并发限制 - **WHEN** YAML 中未配置 runtime.maxConcurrentChecks - **THEN** 系统 SHALL 使用默认值 20 #### Scenario: 配置并发限制 - **WHEN** YAML 中配置 `runtime.maxConcurrentChecks: 5` - **THEN** 系统 SHALL 将全局最大并发检查数设置为 5 ### Requirement: YAML 配置使用 Bun 内置解析 系统 SHALL 使用 Bun 内置的 `Bun.YAML.parse()` 解析配置文件,不引入外部 YAML 解析库。 #### Scenario: 解析 YAML 内容 - **WHEN** 系统读取 YAML 文件内容 - **THEN** 系统 SHALL 调用 `Bun.YAML.parse()` 将内容解析为配置对象 ### Requirement: expect 配置增强 系统 SHALL 支持 typed target 的领域专用 expect 配置,包括 HTTP 的 `status`(支持精确数字和范围模式)、`headers`、`body` 和 cmd 的 `exitCode`、`stdout`、`stderr`。内容类 expect MUST 使用数组表达配置顺序。 #### Scenario: 解析 HTTP expect 配置 - **WHEN** YAML 配置文件中 HTTP target 的 expect 包含 status、headers、body 规则数组及内部方法 - **THEN** 系统 SHALL 正确解析并存储为 HTTP target 的 expect 字段 #### Scenario: 解析 cmd expect 配置 - **WHEN** YAML 配置文件中 cmd target 的 expect 包含 exitCode、stdout 和 stderr 规则数组 - **THEN** 系统 SHALL 正确解析并存储为 cmd target 的 expect 字段 #### Scenario: 解析 body 有序规则数组 - **WHEN** YAML 中 HTTP target 配置 `expect.body` 为 contains、json、regex 三个数组项 - **THEN** 系统 SHALL 保留数组顺序,供执行阶段按配置顺序快速失败 #### Scenario: 不配置 HTTP status - **WHEN** HTTP target 未配置 `expect.status` - **THEN** 系统 SHALL 在执行 expect 时使用默认 `status: [200]` 语义 #### Scenario: 配置 HTTP status 范围模式 - **WHEN** HTTP target 配置 `expect.status: ["2xx"]` - **THEN** 系统 SHALL 在执行 expect 时匹配所有 200-299 状态码 #### Scenario: 配置 HTTP status 混合模式 - **WHEN** HTTP target 配置 `expect.status: ["2xx", 301]` - **THEN** 系统 SHALL 在执行 expect 时匹配所有 200-299 状态码或精确匹配 301 #### Scenario: 不配置 cmd exitCode - **WHEN** cmd target 未配置 `expect.exitCode` - **THEN** 系统 SHALL 在执行 expect 时使用默认 `exitCode: [0]` 语义 #### Scenario: 不配置 expect - **WHEN** target 未配置任何 expect 规则 - **THEN** 系统 SHALL 正常处理,expect 字段为 undefined ### Requirement: 数据保留配置字段 配置 schema 的 `runtime` 段 SHALL 支持 `retention` 字段,类型为字符串,格式为 `<数字><单位>`(单位:`d` 天、`h` 小时、`m` 分钟),用于指定历史数据保留时长。 #### Scenario: retention 字段校验通过 - **WHEN** 配置文件中 `runtime.retention` 为合法格式(如 `"7d"`、`"24h"`、`"30m"`) - **THEN** 配置校验 SHALL 通过 #### Scenario: retention 字段格式非法 - **WHEN** 配置文件中 `runtime.retention` 为非法格式(如 `"abc"`、`"7x"`、`""`) - **THEN** 配置校验 SHALL 失败并报告格式错误 #### Scenario: retention 字段缺省 - **WHEN** 配置文件中未指定 `runtime.retention` - **THEN** 系统 SHALL 使用默认值 `"7d"` ### Requirement: 数据目录路径解析 配置加载流程 SHALL 将 `server.dataDir` 相对路径基于配置文件所在目录(configDir)解析为绝对路径。绝对路径 SHALL 保持不变。 #### Scenario: dataDir 为相对路径 - **WHEN** 配置文件位于 `/opt/dial/probes.yaml`,且 `server.dataDir` 配置为 `./data` - **THEN** 系统 SHALL 将 dataDir 解析为 `/opt/dial/data`,而非依赖进程 cwd #### Scenario: dataDir 为绝对路径 - **WHEN** `server.dataDir` 配置为 `/var/lib/dial/data` - **THEN** 系统 SHALL 直接使用该绝对路径,不做额外解析 #### Scenario: dataDir 使用默认值 - **WHEN** 未配置 `server.dataDir`(使用默认值 `./data`) - **THEN** 系统 SHALL 将默认值 `./data` 基于 configDir 解析为绝对路径 ### Requirement: target 通用元信息字段约束 系统 SHALL 在 YAML target 通用字段中支持 `description` 字段,并对 `id`、`name` 和 `description` 执行契约校验。`id` MUST 为 1 到 30 个字符。`name` MUST 为 null 或 1 到 30 个字符的字符串,且语义校验 SHALL 拒绝仅包含空白字符的 name。`description` MUST 为 null 或不超过 500 个字符的字符串,且 MAY 为空字符串。 #### Scenario: description 字段解析 - **WHEN** 系统读取包含 `description: "检查生产 API 健康状态"` 的 target - **THEN** 系统 SHALL 将该字段解析为 target 的目标说明 #### Scenario: name 为 null 通过校验 - **WHEN** 系统读取包含 `name: null` 或省略 `name` 的 target - **THEN** 系统 SHALL 接受该配置 #### Scenario: name 仅包含空白字符报错 - **WHEN** 系统读取包含 `name: " "` 的 target - **THEN** 系统 SHALL 以配置错误退出,提示 name 不能为空白 #### Scenario: description 为空字符串 - **WHEN** 系统读取包含 `description: ""` 的 target - **THEN** 系统 SHALL 接受该配置,且不触发长度错误 #### Scenario: description 为 null 通过校验 - **WHEN** 系统读取包含 `description: null` 或省略 `description` 的 target - **THEN** 系统 SHALL 接受该配置 #### Scenario: description 类型非法 - **WHEN** YAML 中某个 target 的 `description` 字段不是字符串也不是 null - **THEN** 系统 SHALL 以错误退出,提示 description 字段类型错误 #### Scenario: description 超过最大长度 - **WHEN** YAML 中某个 target 的 `description` 字段超过 500 个字符 - **THEN** 系统 SHALL 以错误退出,提示 description 字段长度错误 #### Scenario: id 超过最大长度 - **WHEN** YAML 中某个 target 的 `id` 字段超过 30 个字符 - **THEN** 系统 SHALL 以错误退出,提示 id 字段长度错误 #### Scenario: name 超过最大长度 - **WHEN** YAML 中某个 target 的 `name` 字段超过 30 个字符 - **THEN** 系统 SHALL 以错误退出,提示 name 字段长度错误 #### Scenario: 变量替换后 description 超长 - **WHEN** target 的 `description` 通过变量替换后超过 500 个字符 - **THEN** 系统 SHALL 在契约校验阶段以错误退出,提示 description 字段长度错误 ### Requirement: 配置 schema 导出包含 target 元信息约束 系统 SHALL 在导出的 `probe-config.schema.json` 中包含 target `id`、`name` 和 `description` 的长度约束和可空类型,用于编辑器提示和外部校验。 #### Scenario: schema 导出 description - **WHEN** 系统导出 `probe-config.schema.json` - **THEN** target schema SHALL 包含可选的 `description` 字段,类型为 string 或 null,字符串最大长度为 500 #### Scenario: schema 导出 id 和 name - **WHEN** 系统导出 `probe-config.schema.json` - **THEN** target schema SHALL 声明 `id` 的 minLength 为 1、maxLength 为 30,并声明 `name` 为可选字段,类型为 string 或 null,字符串的 minLength 为 1、maxLength 为 30 ### Requirement: TCP 配置校验 系统 SHALL 在启动期对 tcp checker 的配置契约和语义执行严格校验。Tcp target 的 `tcp` 分组 SHALL 只允许 `host`、`port`、`readBanner`、`bannerReadTimeout` 和 `maxBannerBytes` 字段;Tcp expect SHALL 只允许 `connected`、`maxDurationMs` 和 `banner` 字段。未知字段、非法类型、非法端口、非法 size 和不可编译正则 MUST 导致启动期配置错误。 #### Scenario: tcp host 类型非法 - **WHEN** YAML 中 tcp target 的 `tcp.host` 不是非空字符串 - **THEN** 系统 SHALL 以配置错误退出,提示 tcp.host 必须为非空字符串 #### Scenario: tcp port 类型非法 - **WHEN** YAML 中 tcp target 的 `tcp.port` 不是整数 - **THEN** 系统 SHALL 以配置错误退出,提示 tcp.port 必须为整数端口 #### Scenario: tcp readBanner 类型非法 - **WHEN** YAML 中 tcp target 的 `tcp.readBanner` 不是布尔值 - **THEN** 系统 SHALL 以配置错误退出,提示 tcp.readBanner 必须为布尔值 #### Scenario: tcp bannerReadTimeout 非法 - **WHEN** YAML 中 tcp target 或 defaults.tcp 的 `bannerReadTimeout` 不是非负有限数字 - **THEN** 系统 SHALL 以配置错误退出,提示 bannerReadTimeout 格式错误 #### Scenario: tcp maxBannerBytes 非法 - **WHEN** YAML 中 tcp target 或 defaults.tcp 的 `maxBannerBytes` 不是合法 size 值 - **THEN** 系统 SHALL 以配置错误退出,提示 maxBannerBytes 格式错误 #### Scenario: tcp expect connected 类型非法 - **WHEN** YAML 中 tcp target 的 `expect.connected` 不是布尔值 - **THEN** 系统 SHALL 以配置错误退出,提示 expect.connected 必须为布尔值 #### Scenario: tcp expect banner 非法 - **WHEN** YAML 中 tcp target 的 `expect.banner` 不是合法 operator 对象 - **THEN** 系统 SHALL 以配置错误退出,提示 expect.banner 格式错误 #### Scenario: tcp expect banner match 正则非法 - **WHEN** YAML 中 tcp target 配置 `expect.banner: { match: "[invalid" }` - **THEN** 系统 SHALL 在启动期配置校验失败,而不是延迟到运行期抛错 #### Scenario: tcp 分组未知字段失败 - **WHEN** YAML 中 tcp target 的 `tcp` 分组包含 `tls: true` 等未知字段 - **THEN** 系统 SHALL 以配置错误退出,提示 tcp 分组包含未知字段 #### Scenario: defaults.tcp 未知字段失败 - **WHEN** YAML 中 defaults.tcp 包含 `host` 或其他非默认字段 - **THEN** 系统 SHALL 以配置错误退出,提示 defaults.tcp 包含未知字段