feat: 扩展配置变量替换范围至 server/probes/targets,支持空默认值语法
This commit is contained in:
@@ -37,10 +37,10 @@
|
||||
|
||||
#### Scenario: 不定义 variables 段
|
||||
- **WHEN** 配置文件不包含 variables 段
|
||||
- **THEN** 系统 SHALL 正常启动,targets 中的 `${...}` 引用仅从环境变量查找
|
||||
- **THEN** 系统 SHALL 正常启动,支持变量替换的配置字段中的 `${...}` 引用仅从环境变量和表达式默认值查找
|
||||
|
||||
### Requirement: 变量引用语法
|
||||
targets 中的字符串值 SHALL 支持 `${key}` 语法引用变量。系统 SHALL 支持 `${key|default}` 语法设置默认值,其中第一个 `|` 为分隔符,后续 `|` 属于默认值内容。系统 SHALL 支持 `$${...}` 转义语法输出字面量 `${...}`。
|
||||
支持变量替换的字符串值 SHALL 支持 `${key}` 语法引用变量。系统 SHALL 支持 `${key|default}` 语法设置默认值,其中第一个 `|` 为分隔符,后续 `|` 属于默认值内容。系统 SHALL 支持 `${key|}` 显式设置空字符串默认值。系统 SHALL 支持 `$${...}` 转义语法输出字面量 `${...}`。
|
||||
|
||||
#### Scenario: 简单变量引用
|
||||
- **WHEN** target 字段值为 `"${base_url}/health"` 且 variables 中定义 `base_url: "https://api.example.com"`
|
||||
@@ -54,6 +54,10 @@ targets 中的字符串值 SHALL 支持 `${key}` 语法引用变量。系统 SHA
|
||||
- **WHEN** target 字段值为 `"${PATTERN|foo|bar}"` 且变量不存在
|
||||
- **THEN** 系统 SHALL 使用 `"foo|bar"` 作为默认值(第一个 `|` 为分隔符)
|
||||
|
||||
#### Scenario: 空默认值
|
||||
- **WHEN** 支持变量替换的字段值为 `"${OPTIONAL_VALUE|}"` 且 variables 和环境变量中均不存在 OPTIONAL_VALUE
|
||||
- **THEN** 系统 SHALL 使用空字符串 `""` 作为默认值且不报 unresolved-variable 错误
|
||||
|
||||
#### Scenario: 转义语法
|
||||
- **WHEN** target 字段值为 `"Hello $${name}"`
|
||||
- **THEN** 系统 SHALL 输出 `"Hello ${name}"`,不进行变量替换
|
||||
@@ -82,11 +86,11 @@ targets 中的字符串值 SHALL 支持 `${key}` 语法引用变量。系统 SHA
|
||||
- **THEN** 系统 SHALL 使用默认值(类型推断后为 number 60)
|
||||
|
||||
#### Scenario: 变量未定义且无默认值报错
|
||||
- **WHEN** target 字段引用 `${MISSING_VAR}` 且 variables、环境变量中均不存在,也未设置默认值
|
||||
- **WHEN** 支持变量替换的字段引用 `${MISSING_VAR}` 且 variables、环境变量中均不存在,也未设置默认值
|
||||
- **THEN** 系统 SHALL 以配置错误退出,提示该变量未定义
|
||||
|
||||
### Requirement: 完整引用类型保留
|
||||
当字段值仅包含单个变量引用(完整引用)时,系统 SHALL 保留变量的原始类型。完整引用的判定为:字段值去掉首尾空白后严格匹配单个 `${key}` 或 `${key|default}` 模式且无其他字符。
|
||||
当字段值仅包含单个变量引用(完整引用)时,系统 SHALL 保留变量的原始类型。完整引用的判定为:字段值去掉首尾空白后严格匹配单个 `${key}` 或 `${key|default}` 模式且无其他字符。环境变量和表达式默认值的完整引用 SHALL 做 number/boolean 类型推断,但空字符串 MUST 保持为 string `""`。
|
||||
|
||||
#### Scenario: 完整引用 number 变量
|
||||
- **WHEN** target 字段值为 `"${port}"` 且 variables 中 `port: 5432`
|
||||
@@ -120,8 +124,16 @@ targets 中的字符串值 SHALL 支持 `${key}` 语法引用变量。系统 SHA
|
||||
- **WHEN** target 字段值为 `"${HOST|localhost}"` 且变量不存在
|
||||
- **THEN** 系统 SHALL 将该字段替换为 string 类型的 "localhost"
|
||||
|
||||
#### Scenario: 空默认值保持字符串
|
||||
- **WHEN** 支持变量替换的字段值为 `"${OPTIONAL_VALUE|}"` 且变量不存在
|
||||
- **THEN** 系统 SHALL 将该字段替换为 string 类型的空字符串 `""`,MUST NOT 推断为 number 0
|
||||
|
||||
#### Scenario: 空环境变量保持字符串
|
||||
- **WHEN** 支持变量替换的字段值为 `"${EMPTY_ENV}"` 且环境变量 `EMPTY_ENV` 存在但值为空字符串
|
||||
- **THEN** 系统 SHALL 将该字段替换为 string 类型的空字符串 `""`,MUST NOT 推断为 number 0
|
||||
|
||||
### Requirement: 替换范围限制
|
||||
变量替换 SHALL 仅作用于 targets 段。`id` 和 `type` 字段 MUST NOT 参与变量替换。`server`、`probes` 段 MUST NOT 参与变量替换。系统 SHALL 递归遍历 target 对象树中所有字符串值进行替换(包括嵌套对象和数组元素中的字符串)。顶层 `defaults` 不再是合法配置段,因此不属于变量替换范围。
|
||||
变量替换 SHALL 作用于 `server`、`probes` 和 `targets` 段中的字符串值。`variables` 段自身 MUST NOT 参与变量替换。系统 SHALL 递归遍历支持范围内对象树中所有字符串 value 进行替换,包括嵌套对象和数组元素中的字符串。系统 MUST NOT 替换对象 key。`targets[].id` 和 `targets[].type` 字段 MUST NOT 参与变量替换;target 内部其他路径上名为 `id` 或 `type` 的字段 SHALL 正常参与变量替换。顶层 `defaults` 不再是合法配置段,因此不属于变量替换范围。
|
||||
|
||||
#### Scenario: target 嵌套对象中的变量替换
|
||||
- **WHEN** target 配置 `http.headers.Authorization: "${token}"` 且 variables 中定义 `token: "Bearer abc"`
|
||||
@@ -131,44 +143,76 @@ targets 中的字符串值 SHALL 支持 `${key}` 语法引用变量。系统 SHA
|
||||
- **WHEN** target 配置 `cmd.args: ["--host", "${host}"]` 且 variables 中定义 `host: "localhost"`
|
||||
- **THEN** 系统 SHALL 将数组第二个元素替换为 "localhost"
|
||||
|
||||
#### Scenario: id 字段不替换
|
||||
#### Scenario: server 段变量替换
|
||||
- **WHEN** server 配置 `listen.host: "${server_host}"` 且 variables 中定义 `server_host: "0.0.0.0"`
|
||||
- **THEN** 系统 SHALL 将 server.listen.host 替换为 "0.0.0.0"
|
||||
|
||||
#### Scenario: probes 段变量替换
|
||||
- **WHEN** probes 配置 `execution.maxConcurrentChecks: "${max_checks}"` 且 variables 中定义 `max_checks: 5`
|
||||
- **THEN** 系统 SHALL 将 probes.execution.maxConcurrentChecks 替换为 number 5
|
||||
|
||||
#### Scenario: variables 段不替换
|
||||
- **WHEN** variables 配置 `host: "${HOST}"` 且环境变量 HOST 存在
|
||||
- **THEN** 系统 SHALL 保持 variables.host 为字面量 `"${HOST}"`,不进行替换
|
||||
|
||||
#### Scenario: target id 字段不替换
|
||||
- **WHEN** target 配置 `id: "${my_id}"` 且 variables 中定义 `my_id: "test"`
|
||||
- **THEN** 系统 SHALL 保持 id 字段值为字面量 `"${my_id}"`,不进行替换
|
||||
|
||||
#### Scenario: type 字段不替换
|
||||
#### Scenario: target type 字段不替换
|
||||
- **WHEN** target 配置 `type: "${checker_type}"` 且 variables 中定义 `checker_type: "http"`
|
||||
- **THEN** 系统 SHALL 保持 type 字段值为字面量 `"${checker_type}"`,不进行替换
|
||||
|
||||
#### Scenario: server 段不替换
|
||||
- **WHEN** server 配置 `listen.host: "${server_host}"` 且 variables 中定义 `server_host: "0.0.0.0"`
|
||||
- **THEN** 系统 SHALL 保持 server.listen.host 为字面量 `"${server_host}"`,不进行替换
|
||||
#### Scenario: target 嵌套 id 字段参与替换
|
||||
- **WHEN** target 内部非顶层身份字段配置 `expect.rows[0].id.equals: "${expected_id}"` 且 variables 中定义 `expected_id: 1`
|
||||
- **THEN** 系统 SHALL 将该嵌套 id 字段替换为 number 1
|
||||
|
||||
#### Scenario: probes 段不替换
|
||||
- **WHEN** probes 配置 `execution.maxConcurrentChecks: "${max_checks}"` 且 variables 中定义 `max_checks: 5`
|
||||
- **THEN** 系统 SHALL 保持 probes.execution.maxConcurrentChecks 为字面量 `"${max_checks}"`,不进行替换
|
||||
#### Scenario: target 嵌套 type 字段参与替换
|
||||
- **WHEN** target 内部非顶层身份字段配置 `expect.body[0].json.path: "$.${field_type}"` 且 variables 中定义 `field_type: "type"`
|
||||
- **THEN** 系统 SHALL 对该嵌套字段执行变量替换
|
||||
|
||||
#### Scenario: 对象 key 不替换
|
||||
- **WHEN** target 配置 `http.headers."${HEADER_NAME}": "demo"` 且 variables 中定义 `HEADER_NAME: "X-Demo"`
|
||||
- **THEN** 系统 SHALL 保持 header key 为字面量 `"${HEADER_NAME}"`,不进行替换
|
||||
|
||||
#### Scenario: defaults 段被拒绝
|
||||
- **WHEN** 配置文件声明顶层 `defaults`
|
||||
- **THEN** 系统 SHALL 在契约校验阶段拒绝该未知字段,而不是尝试变量替换
|
||||
|
||||
### Requirement: 变量替换错误报告
|
||||
变量替换阶段的错误 SHALL 作为 `ConfigValidationIssue` 输出,code 为 `unresolved-variable`。错误信息 SHALL 包含 target 索引、target id、字段路径和变量名。
|
||||
变量替换阶段的错误 SHALL 作为 `ConfigValidationIssue` 输出,code 为 `unresolved-variable`。错误信息 SHALL 包含字段路径和变量名。对于 `targets[i]` 内的错误,错误信息还 SHALL 包含 target 索引、target id 和 target 展示名上下文。
|
||||
|
||||
#### Scenario: 单个变量缺失报错
|
||||
#### Scenario: 单个 target 变量缺失报错
|
||||
- **WHEN** targets[0] (id: "api-health") 的 http.url 引用 `${base_url}` 但变量不存在
|
||||
- **THEN** 系统 SHALL 输出包含 target 索引 0、id "api-health"、路径 "http.url"、变量名 "base_url" 的错误信息
|
||||
|
||||
#### Scenario: server 变量缺失报错
|
||||
- **WHEN** server.listen.host 引用 `${server_host}` 但变量不存在
|
||||
- **THEN** 系统 SHALL 输出包含路径 "server.listen.host" 和变量名 "server_host" 的错误信息
|
||||
|
||||
#### Scenario: probes 变量缺失报错
|
||||
- **WHEN** probes.execution.maxConcurrentChecks 引用 `${max_checks}` 但变量不存在
|
||||
- **THEN** 系统 SHALL 输出包含路径 "probes.execution.maxConcurrentChecks" 和变量名 "max_checks" 的错误信息
|
||||
|
||||
#### Scenario: 多个变量缺失批量报错
|
||||
- **WHEN** 多个 target 的多个字段引用了不存在的变量
|
||||
- **WHEN** 多个配置字段引用了不存在的变量
|
||||
- **THEN** 系统 SHALL 收集所有缺失变量错误后统一输出,而非遇到第一个就退出
|
||||
|
||||
### Requirement: 变量替换执行时机
|
||||
变量替换 SHALL 在 YAML 解析之后、schema 契约校验(AJV)之前执行。替换完成后的配置对象 SHALL 传入后续校验流程。
|
||||
|
||||
#### Scenario: 替换后通过 schema 校验
|
||||
#### Scenario: target 替换后通过 schema 校验
|
||||
- **WHEN** target 配置 `http.maxRedirects: "${MAX_REDIRECTS}"` 且环境变量 `MAX_REDIRECTS=5`
|
||||
- **THEN** 系统 SHALL 先将该字段替换为 number 5,再进入 AJV 校验(期望 integer),校验通过
|
||||
|
||||
#### Scenario: 替换后未通过 schema 校验
|
||||
#### Scenario: target 替换后未通过 schema 校验
|
||||
- **WHEN** target 配置 `http.maxRedirects: "${MAX_REDIRECTS}"` 且环境变量 `MAX_REDIRECTS=abc`
|
||||
- **THEN** 系统 SHALL 先将该字段替换为 string "abc",再进入 AJV 校验(期望 integer),校验失败并报错
|
||||
|
||||
#### Scenario: server 替换后通过 schema 校验
|
||||
- **WHEN** server 配置 `listen.port: "${SERVER_PORT}"` 且 variables 中定义 `SERVER_PORT: 3000`
|
||||
- **THEN** 系统 SHALL 先将该字段替换为 number 3000,再进入 AJV 校验(期望 integer),校验通过
|
||||
|
||||
#### Scenario: probes 替换后通过 schema 校验
|
||||
- **WHEN** probes 配置 `execution.maxConcurrentChecks: "${MAX_CHECKS}"` 且 variables 中定义 `MAX_CHECKS: 20`
|
||||
- **THEN** 系统 SHALL 先将该字段替换为 number 20,再进入 AJV 校验(期望 integer),校验通过
|
||||
|
||||
Reference in New Issue
Block a user