Spaces:
Configuration error
Configuration error
| """Unit tests for app.config (SETUP-03). | |
| These tests exercise `app.config.load_settings()` directly, using only | |
| monkeypatched (fake) environment values -- never the project's real `.env` | |
| or real secrets. | |
| """ | |
| from __future__ import annotations | |
| import pytest | |
| from app.config import ConfigError, load_settings | |
| VALID_ENV = { | |
| "DB_HOST": "test-db-host.invalid", | |
| "DB_PORT": "3306", | |
| "DB_NAME": "test_db", | |
| "DB_USER": "test_user", | |
| "DB_PASSWORD": "test-password-not-real", | |
| "DATABASE_URL": ( | |
| "mysql+pymysql://test_user:test-password-not-real" | |
| "@test-db-host.invalid:3306/test_db" | |
| ), | |
| "APP_PORT": "8000", | |
| "ALLOWED_ORIGINS": "https://example.test,https://other.example.test", | |
| "FRAUD_ALERT_THRESHOLD": "0.5", | |
| "MODEL_PATH": "training/artifacts/model_v1.pkl", | |
| "SEED_OFFICER_EMAIL": "officer@example.test", | |
| "SEED_OFFICER_PASSWORD": "fake-seed-password-not-real", | |
| "SESSION_EXPIRE_MINUTES": "1440", | |
| "KAGGLE_USERNAME": "fake-kaggle-user", | |
| "KAGGLE_KEY": "fake-kaggle-key-not-real", | |
| } | |
| def valid_env(monkeypatch: pytest.MonkeyPatch) -> None: | |
| """Set a complete, fully-fake, valid environment for each test.""" | |
| for key, value in VALID_ENV.items(): | |
| monkeypatch.setenv(key, value) | |
| def test_complete_env_yields_typed_settings(valid_env: None) -> None: | |
| settings = load_settings() | |
| assert settings.DB_HOST == VALID_ENV["DB_HOST"] | |
| assert settings.DB_PORT == 3306 | |
| assert isinstance(settings.DB_PORT, int) | |
| assert settings.DB_NAME == VALID_ENV["DB_NAME"] | |
| assert settings.DB_USER == VALID_ENV["DB_USER"] | |
| assert settings.DB_PASSWORD == VALID_ENV["DB_PASSWORD"] | |
| assert settings.DATABASE_URL == VALID_ENV["DATABASE_URL"] | |
| assert settings.APP_PORT == 8000 | |
| assert isinstance(settings.APP_PORT, int) | |
| assert settings.ALLOWED_ORIGINS == [ | |
| "https://example.test", | |
| "https://other.example.test", | |
| ] | |
| assert settings.FRAUD_ALERT_THRESHOLD == pytest.approx(0.5) | |
| assert isinstance(settings.FRAUD_ALERT_THRESHOLD, float) | |
| assert settings.MODEL_PATH == VALID_ENV["MODEL_PATH"] | |
| assert settings.SEED_OFFICER_EMAIL == VALID_ENV["SEED_OFFICER_EMAIL"] | |
| assert settings.SEED_OFFICER_PASSWORD == VALID_ENV["SEED_OFFICER_PASSWORD"] | |
| assert settings.SESSION_EXPIRE_MINUTES == 1440 | |
| assert isinstance(settings.SESSION_EXPIRE_MINUTES, int) | |
| assert settings.KAGGLE_USERNAME == VALID_ENV["KAGGLE_USERNAME"] | |
| assert settings.KAGGLE_KEY == VALID_ENV["KAGGLE_KEY"] | |
| def test_missing_required_var_raises_config_error_naming_the_variable( | |
| valid_env: None, monkeypatch: pytest.MonkeyPatch, missing_var: str | |
| ) -> None: | |
| monkeypatch.delenv(missing_var, raising=False) | |
| with pytest.raises(ConfigError) as exc_info: | |
| load_settings() | |
| message = str(exc_info.value) | |
| assert missing_var in message | |
| # Never echo secret-ish values in the error message. | |
| assert VALID_ENV["DB_PASSWORD"] not in message | |
| assert VALID_ENV["KAGGLE_KEY"] not in message | |
| def test_out_of_range_fraud_alert_threshold_raises_config_error( | |
| valid_env: None, monkeypatch: pytest.MonkeyPatch | |
| ) -> None: | |
| monkeypatch.setenv("FRAUD_ALERT_THRESHOLD", "1.5") | |
| with pytest.raises(ConfigError) as exc_info: | |
| load_settings() | |
| assert "FRAUD_ALERT_THRESHOLD" in str(exc_info.value) | |
| def test_non_numeric_fraud_alert_threshold_raises_config_error( | |
| valid_env: None, monkeypatch: pytest.MonkeyPatch | |
| ) -> None: | |
| monkeypatch.setenv("FRAUD_ALERT_THRESHOLD", "not-a-number") | |
| with pytest.raises(ConfigError) as exc_info: | |
| load_settings() | |
| assert "FRAUD_ALERT_THRESHOLD" in str(exc_info.value) | |
| def test_unparseable_db_port_raises_config_error( | |
| valid_env: None, monkeypatch: pytest.MonkeyPatch | |
| ) -> None: | |
| monkeypatch.setenv("DB_PORT", "not-an-int") | |
| with pytest.raises(ConfigError) as exc_info: | |
| load_settings() | |
| assert "DB_PORT" in str(exc_info.value) | |
| def test_missing_app_port_falls_back_to_default( | |
| valid_env: None, monkeypatch: pytest.MonkeyPatch | |
| ) -> None: | |
| monkeypatch.delenv("APP_PORT", raising=False) | |
| settings = load_settings() | |
| assert settings.APP_PORT == 8000 | |
| def test_unparseable_app_port_raises_config_error( | |
| valid_env: None, monkeypatch: pytest.MonkeyPatch | |
| ) -> None: | |
| monkeypatch.setenv("APP_PORT", "not-an-int") | |
| with pytest.raises(ConfigError) as exc_info: | |
| load_settings() | |
| assert "APP_PORT" in str(exc_info.value) | |
| def test_missing_session_expire_minutes_falls_back_to_default( | |
| valid_env: None, monkeypatch: pytest.MonkeyPatch | |
| ) -> None: | |
| monkeypatch.delenv("SESSION_EXPIRE_MINUTES", raising=False) | |
| settings = load_settings() | |
| assert settings.SESSION_EXPIRE_MINUTES == 1440 | |