13 KiB
Purpose
定义配置文件的变量定义、引用、解析和替换机制,支持集中管理共享值和环境变量注入。
Requirements
Requirement: variables 段定义
配置文件 SHALL 支持可选的顶层 variables 段,用于定义变量键值对。variables 的 key SHALL 符合 [a-zA-Z_][a-zA-Z0-9_]* 命名规则。variables 的 value SHALL 仅支持 string、number、boolean 三种类型,MUST NOT 支持 null、array、object。variables 段自身 MUST NOT 支持引用其他变量或环境变量(值为纯字面量)。
Scenario: 定义字符串变量
- WHEN 配置文件包含
variables: { base_url: "https://api.example.com" } - THEN 系统 SHALL 解析 base_url 为字符串类型变量,值为 "https://api.example.com"
Scenario: 定义数字变量
- WHEN 配置文件包含
variables: { port: 5432 } - THEN 系统 SHALL 解析 port 为 number 类型变量,值为 5432
Scenario: 定义布尔变量
- WHEN 配置文件包含
variables: { ssl_enabled: true } - THEN 系统 SHALL 解析 ssl_enabled 为 boolean 类型变量,值为 true
Scenario: 变量值为 null 报错
- WHEN 配置文件包含
variables: { empty: null } - THEN 系统 SHALL 以配置错误退出,提示 variables 的值不允许为 null
Scenario: 变量值为数组报错
- WHEN 配置文件包含
variables: { list: [1, 2, 3] } - THEN 系统 SHALL 以配置错误退出,提示 variables 的值不允许为 array
Scenario: 变量值为对象报错
- WHEN 配置文件包含
variables: { obj: { a: 1 } } - THEN 系统 SHALL 以配置错误退出,提示 variables 的值不允许为 object
Scenario: 变量 key 不合法报错
- WHEN 配置文件包含
variables: { "123start": "value" } - THEN 系统 SHALL 以配置错误退出,提示变量名不符合命名规则
Scenario: 不定义 variables 段
- WHEN 配置文件不包含 variables 段
- THEN 系统 SHALL 正常启动,支持变量替换的配置字段中的
${...}引用仅从环境变量和表达式默认值查找
Requirement: 变量引用语法
支持变量替换的字符串值 SHALL 支持 ${key} 语法引用变量。系统 SHALL 支持 ${key|default} 语法设置默认值,其中第一个 | 为分隔符,后续 | 属于默认值内容。系统 SHALL 支持 ${key|} 显式设置空字符串默认值。系统 SHALL 支持 $${...} 转义语法输出字面量 ${...}。
Scenario: 简单变量引用
- WHEN target 字段值为
"${base_url}/health"且 variables 中定义base_url: "https://api.example.com" - THEN 系统 SHALL 将该字段替换为
"https://api.example.com/health"
Scenario: 带默认值的变量引用
- WHEN target 字段值为
"${DB_PORT|5432}"且 variables 和环境变量中均不存在 DB_PORT - THEN 系统 SHALL 将该字段替换为使用默认值(类型推断后为 number 5432)
Scenario: 默认值包含管道符
- 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}",不进行变量替换
Scenario: 多个变量引用
- WHEN target 字段值为
"${protocol}://${host}:${port}/api" - THEN 系统 SHALL 逐个解析并替换所有变量引用,结果为拼接后的字符串
Scenario: 无变量引用的字符串
- WHEN target 字段值为
"https://example.com"且不含${...}模式 - THEN 系统 SHALL 保持原样,不做任何处理
Requirement: 变量解析优先级
系统 SHALL 按以下优先级解析变量引用:variables 定义 → 环境变量 → 默认值。如果三者均不存在,系统 SHALL 以配置错误退出。
Scenario: variables 优先于环境变量
- WHEN variables 中定义
port: 5432且环境变量port=3000也存在 - THEN 系统 SHALL 使用 variables 中的值 5432
Scenario: 环境变量作为 fallback
- WHEN variables 中未定义
DB_HOST但环境变量DB_HOST=localhost存在 - THEN 系统 SHALL 使用环境变量的值 "localhost"
Scenario: 默认值作为最终 fallback
- WHEN variables 和环境变量中均不存在
CACHE_TTL,且引用为${CACHE_TTL|60} - THEN 系统 SHALL 使用默认值(类型推断后为 number 60)
Scenario: 变量未定义且无默认值报错
- WHEN 支持变量替换的字段引用
${MISSING_VAR}且 variables、环境变量中均不存在,也未设置默认值 - THEN 系统 SHALL 以配置错误退出,提示该变量未定义
Requirement: 完整引用类型保留
当字段值仅包含单个变量引用(完整引用)时,系统 SHALL 保留变量的原始类型。完整引用的判定为:字段值去掉首尾空白后严格匹配单个 ${key} 或 ${key|default} 模式且无其他字符。环境变量和表达式默认值的完整引用 SHALL 做 number/boolean 类型推断,但空字符串 MUST 保持为 string ""。
Scenario: 完整引用 number 变量
- WHEN target 字段值为
"${port}"且 variables 中port: 5432 - THEN 系统 SHALL 将该字段替换为 number 类型的 5432
Scenario: 完整引用 boolean 变量
- WHEN target 字段值为
"${ssl}"且 variables 中ssl: true - THEN 系统 SHALL 将该字段替换为 boolean 类型的 true
Scenario: 完整引用 string 变量
- WHEN target 字段值为
"${host}"且 variables 中host: "example.com" - THEN 系统 SHALL 将该字段替换为 string 类型的 "example.com"
Scenario: 部分引用强制为字符串
- WHEN target 字段值为
"port: ${port}"且 variables 中port: 5432 - THEN 系统 SHALL 将该字段替换为 string 类型的 "port: 5432"
Scenario: 环境变量完整引用类型推断
- WHEN target 字段值为
"${MAX_REDIRECTS}"且环境变量MAX_REDIRECTS=5 - THEN 系统 SHALL 将该字段替换为 number 类型的 5(类型推断)
Scenario: 默认值完整引用类型推断
- WHEN target 字段值为
"${TIMEOUT|30}"且变量不存在 - THEN 系统 SHALL 将该字段替换为 number 类型的 30(类型推断)
Scenario: 默认值推断为 boolean
- WHEN target 字段值为
"${IGNORE_SSL|false}"且变量不存在 - THEN 系统 SHALL 将该字段替换为 boolean 类型的 false
Scenario: 默认值无法推断保持字符串
- 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 作用于 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" - THEN 系统 SHALL 将该 header 值替换为 "Bearer abc"
Scenario: target 数组元素中的变量替换
- WHEN target 配置
cmd.args: ["--host", "${host}"]且 variables 中定义host: "localhost" - THEN 系统 SHALL 将数组第二个元素替换为 "localhost"
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: target type 字段不替换
- WHEN target 配置
type: "${checker_type}"且 variables 中定义checker_type: "http" - THEN 系统 SHALL 保持 type 字段值为字面量
"${checker_type}",不进行替换
Scenario: target 嵌套 id 字段参与替换
- WHEN target 内部非顶层身份字段配置
expect.rows[0].id.equals: "${expected_id}"且 variables 中定义expected_id: 1 - THEN 系统 SHALL 将该嵌套 id 字段替换为 number 1
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 包含字段路径和变量名。对于 targets[i] 内的错误,错误信息还 SHALL 包含 target 索引、target id 和 target 展示名上下文。
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 多个配置字段引用了不存在的变量
- THEN 系统 SHALL 收集所有缺失变量错误后统一输出,而非遇到第一个就退出
Requirement: 变量替换执行时机
变量替换 SHALL 在 YAML 解析之后、schema 契约校验(AJV)之前执行。替换完成后的配置对象 SHALL 传入后续校验流程。
Scenario: target 替换后通过 schema 校验
- WHEN target 配置
http.maxRedirects: "${MAX_REDIRECTS}"且环境变量MAX_REDIRECTS=5 - THEN 系统 SHALL 先将该字段替换为 number 5,再进入 AJV 校验(期望 integer),校验通过
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),校验通过