"""配置加载的测试。 重点在两处:非法值必须**响亮失败而不是静默回落**(一次配置笔误变成难查的行为 偏差是最糟的结果),以及密钥**绝不能有默认值**。 """ from __future__ import annotations import pytest from app.config import ServiceAuthSettings, Settings, VolcengineSettings class TestCredentialsHaveNoDefaults: def test_api_key_defaults_to_empty_not_a_placeholder(self, monkeypatch): monkeypatch.delenv("ARK_API_KEY", raising=False) assert VolcengineSettings.from_env().api_key == "" def test_service_jwt_public_key_defaults_to_empty(self, monkeypatch): monkeypatch.delenv("PATBOND_SERVICE_JWT_PUBLIC_KEY", raising=False) assert ServiceAuthSettings.from_env().public_key == "" def test_missing_credentials_do_not_prevent_loading(self, monkeypatch): """缺密钥时配置仍能加载。 服务要能起来并提供健康检查;缺配置在**调用点**响亮失败,而不是让整个 进程起不来——与 Java 侧 JwtVerifier/ServiceTokenSigner 的既有约定一致。 """ for name in ("ARK_API_KEY", "PATBOND_SERVICE_JWT_PUBLIC_KEY"): monkeypatch.delenv(name, raising=False) settings = Settings.from_env() assert settings.volcengine.api_key == "" assert settings.service_auth.public_key == "" class TestEnvParsing: def test_values_are_trimmed(self, monkeypatch): # 从 .env 或 compose 里复制粘贴时尾随空格极常见。 monkeypatch.setenv("ARK_API_KEY", " dummy-value ") assert VolcengineSettings.from_env().api_key == "dummy-value" def test_int_override(self, monkeypatch): monkeypatch.setenv("ARK_TASK_EXPIRY_SEC", "7200") assert VolcengineSettings.from_env().task_expiry_sec == 7200 def test_float_override(self, monkeypatch): monkeypatch.setenv("ARK_TIMEOUT_SEC", "2.5") assert VolcengineSettings.from_env().timeout_sec == 2.5 def test_malformed_int_fails_loudly_instead_of_falling_back(self, monkeypatch): # 静默回落到默认值会让一次笔误变成难查的行为偏差。 monkeypatch.setenv("ARK_TASK_EXPIRY_SEC", "一小时") with pytest.raises(ValueError, match="不是合法整数"): VolcengineSettings.from_env() def test_malformed_float_fails_loudly(self, monkeypatch): monkeypatch.setenv("ARK_TIMEOUT_SEC", "fast") with pytest.raises(ValueError, match="不是合法数字"): VolcengineSettings.from_env() def test_empty_string_uses_the_default(self, monkeypatch): # compose 里 `ARK_TIMEOUT_SEC: ${FOO:-}` 展开成空串是常见情形。 monkeypatch.setenv("ARK_TIMEOUT_SEC", "") assert VolcengineSettings.from_env().timeout_sec == 10.0 class TestVendorConstraintsAreEnforcedAtStartup: """厂商的硬性区间在启动时校验,而不是等提交时被拒。""" def test_expiry_below_the_vendor_minimum_is_rejected(self): with pytest.raises(ValueError, match=r"\[3600, 259200\]"): VolcengineSettings(task_expiry_sec=60) def test_expiry_above_the_vendor_maximum_is_rejected(self): with pytest.raises(ValueError, match=r"\[3600, 259200\]"): VolcengineSettings(task_expiry_sec=300000) def test_the_boundaries_themselves_are_allowed(self): assert VolcengineSettings(task_expiry_sec=3600).task_expiry_sec == 3600 assert VolcengineSettings(task_expiry_sec=259200).task_expiry_sec == 259200 def test_default_is_the_minimum_not_the_vendor_default(self): # 厂商默认 48 小时,被放弃的任务会在厂商侧挂两天。 assert VolcengineSettings().task_expiry_sec == 3600 class TestServiceAuthDefaults: def test_audience_and_type_have_sensible_defaults(self, monkeypatch): for name in ( "PATBOND_SERVICE_JWT_AUDIENCE", "PATBOND_SERVICE_JWT_ISSUER", "PATBOND_SERVICE_JWT_TYPE", ): monkeypatch.delenv(name, raising=False) auth = ServiceAuthSettings.from_env() assert auth.audience == "patbond-ai" assert auth.issuer == "patbond-api" assert auth.required_type == "service"