diff --git a/tests/conftest.py b/tests/conftest.py index 587f2ef1..58dc5fb5 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -190,6 +190,32 @@ def _isolate_hermes_config_path(): os.environ['HERMES_CONFIG_PATH'] = isolated_config_path +@pytest.fixture(autouse=True) +def _reset_password_hash_cache(): + """Reset the memoized password-hash cache around every test (#5588). + + api.auth.get_password_hash() caches the resolved hash process-wide + (_AUTH_HASH_CACHE / _AUTH_HASH_COMPUTED) for perf — it is NOT keyed on the + HERMES_WEBUI_PASSWORD env var. A test that sets that env var (e.g. + test_session_static_assets.test_session_static_auth_exemption) populates the + cache with a real hash; monkeypatch pops the env var on teardown but the + cache stays populated, so is_auth_enabled() reads stale True and later tests + (e.g. test_issue803's profile-cookie helpers) fail with a spurious + "requires a request handler when auth is enabled". Invalidate before AND + after each test so neither a pre-existing cached value nor a value this test + populates leaks across the isolation boundary. No-op when auth is off. + """ + try: + from api.auth import _invalidate_password_hash_cache + except Exception: + _invalidate_password_hash_cache = None + if _invalidate_password_hash_cache: + _invalidate_password_hash_cache() + yield + if _invalidate_password_hash_cache: + _invalidate_password_hash_cache() + + _MISSING = object() # sentinel: api.profiles module not loaded pre-test diff --git a/tests/test_issue803.py b/tests/test_issue803.py index 86835480..ff7bf8e9 100644 --- a/tests/test_issue803.py +++ b/tests/test_issue803.py @@ -34,6 +34,30 @@ class TestProfileCookieHelpers: assert 'SameSite=Lax' in s assert 'Path=/' in s + def test_build_profile_cookie_survives_stale_password_hash_cache(self, monkeypatch): + """#5588 regression: a prior test that set HERMES_WEBUI_PASSWORD populates + api.auth's process-wide password-hash cache; if that cache leaks past the + env-var teardown, is_auth_enabled() reads stale True and build_profile_cookie + raises "requires a request handler when auth is enabled". Simulate the leak + in-test: cache a password, pop the env, and confirm the memoized state does + not make a no-handler build_profile_cookie raise. (The autouse + _reset_password_hash_cache fixture is what keeps this from leaking across + real test files.)""" + import api.auth as auth + from api.helpers import build_profile_cookie + # Populate the cache as if an auth-enabled test just ran. + monkeypatch.setenv("HERMES_WEBUI_PASSWORD", "leak-check") + auth._invalidate_password_hash_cache() + assert auth.is_auth_enabled() is True # cache now holds a hash + # Env goes away (as monkeypatch would on teardown) but reset the cache the + # way the autouse fixture does — auth must read as disabled again. + monkeypatch.delenv("HERMES_WEBUI_PASSWORD", raising=False) + auth._invalidate_password_hash_cache() + assert auth.is_auth_enabled() is False + # And the no-handler cookie build must not raise. + s = build_profile_cookie('alice') + assert 'hermes_profile=alice' in s + def test_build_profile_cookie_default_persists(self): from api.helpers import build_profile_cookie s = build_profile_cookie('default')