Files
xiaoxia-saas/tests/unit/test_config_oss.py
xiaoxia 881eea9195
CI/CD Pipeline / Validate Code Quality And Tests (push) Successful in 36s
CI/CD Pipeline / Frontend Lint (push) Successful in 51s
CI/CD Pipeline / Build Production Runtime Images (push) Has been skipped
CI/CD Pipeline / Deploy Production (push) Has been skipped
CI/CD Pipeline / Production Browser E2E (push) Has been skipped
CI/CD Pipeline / Unit Tests (push) Successful in 56s
CI/CD Pipeline / Build & Push Staging (Watchtower auto-deploy) (push) Failing after 9s
CI/CD Pipeline / Staging E2E Tests (push) Has been skipped
CI/CD Pipeline / Staging API Integration Tests (push) Has been skipped
CI/CD Pipeline / Integration Tests (push) Successful in 1m50s
fix(api+ci): DELETE 204响应体全修复 + isort/black整理 + CI环境兼容 + 测试修复 (#286)
fix(api+ci): DELETE 204响应体全修复 + isort/black整理 + CI环境兼容 + 测试修复
2026-07-14 07:07:51 +08:00

197 lines
6.9 KiB
Python
Executable File

"""
config.py OSS 配置字段单元测试
覆盖:
- OSS 相关字段默认值
- 环境变量覆盖
- 字段名与代码引用一致
- pydantic_settings 加载行为
"""
from __future__ import annotations
import importlib.util
import os
from pathlib import Path
from unittest.mock import patch
def _load_settings_class():
"""
直接加载 config.py 模块,绕过 apps/api/__init__.py 的副作用。
apps/api/__init__.py 会导入 main.py,而 main.py 依赖 app 模块。
"""
config_path = Path(__file__).resolve().parents[2] / "apps" / "api" / "app" / "config.py"
spec = importlib.util.spec_from_file_location("config_module", config_path)
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
return module.Settings
def _fresh_settings(**env_overrides: dict[str, str]):
"""
每次创建一个全新的 Settings 实例。
env_overrides 会注入到 os.environ。
"""
env = {
"JWT_SECRET_KEY": "unit-test-secret-key-12345",
**env_overrides,
}
with patch.dict(os.environ, env, clear=True):
Settings = _load_settings_class()
return Settings()
# ---------------------------------------------------------------------------
# 默认值测试
# ---------------------------------------------------------------------------
class TestOSSConfigDefaults:
"""OSS 配置字段默认值必须与代码引用一致。"""
def test_oss_endpoint_default(self):
settings = _fresh_settings()
assert settings.OSS_ENDPOINT == "oss-cn-hangzhou.aliyuncs.com"
def test_oss_access_key_id_default_empty(self):
settings = _fresh_settings()
assert settings.OSS_ACCESS_KEY_ID == ""
def test_oss_access_key_secret_default_empty(self):
settings = _fresh_settings()
assert settings.OSS_ACCESS_KEY_SECRET == ""
def test_oss_bucket_name_default(self):
settings = _fresh_settings()
assert settings.OSS_BUCKET_NAME == "xiaoxia-autocut"
def test_oss_direct_upload_max_mb_default_is_2000(self):
"""PR #117 修复:默认值从 800 改为 2000。"""
settings = _fresh_settings()
assert settings.OSS_DIRECT_UPLOAD_MAX_MB == 2000
def test_oss_direct_upload_expire_seconds_default(self):
settings = _fresh_settings()
assert settings.OSS_DIRECT_UPLOAD_EXPIRE_SECONDS == 900
# ---------------------------------------------------------------------------
# 环境变量覆盖测试
# ---------------------------------------------------------------------------
class TestOSSConfigEnvOverride:
"""环境变量能正确覆盖 OSS 配置字段。"""
def test_oss_endpoint_override(self):
settings = _fresh_settings(OSS_ENDPOINT="oss-cn-shanghai.aliyuncs.com")
assert settings.OSS_ENDPOINT == "oss-cn-shanghai.aliyuncs.com"
def test_oss_access_key_id_override(self):
settings = _fresh_settings(OSS_ACCESS_KEY_ID="test-key-id")
assert settings.OSS_ACCESS_KEY_ID == "test-key-id"
def test_oss_access_key_secret_override(self):
settings = _fresh_settings(OSS_ACCESS_KEY_SECRET="test-key-secret")
assert settings.OSS_ACCESS_KEY_SECRET == "test-key-secret"
def test_oss_bucket_name_override(self):
settings = _fresh_settings(OSS_BUCKET_NAME="test-bucket")
assert settings.OSS_BUCKET_NAME == "test-bucket"
def test_oss_direct_upload_max_mb_override(self):
settings = _fresh_settings(OSS_DIRECT_UPLOAD_MAX_MB="4096")
assert settings.OSS_DIRECT_UPLOAD_MAX_MB == 4096
def test_oss_direct_upload_expire_seconds_override(self):
settings = _fresh_settings(OSS_DIRECT_UPLOAD_EXPIRE_SECONDS="1800")
assert settings.OSS_DIRECT_UPLOAD_EXPIRE_SECONDS == 1800
# ---------------------------------------------------------------------------
# 字段名一致性测试(防止再次出现字段名拼写错误导致 500)
# ---------------------------------------------------------------------------
class TestOSSConfigFieldNameConsistency:
"""
确保 Settings 类包含代码中实际引用的所有字段。
防止类似 OSS_DIRECT_UPLOAD_EXPRESS_SECRET 的拼写错误再次发生。
"""
def test_settings_has_oss_endpoint_field(self):
settings = _fresh_settings()
assert hasattr(settings, "OSS_ENDPOINT")
def test_settings_has_oss_access_key_id_field(self):
settings = _fresh_settings()
assert hasattr(settings, "OSS_ACCESS_KEY_ID")
def test_settings_has_oss_access_key_secret_field(self):
settings = _fresh_settings()
assert hasattr(settings, "OSS_ACCESS_KEY_SECRET")
def test_settings_has_oss_bucket_name_field(self):
settings = _fresh_settings()
assert hasattr(settings, "OSS_BUCKET_NAME")
def test_settings_has_oss_direct_upload_max_mb_field(self):
settings = _fresh_settings()
assert hasattr(settings, "OSS_DIRECT_UPLOAD_MAX_MB")
def test_settings_has_oss_direct_upload_expire_seconds_field(self):
settings = _fresh_settings()
assert hasattr(settings, "OSS_DIRECT_UPLOAD_EXPIRE_SECONDS")
def test_property_aliases_match_field_names(self):
"""确保 property 访问器与字段值一致。"""
settings = _fresh_settings(
OSS_ENDPOINT="ep",
OSS_ACCESS_KEY_ID="kid",
OSS_ACCESS_KEY_SECRET="ksec",
OSS_BUCKET_NAME="bkt",
)
assert settings.oss_endpoint == "ep"
assert settings.oss_access_key_id == "kid"
assert settings.oss_access_key_secret == "ksec"
assert settings.oss_bucket_name == "bkt"
# ---------------------------------------------------------------------------
# extra="ignore" 行为测试
# ---------------------------------------------------------------------------
class TestSettingsExtraIgnore:
"""extra="ignore" 确保未知环境变量不会导致启动失败。"""
def test_unknown_env_var_is_ignored(self):
settings = _fresh_settings(UNKNOWN_RANDOM_VAR="whatever")
assert not hasattr(settings, "UNKNOWN_RANDOM_VAR")
def test_settings_loads_without_error(self):
settings = _fresh_settings()
assert settings.APP_NAME == "xiaoxia-saas"
# ---------------------------------------------------------------------------
# 别名测试(MAX_UPLOAD_SIZE_MB 兼容旧配置)
# ---------------------------------------------------------------------------
class TestOSSConfigAliases:
"""OSS_DIRECT_UPLOAD_MAX_MB 支持 MAX_UPLOAD_SIZE_MB 别名。"""
def test_max_upload_size_mb_alias_works(self):
"""旧环境变量 MAX_UPLOAD_SIZE_MB 仍能生效。"""
env = {
"JWT_SECRET_KEY": "unit-test-secret-key-12345",
"MAX_UPLOAD_SIZE_MB": "3000",
}
with patch.dict(os.environ, env, clear=False):
os.environ.pop("OSS_DIRECT_UPLOAD_MAX_MB", None)
Settings = _load_settings_class()
settings = Settings()
assert settings.OSS_DIRECT_UPLOAD_MAX_MB == 3000