# -*- 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}')" )