245 lines
8.3 KiB
Python
245 lines
8.3 KiB
Python
"""Tests for infrastructure config."""
|
|
|
|
import pytest
|
|
|
|
from app.infrastructure.config import (
|
|
AppConfig,
|
|
DBConfig,
|
|
Environment,
|
|
KCConfig,
|
|
SecurityConfig,
|
|
Settings,
|
|
)
|
|
|
|
|
|
class TestSettings:
|
|
"""Test Settings with composition pattern."""
|
|
|
|
def test_default_values(self) -> None:
|
|
"""Test default settings values by creating settings without env file."""
|
|
# Create settings with required secrets and no env file
|
|
s = Settings(
|
|
_env_file=None,
|
|
security=SecurityConfig(secret_key="test-secret-key"),
|
|
kc=KCConfig(client_secret="test-client-secret"),
|
|
)
|
|
assert s.app.name == "Blog API"
|
|
assert s.app.debug is False
|
|
assert s.app.host == "0.0.0.0"
|
|
assert s.app.port == 8000
|
|
assert s.database_url == "sqlite+aiosqlite:///./blog.db"
|
|
assert s.db.echo is False
|
|
assert s.security.secret_key == "test-secret-key"
|
|
assert s.kc.client_secret == "test-client-secret"
|
|
assert s.environment == Environment.DEV
|
|
|
|
def test_custom_values(self) -> None:
|
|
"""Test custom settings values."""
|
|
s = Settings(
|
|
_env_file=None,
|
|
environment=Environment.PROD,
|
|
app=AppConfig(
|
|
name="Test API",
|
|
debug=True,
|
|
host="localhost",
|
|
port=9000,
|
|
),
|
|
db=DBConfig(url="postgresql+asyncpg://user:pass@host/db"),
|
|
security=SecurityConfig(secret_key="test-secret"),
|
|
kc=KCConfig(client_secret="test-client-secret"),
|
|
)
|
|
assert s.app.name == "Test API"
|
|
assert s.app.debug is True
|
|
assert s.app.host == "localhost"
|
|
assert s.app.port == 9000
|
|
assert s.database_url == "postgresql+asyncpg://user:pass@host/db"
|
|
assert s.security.secret_key == "test-secret"
|
|
assert s.kc.client_secret == "test-client-secret"
|
|
assert s.environment == Environment.PROD
|
|
|
|
def test_model_config(self) -> None:
|
|
"""Test settings model config."""
|
|
assert "env_file" in Settings.model_config
|
|
|
|
def test_is_dev_property(self) -> None:
|
|
"""Test is_dev property."""
|
|
s = Settings(
|
|
_env_file=None,
|
|
environment=Environment.DEV,
|
|
security=SecurityConfig(secret_key="test"),
|
|
kc=KCConfig(client_secret="test"),
|
|
)
|
|
assert s.is_dev is True
|
|
assert s.is_prod is False
|
|
|
|
def test_is_prod_property(self) -> None:
|
|
"""Test is_prod property."""
|
|
s = Settings(
|
|
_env_file=None,
|
|
environment=Environment.PROD,
|
|
security=SecurityConfig(secret_key="test"),
|
|
kc=KCConfig(client_secret="test"),
|
|
)
|
|
assert s.is_prod is True
|
|
assert s.is_dev is False
|
|
|
|
def test_prod_requires_security_secret(self) -> None:
|
|
"""Test that prod mode requires security secret_key."""
|
|
with pytest.raises(ValueError, match="SECURITY_SECRET_KEY"):
|
|
Settings(
|
|
_env_file=None,
|
|
environment=Environment.PROD,
|
|
security=SecurityConfig(secret_key=""),
|
|
kc=KCConfig(client_secret="test"),
|
|
)
|
|
|
|
def test_prod_requires_kc_secret(self) -> None:
|
|
"""Test that prod mode requires KC client_secret."""
|
|
with pytest.raises(ValueError, match="KC_CLIENT_SECRET"):
|
|
Settings(
|
|
_env_file=None,
|
|
environment=Environment.PROD,
|
|
security=SecurityConfig(secret_key="test"),
|
|
kc=KCConfig(client_secret=""),
|
|
)
|
|
|
|
def test_database_url_dev_default(self) -> None:
|
|
"""Test default database URL in dev mode."""
|
|
s = Settings(
|
|
_env_file=None,
|
|
environment=Environment.DEV,
|
|
security=SecurityConfig(secret_key="test"),
|
|
kc=KCConfig(client_secret="test"),
|
|
)
|
|
assert s.database_url == "sqlite+aiosqlite:///./blog.db"
|
|
|
|
def test_database_url_prod_builds_postgres(self) -> None:
|
|
"""Test that database URL builds from components in prod."""
|
|
s = Settings(
|
|
_env_file=None,
|
|
environment=Environment.PROD,
|
|
db=DBConfig(
|
|
url=None, # Force building from components
|
|
host="db.example.com",
|
|
port=5433,
|
|
user="admin",
|
|
password="secret",
|
|
name="mydb",
|
|
),
|
|
security=SecurityConfig(secret_key="test"),
|
|
kc=KCConfig(client_secret="test"),
|
|
)
|
|
assert s.database_url == "postgresql+asyncpg://admin:secret@db.example.com:5433/mydb"
|
|
|
|
def test_database_url_override(self) -> None:
|
|
"""Test that explicit database URL overrides auto-building."""
|
|
s = Settings(
|
|
_env_file=None,
|
|
environment=Environment.PROD,
|
|
db=DBConfig(
|
|
url="postgresql+asyncpg://custom/url",
|
|
host="ignored",
|
|
user="ignored",
|
|
),
|
|
security=SecurityConfig(secret_key="test"),
|
|
kc=KCConfig(client_secret="test"),
|
|
)
|
|
assert s.database_url == "postgresql+asyncpg://custom/url"
|
|
|
|
|
|
class TestAppConfig:
|
|
"""Test AppConfig."""
|
|
|
|
def test_default_values(self) -> None:
|
|
"""Test AppConfig default values."""
|
|
cfg = AppConfig()
|
|
assert cfg.name == "Blog API"
|
|
assert cfg.debug is False
|
|
assert cfg.host == "0.0.0.0"
|
|
assert cfg.port == 8000
|
|
|
|
|
|
class TestDBConfig:
|
|
"""Test DBConfig."""
|
|
|
|
def test_default_values(self) -> None:
|
|
"""Test DBConfig default values."""
|
|
cfg = DBConfig()
|
|
assert cfg.url is None
|
|
assert cfg.echo is False
|
|
assert cfg.host == "localhost"
|
|
assert cfg.port == 5432
|
|
assert cfg.user == "postgres"
|
|
assert cfg.password == "postgres"
|
|
assert cfg.name == "blog"
|
|
|
|
def test_postgres_url_validation(self) -> None:
|
|
"""Test URL validation for postgres."""
|
|
cfg = DBConfig(url="postgresql+asyncpg://user:pass@host/db")
|
|
assert cfg.url == "postgresql+asyncpg://user:pass@host/db"
|
|
|
|
def test_sqlite_url_validation(self) -> None:
|
|
"""Test URL validation for sqlite."""
|
|
cfg = DBConfig(url="sqlite+aiosqlite:///./test.db")
|
|
assert cfg.url == "sqlite+aiosqlite:///./test.db"
|
|
|
|
def test_invalid_url_validation(self) -> None:
|
|
"""Test URL validation rejects invalid URLs."""
|
|
with pytest.raises(ValueError, match="sqlite+.*postgresql+"):
|
|
DBConfig(url="mysql://invalid")
|
|
|
|
|
|
class TestKCConfig:
|
|
"""Test KCConfig."""
|
|
|
|
def test_default_values(self) -> None:
|
|
"""Test KCConfig default values."""
|
|
cfg = KCConfig(client_secret="test-secret")
|
|
assert cfg.server_url == "http://localhost:8080"
|
|
assert cfg.realm == "blog"
|
|
assert cfg.client_id == "blog-api"
|
|
assert cfg.client_secret == "test-secret"
|
|
assert cfg.token_cache_ttl == 60
|
|
|
|
def test_is_configured_with_secret(self) -> None:
|
|
"""Test is_configured returns True when secret is set."""
|
|
cfg = KCConfig(client_secret="test-secret")
|
|
assert cfg.is_configured is True
|
|
|
|
def test_is_configured_without_secret(self) -> None:
|
|
"""Test is_configured returns False when secret is empty."""
|
|
cfg = KCConfig(client_secret="")
|
|
assert cfg.is_configured is False
|
|
|
|
|
|
class TestSecurityConfig:
|
|
"""Test SecurityConfig."""
|
|
|
|
def test_default_values(self) -> None:
|
|
"""Test SecurityConfig default values."""
|
|
cfg = SecurityConfig(secret_key="test-key")
|
|
assert cfg.secret_key == "test-key"
|
|
assert cfg.access_token_expire_minutes == 30
|
|
|
|
def test_is_configured_with_secret(self) -> None:
|
|
"""Test is_configured returns True when secret is set."""
|
|
cfg = SecurityConfig(secret_key="test-secret")
|
|
assert cfg.is_configured is True
|
|
|
|
def test_is_configured_without_secret(self) -> None:
|
|
"""Test is_configured returns False when secret is empty."""
|
|
cfg = SecurityConfig(secret_key="")
|
|
assert cfg.is_configured is False
|
|
|
|
|
|
class TestEnvironment:
|
|
"""Test Environment enum."""
|
|
|
|
def test_dev_value(self) -> None:
|
|
"""Test DEV environment value."""
|
|
assert Environment.DEV.value == "dev"
|
|
|
|
def test_prod_value(self) -> None:
|
|
"""Test PROD environment value."""
|
|
assert Environment.PROD.value == "prod"
|