Files
smart-support/backend/tests/unit/test_config.py
Yaojia Wang e55ec42ae5 fix: restore green builds and align frontend-backend contracts (P0)
- Isolate Settings tests from .env and process env leakage
- Fix analytics metadata test to unwrap psycopg Json wrapper
- Remove unused state variables causing frontend build failures
- Fix ReviewPage to use /classifications endpoint instead of nonexistent /result
- Normalize ReviewPage status enums (failed not error) and access_type values
- Align api.ts types with backend response shapes (ReplayPage, AnalyticsData, AgentUsage)
2026-04-05 23:00:39 +02:00

92 lines
3.2 KiB
Python

"""Tests for app.config module."""
from __future__ import annotations
import pytest
from app.config import Settings
def _isolated_settings(**kwargs: object) -> Settings:
"""Create a Settings instance that ignores .env files and process env vars.
pydantic-settings reads from env_file and environment by default, which
causes test results to depend on the machine they run on. We override
model_config at the class level temporarily so that every test gets
deterministic results.
"""
# Build a throwaway subclass that disables env-file and env-var loading.
class _IsolatedSettings(Settings):
model_config = Settings.model_config.copy()
model_config["env_file"] = None # type: ignore[assignment]
model_config["env_ignore_empty"] = True
# _env_parse_none_str makes pydantic-settings treat missing env vars as
# absent rather than empty-string, so required fields will raise.
import os
env_backup = os.environ.copy()
# Strip all env vars that Settings knows about so they can't leak in.
settings_fields = set(Settings.model_fields)
for key in list(os.environ):
if key.lower() in settings_fields:
del os.environ[key]
try:
return _IsolatedSettings(**kwargs) # type: ignore[return-value]
finally:
os.environ.clear()
os.environ.update(env_backup)
@pytest.mark.unit
class TestSettings:
def test_default_values(self) -> None:
settings = _isolated_settings(
database_url="postgresql://x:x@localhost/db",
anthropic_api_key="key",
)
assert settings.llm_provider == "anthropic"
assert settings.llm_model == "claude-sonnet-4-6"
assert settings.session_ttl_minutes == 30
assert settings.interrupt_ttl_minutes == 30
def test_custom_values(self) -> None:
settings = _isolated_settings(
database_url="postgresql://x:x@localhost/db",
llm_provider="openai",
llm_model="gpt-4o",
session_ttl_minutes=15,
openai_api_key="sk-test",
)
assert settings.llm_provider == "openai"
assert settings.llm_model == "gpt-4o"
assert settings.session_ttl_minutes == 15
def test_invalid_provider_rejected(self) -> None:
with pytest.raises(Exception):
_isolated_settings(
database_url="postgresql://x:x@localhost/db",
llm_provider="invalid",
)
def test_missing_database_url_rejected(self) -> None:
with pytest.raises(Exception):
_isolated_settings(anthropic_api_key="key")
def test_empty_api_key_for_provider_rejected(self) -> None:
with pytest.raises(ValueError, match="API key"):
_isolated_settings(
database_url="postgresql://x:x@localhost/db",
llm_provider="anthropic",
anthropic_api_key="",
)
def test_wrong_provider_key_rejected(self) -> None:
with pytest.raises(ValueError, match="API key"):
_isolated_settings(
database_url="postgresql://x:x@localhost/db",
llm_provider="openai",
anthropic_api_key="key",
openai_api_key="",
)