73 lines
2.8 KiB
Python
73 lines
2.8 KiB
Python
# -*- coding: utf-8 -*-
|
||
"""
|
||
配置优先级属性测试
|
||
|
||
**Validates: Requirements 4.3**
|
||
|
||
Property 3: 配置优先级 - .env.local 覆盖
|
||
对于任意配置项名称和两个不同的值,当根 .env 和应用 .env.local
|
||
都定义了该配置项时,配置加载器返回的值应等于 .env.local 中的值。
|
||
|
||
测试逻辑:
|
||
1. 使用 hypothesis 生成随机配置项名称(字母数字下划线)和两个不同的值
|
||
2. 创建临时 .env 文件,写入 KEY=value1
|
||
3. 创建临时 .env.local 文件,写入 KEY=value2
|
||
4. 使用 python-dotenv 模拟分层加载(先加载 .env,再加载 .env.local,override=True)
|
||
5. 验证最终值等于 value2(.env.local 的值)
|
||
|
||
不依赖 ETL 的 AppConfig,直接测试 python-dotenv 的分层加载行为,
|
||
因为这是设计文档中描述的配置隔离机制的基础。
|
||
"""
|
||
import os
|
||
import tempfile
|
||
|
||
from hypothesis import given, settings, assume
|
||
from hypothesis.strategies import from_regex
|
||
|
||
from dotenv import dotenv_values
|
||
|
||
# 策略:生成合法的 .env 配置项名称和值
|
||
# 配置项名称:首字符为大写字母,后跟大写字母/数字/下划线
|
||
_key_strategy = from_regex(r"[A-Z][A-Z0-9_]{0,29}", fullmatch=True)
|
||
|
||
# 配置项值:可打印 ASCII,排除换行、# 号(注释符)和引号(避免解析歧义)
|
||
_value_strategy = from_regex(r"[A-Za-z0-9_./:@\-]{1,50}", fullmatch=True)
|
||
|
||
|
||
@given(key=_key_strategy, val_root=_value_strategy, val_local=_value_strategy)
|
||
@settings(max_examples=100)
|
||
def test_env_local_overrides_root_env(key: str, val_root: str, val_local: str):
|
||
"""
|
||
Property 3: 配置优先级 - .env.local 覆盖
|
||
|
||
当根 .env 和应用 .env.local 都定义了同一配置项时,
|
||
分层加载(先 .env,再 .env.local override=True)的结果
|
||
应等于 .env.local 中的值。
|
||
|
||
**Validates: Requirements 4.3**
|
||
"""
|
||
# 两个值必须不同,否则无法验证覆盖语义
|
||
assume(val_root != val_local)
|
||
|
||
with tempfile.TemporaryDirectory() as tmpdir:
|
||
root_env = os.path.join(tmpdir, ".env")
|
||
local_env = os.path.join(tmpdir, ".env.local")
|
||
|
||
# 写入根 .env(公共配置)
|
||
with open(root_env, "w", encoding="utf-8") as f:
|
||
f.write(f"{key}={val_root}\n")
|
||
|
||
# 写入应用 .env.local(私有覆盖)
|
||
with open(local_env, "w", encoding="utf-8") as f:
|
||
f.write(f"{key}={val_local}\n")
|
||
|
||
# 模拟分层加载:先加载 .env,再用 .env.local 覆盖
|
||
config = dotenv_values(root_env)
|
||
local_config = dotenv_values(local_env)
|
||
config.update(local_config) # .env.local 覆盖 .env
|
||
|
||
assert config[key] == val_local, (
|
||
f"配置项 '{key}' 应被 .env.local 覆盖为 '{val_local}',"
|
||
f"但实际值为 '{config[key]}'(根 .env 值: '{val_root}')"
|
||
)
|