fix(ci): eliminate test_set_key flakiness — v0.50.161

Root cause: test_profile_env_isolation.py and test_profile_path_security.py called sys.modules.pop() without restoring, poisoning subsequent tests. Fix: monkeypatch.delitem so pytest auto-restores. Also holds _ENV_LOCK for full I/O cycle in _write_env_file and creates .env at 0600 via os.open. Reviewed by Opus (no independent review needed — test/providers fix only).
This commit is contained in:
nesquena-hermes
2026-04-22 19:09:37 -07:00
committed by GitHub
parent cc025aab79
commit 0f1b232c12
5 changed files with 63 additions and 26 deletions

View File

@@ -18,9 +18,10 @@ def test_profile_switch_clears_previous_profile_env_vars(monkeypatch, tmp_path):
monkeypatch.delenv("OPENAI_API_KEY", raising=False)
monkeypatch.delenv("CUSTOM_TOKEN", raising=False)
sys.modules.pop("api.profiles", None)
# Use monkeypatch so sys.modules is restored after the test, preventing
# api.profiles from being permanently removed and poisoning subsequent tests.
monkeypatch.delitem(sys.modules, "api.profiles", raising=False)
profiles = importlib.import_module("api.profiles")
profiles = importlib.reload(profiles)
profiles.init_profile_state()
profiles.switch_profile("p1")
@@ -52,9 +53,10 @@ def test_profile_switch_replaces_overlapping_keys(monkeypatch, tmp_path):
monkeypatch.delenv("ONLY_P1", raising=False)
monkeypatch.delenv("ONLY_P2", raising=False)
sys.modules.pop("api.profiles", None)
# Use monkeypatch so sys.modules is restored after the test, preventing
# api.profiles from being permanently removed and poisoning subsequent tests.
monkeypatch.delitem(sys.modules, "api.profiles", raising=False)
profiles = importlib.import_module("api.profiles")
profiles = importlib.reload(profiles)
profiles.init_profile_state()
profiles.switch_profile("p1")

View File

@@ -15,11 +15,21 @@ def _reload_profiles_module(base_home: Path):
os.environ["HERMES_BASE_HOME"] = str(base_home)
os.environ["HERMES_HOME"] = str(base_home)
# Save the original module references so we can restore them after the test.
# Permanently deleting api.config / api.profiles from sys.modules breaks
# subsequent tests that import these modules and expect consistent state.
_saved = {name: sys.modules[name] for name in ["api.config", "api.profiles"]
if name in sys.modules}
for name in ["api.config", "api.profiles"]:
if name in sys.modules:
del sys.modules[name]
profiles = importlib.import_module("api.profiles")
# Restore original modules so the cache stays consistent for the rest of the suite.
sys.modules.update(_saved)
return profiles

View File

@@ -59,6 +59,13 @@ def _install_fake_hermes_cli(monkeypatch):
monkeypatch.delitem(sys.modules, "agent.credential_pool", raising=False)
monkeypatch.delitem(sys.modules, "agent", raising=False)
# Flush the 60-second TTL model cache so no prior test's result bleeds in.
try:
from api.config import invalidate_models_cache
invalidate_models_cache()
except Exception:
pass
# ── Unit tests (api/providers.py functions directly) ──────────────────────
@@ -162,6 +169,9 @@ class TestSetProviderKey:
"""Setting a key should write the env var to ~/.hermes/.env."""
_install_fake_hermes_cli(monkeypatch)
monkeypatch.setattr(profiles, "get_active_hermes_home", lambda: tmp_path)
# Also pin HERMES_HOME so code that reads it directly gets tmp_path,
# not the conftest session TEST_STATE_DIR that bleeds into the main process.
monkeypatch.setenv("HERMES_HOME", str(tmp_path))
old_cfg = dict(config.cfg)
old_mtime = config._cfg_mtime
@@ -181,7 +191,7 @@ class TestSetProviderKey:
# Verify .env file was written
env_path = tmp_path / ".env"
assert env_path.exists()
assert env_path.exists(), f".env not written to {env_path}; HERMES_HOME={__import__('os').environ.get('HERMES_HOME')!r}"
content = env_path.read_text()
assert "ANTHROPIC_API_KEY=sk-ant-test-key-12345678" in content
finally: