fix(tests): isolate the developer's real .env from the test suite (#329)
get_effective_config() runs load_dotenv(find_dotenv(usecwd=True), override=True), so any test that loads config injected the repo's real .env into os.environ for the rest of the pytest process. An empty-valued line like MINIMAX_BASE_URL= then made os.environ.get(key, default) return '' instead of the default, failing the MiniMax routing tests in full-suite runs while they passed in isolation. Generalizes the find_dotenv redirect that test_config.py's temp_config_dir fixture already applied locally into a suite-wide autouse fixture, pointing at a never-created path so tests writing their own tmp_path/.env cannot collide with it. Adds a regression test reproducing the leak. Co-authored-by: Xi Zhang <106144707+X-iZhang@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
f72f7b93d5
commit
f3e65a446f
@@ -1,7 +1,11 @@
|
||||
"""Shared fixtures for EvoScientist tests."""
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
_NONEXISTENT_DOTENV = str(Path(__file__).with_name(".pytest-dotenv-does-not-exist"))
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def _reset_tool_selection_state():
|
||||
@@ -164,3 +168,24 @@ def restore_model_passthrough_patch():
|
||||
yield
|
||||
finally:
|
||||
_reset()
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def _isolate_dotenv(monkeypatch):
|
||||
"""Keep the developer's real .env out of the test environment.
|
||||
|
||||
``get_effective_config`` runs ``load_dotenv(find_dotenv(usecwd=True),
|
||||
override=True)``, so any test that loads config injects the repo's
|
||||
real .env into ``os.environ`` for the rest of the pytest process.
|
||||
An empty-valued line like ``MINIMAX_BASE_URL=`` then makes
|
||||
``os.environ.get(key, default)`` return "" instead of the default,
|
||||
breaking unrelated tests later in the run (see issue #322).
|
||||
|
||||
Pointing ``find_dotenv`` at a fixed path that does not exist makes
|
||||
``load_dotenv`` a no-op without creating a temporary directory for
|
||||
every test.
|
||||
"""
|
||||
monkeypatch.setattr(
|
||||
"EvoScientist.config.settings.find_dotenv",
|
||||
lambda *args, **kwargs: _NONEXISTENT_DOTENV,
|
||||
)
|
||||
|
||||
+27
-5
@@ -53,11 +53,6 @@ def temp_config_dir(tmp_path, monkeypatch):
|
||||
"""Use a temporary directory for config during tests."""
|
||||
config_dir = tmp_path / "evoscientist"
|
||||
monkeypatch.setenv("XDG_CONFIG_HOME", str(tmp_path))
|
||||
# Prevent load_dotenv from loading the project's real .env file
|
||||
monkeypatch.setattr(
|
||||
"EvoScientist.config.settings.find_dotenv",
|
||||
lambda *a, **k: str(tmp_path / ".env"),
|
||||
)
|
||||
# Also clear any API keys from environment
|
||||
for key in [
|
||||
"ANTHROPIC_API_KEY",
|
||||
@@ -930,3 +925,30 @@ def test_scheduler_config_defaults_and_env(monkeypatch):
|
||||
assert eff2.memory_skill_synthesis_mode == MemorySkillSynthesisMode.AUTO
|
||||
assert eff2.memory_skill_synthesis_cadence == MemorySkillSynthesisCadence.MONTHLY
|
||||
assert eff2.memory_skill_synthesis_time == "04:30"
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# Dotenv isolation (issue #322)
|
||||
# =============================================================================
|
||||
|
||||
|
||||
class TestDotenvIsolation:
|
||||
def test_env_file_not_leaked_into_process_env(self, tmp_path, monkeypatch):
|
||||
"""A .env in cwd must not leak into os.environ during tests.
|
||||
|
||||
Without the suite-wide ``_isolate_dotenv`` fixture,
|
||||
``get_effective_config`` loads the developer's real .env with
|
||||
``override=True``; an empty-valued line like ``MINIMAX_BASE_URL=``
|
||||
then poisons ``os.environ.get(key, default)`` lookups for every
|
||||
test that runs afterwards in the same process.
|
||||
"""
|
||||
monkeypatch.setenv("XDG_CONFIG_HOME", str(tmp_path))
|
||||
repro_dir = tmp_path / "repro"
|
||||
repro_dir.mkdir()
|
||||
(repro_dir / ".env").write_text("MINIMAX_BASE_URL=\n")
|
||||
monkeypatch.chdir(repro_dir)
|
||||
monkeypatch.delenv("MINIMAX_BASE_URL", raising=False)
|
||||
|
||||
get_effective_config()
|
||||
|
||||
assert "MINIMAX_BASE_URL" not in os.environ
|
||||
|
||||
Reference in New Issue
Block a user