mirror of
https://github.com/nesquena/hermes-webui.git
synced 2026-07-18 21:50:55 +00:00
f9acf464e9
Completes the test-sharding half of #3197 (Docker-cache half shipped v0.51.177). Adds pytest-shard 3-way split to tests.yml (3 shards x 3 Python = 9 jobs, fail-fast: false). pytest-shard is 0-indexed so the matrix uses [0,1,2] — the original #3197 used [1,2,3] which would have crashed the out-of-range job and silently skipped shard 0's tests. Made the suite shard-safe by fixing 4 cross-test state-pollution bugs that passed sequentially but failed when sharded: - test_onboarding_mvp: reset onboarding_completed flag (settings.json) in the autouse fixture; the config-cleanup only cleared config.yaml/.env. - test_issue693_system_health_panel: invalidate the process-wide password-hash cache before/after so a prior test's "no password" cache doesn't defeat the auth-gate assertion. - test_auth_session_persistence: assert against auth._SESSIONS_FILE (where auth actually writes) instead of a local _TEST_STATE path that only matched under a lucky import order. - test_profile_env_isolation (root cause of the worst leak): stop deleting + re-importing api.profiles under a temp HERMES_BASE_HOME — that swapped the module object and poisoned the cached _DEFAULT_HERMES_HOME for every later test (broke test_title_aux_routing's load_config). Now points the cached path via monkeypatch.setattr (auto-restored, no module swap). - conftest: autouse fixture restores HERMES_HOME/HERMES_BASE_HOME after each test as defense-in-depth against future switch_profile leaks. Verified: all 3 shards green (6912 passed, 0 failed); full sequential run still green (6957 passed, 0 failed). Slowest shard ~70s vs ~180s sequential.
74 lines
2.9 KiB
Python
74 lines
2.9 KiB
Python
import importlib
|
|
import os
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
|
|
def test_profile_switch_clears_previous_profile_env_vars(monkeypatch, tmp_path):
|
|
base = tmp_path / ".hermes"
|
|
(base / "profiles" / "p1").mkdir(parents=True)
|
|
(base / "profiles" / "p2").mkdir(parents=True)
|
|
(base / "profiles" / "p1" / ".env").write_text(
|
|
"OPENAI_API_KEY=secret-from-p1\nCUSTOM_TOKEN=token-from-p1\n",
|
|
encoding="utf-8",
|
|
)
|
|
|
|
monkeypatch.setenv("HERMES_BASE_HOME", str(base))
|
|
monkeypatch.delenv("HERMES_HOME", raising=False)
|
|
monkeypatch.delenv("OPENAI_API_KEY", raising=False)
|
|
monkeypatch.delenv("CUSTOM_TOKEN", raising=False)
|
|
|
|
# Point the module's cached base-home at our temp base via monkeypatch
|
|
# (auto-restored at teardown) instead of deleting + re-importing api.profiles.
|
|
# The delitem+import_module approach swapped the module object and poisoned
|
|
# dependent modules' cached references, breaking later tests under sharding.
|
|
import api.profiles as profiles
|
|
monkeypatch.setattr(profiles, "_DEFAULT_HERMES_HOME", base)
|
|
|
|
profiles.init_profile_state()
|
|
profiles.switch_profile("p1")
|
|
assert os.environ.get("OPENAI_API_KEY") == "secret-from-p1"
|
|
assert os.environ.get("CUSTOM_TOKEN") == "token-from-p1"
|
|
|
|
profiles.switch_profile("p2")
|
|
assert os.environ.get("OPENAI_API_KEY") is None
|
|
assert os.environ.get("CUSTOM_TOKEN") is None
|
|
assert profiles.get_active_profile_name() == "p2"
|
|
|
|
|
|
def test_profile_switch_replaces_overlapping_keys(monkeypatch, tmp_path):
|
|
base = tmp_path / ".hermes"
|
|
(base / "profiles" / "p1").mkdir(parents=True)
|
|
(base / "profiles" / "p2").mkdir(parents=True)
|
|
(base / "profiles" / "p1" / ".env").write_text(
|
|
"OPENAI_API_KEY=secret-from-p1\nONLY_P1=one\n",
|
|
encoding="utf-8",
|
|
)
|
|
(base / "profiles" / "p2" / ".env").write_text(
|
|
"OPENAI_API_KEY=secret-from-p2\nONLY_P2=two\n",
|
|
encoding="utf-8",
|
|
)
|
|
|
|
monkeypatch.setenv("HERMES_BASE_HOME", str(base))
|
|
monkeypatch.delenv("HERMES_HOME", raising=False)
|
|
monkeypatch.delenv("OPENAI_API_KEY", raising=False)
|
|
monkeypatch.delenv("ONLY_P1", raising=False)
|
|
monkeypatch.delenv("ONLY_P2", raising=False)
|
|
|
|
# Point the module's cached base-home at our temp base via monkeypatch
|
|
# (auto-restored at teardown) instead of deleting + re-importing api.profiles.
|
|
# The delitem+import_module approach swapped the module object and poisoned
|
|
# dependent modules' cached references, breaking later tests under sharding.
|
|
import api.profiles as profiles
|
|
monkeypatch.setattr(profiles, "_DEFAULT_HERMES_HOME", base)
|
|
|
|
profiles.init_profile_state()
|
|
profiles.switch_profile("p1")
|
|
assert os.environ.get("OPENAI_API_KEY") == "secret-from-p1"
|
|
assert os.environ.get("ONLY_P1") == "one"
|
|
|
|
profiles.switch_profile("p2")
|
|
assert os.environ.get("OPENAI_API_KEY") == "secret-from-p2"
|
|
assert os.environ.get("ONLY_P1") is None
|
|
assert os.environ.get("ONLY_P2") == "two"
|