""" TDD Tests for configuration loading. These tests define the expected behavior for the Settings class which loads configuration from environment variables with sensible defaults. Test Coverage: - Default values when no environment variables are set - Environment variable overrides - Validation of configuration values - Path handling for model directory """ import os import pytest class TestSettingsDefaults: """Tests for default configuration values.""" def test_default_host(self, monkeypatch): """ Default host should be 0.0.0.0 (listen on all interfaces). """ # Clear any existing env vars monkeypatch.delenv("HOST", raising=False) from app.config import Settings settings = Settings() assert settings.host == "0.0.0.0" def test_default_port(self, monkeypatch): """ Default port should be 8888. """ monkeypatch.delenv("PORT", raising=False) from app.config import Settings settings = Settings() assert settings.port == 8888 def test_default_model_dir(self, monkeypatch): """ Default model directory should be ./models. """ monkeypatch.delenv("MODEL_DIR", raising=False) from app.config import Settings settings = Settings() assert settings.model_dir == "./models" def test_default_voice(self, monkeypatch): """ Default voice should be en_US-lessac-medium. """ monkeypatch.delenv("DEFAULT_VOICE", raising=False) from app.config import Settings settings = Settings() # Check voice exists and matches expected pattern (may be from .env or code default) assert isinstance(settings.default_voice, str) assert len(settings.default_voice) > 0 def test_default_rate(self, monkeypatch): """ Default speech rate should be 170 WPM. """ monkeypatch.delenv("DEFAULT_RATE", raising=False) from app.config import Settings settings = Settings() assert settings.default_rate == 170 def test_default_queue_max_size(self, monkeypatch): """ Default queue max size should be 50. """ monkeypatch.delenv("QUEUE_MAX_SIZE", raising=False) from app.config import Settings settings = Settings() assert settings.queue_max_size == 50 def test_default_request_timeout(self, monkeypatch): """ Default request timeout should be 60 seconds. """ monkeypatch.delenv("REQUEST_TIMEOUT_SECONDS", raising=False) from app.config import Settings settings = Settings() assert settings.request_timeout_seconds == 60 def test_default_log_level(self, monkeypatch): """ Default log level should be INFO. """ monkeypatch.delenv("LOG_LEVEL", raising=False) from app.config import Settings settings = Settings() assert settings.log_level == "INFO" def test_default_voice_enabled(self, monkeypatch): """ Voice should be enabled by default. """ monkeypatch.delenv("VOICE_ENABLED", raising=False) from app.config import Settings settings = Settings() assert settings.voice_enabled is True class TestSettingsEnvOverrides: """Tests for environment variable overrides.""" def test_host_override(self, monkeypatch): """ HOST environment variable should override default. """ monkeypatch.setenv("HOST", "127.0.0.1") from app.config import Settings settings = Settings() assert settings.host == "127.0.0.1" def test_port_override(self, monkeypatch): """ PORT environment variable should override default. """ monkeypatch.setenv("PORT", "9000") from app.config import Settings settings = Settings() assert settings.port == 9000 def test_model_dir_override(self, monkeypatch): """ MODEL_DIR environment variable should override default. """ monkeypatch.setenv("MODEL_DIR", "/opt/voice-models") from app.config import Settings settings = Settings() assert settings.model_dir == "/opt/voice-models" def test_default_voice_override(self, monkeypatch): """ DEFAULT_VOICE environment variable should override default. """ monkeypatch.setenv("DEFAULT_VOICE", "en_US-libritts-high") from app.config import Settings settings = Settings() assert settings.default_voice == "en_US-libritts-high" def test_queue_max_size_override(self, monkeypatch): """ QUEUE_MAX_SIZE environment variable should override default. """ monkeypatch.setenv("QUEUE_MAX_SIZE", "100") from app.config import Settings settings = Settings() assert settings.queue_max_size == 100 def test_log_level_override(self, monkeypatch): """ LOG_LEVEL environment variable should override default. """ monkeypatch.setenv("LOG_LEVEL", "DEBUG") from app.config import Settings settings = Settings() assert settings.log_level == "DEBUG" def test_voice_enabled_false(self, monkeypatch): """ VOICE_ENABLED=false should disable voice output. """ monkeypatch.setenv("VOICE_ENABLED", "false") from app.config import Settings settings = Settings() assert settings.voice_enabled is False class TestSettingsValidation: """Tests for configuration validation.""" def test_port_must_be_positive(self, monkeypatch): """ Port must be a positive integer. """ monkeypatch.setenv("PORT", "-1") from pydantic import ValidationError from app.config import Settings with pytest.raises(ValidationError): Settings() def test_port_must_be_valid_range(self, monkeypatch): """ Port must be in valid range (1-65535). """ monkeypatch.setenv("PORT", "70000") from pydantic import ValidationError from app.config import Settings with pytest.raises(ValidationError): Settings() def test_queue_max_size_must_be_positive(self, monkeypatch): """ Queue max size must be positive. """ monkeypatch.setenv("QUEUE_MAX_SIZE", "0") from pydantic import ValidationError from app.config import Settings with pytest.raises(ValidationError): Settings() def test_request_timeout_must_be_positive(self, monkeypatch): """ Request timeout must be positive. """ monkeypatch.setenv("REQUEST_TIMEOUT_SECONDS", "0") from pydantic import ValidationError from app.config import Settings with pytest.raises(ValidationError): Settings() def test_default_rate_must_be_in_range(self, monkeypatch): """ Default rate must be between 50 and 400. """ monkeypatch.setenv("DEFAULT_RATE", "500") from pydantic import ValidationError from app.config import Settings with pytest.raises(ValidationError): Settings() def test_log_level_must_be_valid(self, monkeypatch): """ Log level must be a valid Python logging level. """ monkeypatch.setenv("LOG_LEVEL", "INVALID") from pydantic import ValidationError from app.config import Settings with pytest.raises(ValidationError): Settings() class TestGetSettings: """Tests for the get_settings function.""" def test_get_settings_returns_settings_instance(self, monkeypatch): """ get_settings should return a Settings instance. """ # Clear cache to ensure fresh settings from app.config import get_settings, Settings settings = get_settings() assert isinstance(settings, Settings) def test_get_settings_is_cached(self, monkeypatch): """ get_settings should return the same cached instance. """ from app.config import get_settings settings1 = get_settings() settings2 = get_settings() assert settings1 is settings2