diff --git a/tests/conftest.py b/tests/conftest.py index 649f4f0..af2f5a1 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -24,6 +24,32 @@ from transcription.services.documents import DocumentService from transcription.services.jobs import JobService +@pytest.fixture(autouse=True, scope="session") +def isolate_settings_from_local_env_files(tmp_path_factory): + """Point `Settings` at a controlled stub env file instead of a developer one. + + `Settings.model_config` declares `env_file=".env.production"`, resolved against the + current working directory, so a repository-root pytest run would otherwise read real + deployment values into tests that assert declared defaults. + + The stub mirrors what `.github/workflows/quality-gate.yml` writes in CI: only + `OPENROUTER_API_KEY`, which is required and which many tests need `get_settings()` to + find. Everything else falls back to declared defaults, so local and CI runs agree. This + stays a file rather than a process environment variable because + `test_config.py::test_requires_api_key` asserts the missing-key failure via + `_env_file=None`. Guarded by `tests/test_config_isolation.py`. + """ + stub = tmp_path_factory.mktemp("settings-env") / ".env.test" + stub.write_text("OPENROUTER_API_KEY=test-placeholder-not-a-real-key\n", encoding="utf-8") + + original = Settings.model_config.get("env_file") + Settings.model_config["env_file"] = str(stub) + try: + yield + finally: + Settings.model_config["env_file"] = original + + @pytest.fixture def session(): """Provide a clean synchronous database session for sync tests.""" diff --git a/tests/test_config_isolation.py b/tests/test_config_isolation.py new file mode 100644 index 0000000..6886e81 --- /dev/null +++ b/tests/test_config_isolation.py @@ -0,0 +1,39 @@ +"""Suite-wide isolation of `Settings` from developer environment files. + +`Settings.model_config` declares `env_file=".env.production"`, resolved relative to the +current working directory. Running pytest from the repository root therefore loads a real +developer env file into tests that construct `Settings(...)` directly, and the declared +field defaults stop being what the suite actually exercises. + +That breaks verification in both directions: tests asserting default behavior fail locally +for environmental reasons, and tests asserting configured behavior can pass locally against +values that do not exist in CI. The autouse fixture in `conftest.py` neutralizes the class +level `env_file` so defaults are authoritative; tests that need file loading still pass +`_env_file=` explicitly, which takes precedence over the class config. +""" + +from __future__ import annotations + +from pathlib import Path + +from transcription.config import Settings + + +def test_settings_defaults_are_not_overridden_by_a_local_env_file(): + """A bare `Settings(...)` must observe declared defaults, not developer machine state.""" + settings = Settings(openrouter_api_key="test-key") + + assert settings.environment == "development" + assert settings.log_dir == Path("./data/logs") + assert settings.host == "0.0.0.0" + assert settings.port == 8000 + + +def test_explicit_env_file_still_loads(tmp_path): + """Isolation must not disable env-file loading for tests that opt into it.""" + env_path = tmp_path / ".env" + env_path.write_text("PORT=9123\n", encoding="utf-8") + + settings = Settings(openrouter_api_key="test-key", _env_file=env_path, _cli_parse_args=False) + + assert settings.port == 9123