"""Subprocess-surviving isolation marker (#82770). ``PYTEST_CURRENT_TEST`` / ``PYTEST_VERSION`` are pytest's vars: tests that spawn children and rebuild the child environment routinely strip them so the child "looks like a real CLI" — which used to disarm the live-DB guard in the child at the same moment the child lost the ``HERMES_HOME`` redirect. That pairing is exactly how fixture rows (dm:123 / chat-1 / wx-chat) landed in a developer's production state.db. ``HERMES_TEST_ISOLATION`` is Hermes's own marker: the hermetic conftest exports it (value = the isolation root) before any test module imports, it inherits into children by default, and ``hermes_state`` honors it as a test-context signal. These tests pin all three properties. """ import json import os import subprocess import sys from pathlib import Path import hermes_state REPO_ROOT = Path(__file__).resolve().parents[2] _CHILD_PROBE = r""" import json, os, sys sys.path.insert(0, {repo!r}) import hermes_state as hs fired = False try: hs._ensure_test_isolation(hs._real_platform_state_root() / "state.db") except RuntimeError: fired = True print(json.dumps({{ "armed": hs._running_under_pytest(), "fired": fired, }})) """ def _spawn_probe(env: dict) -> dict: """Run the guard probe in a real child process with exactly *env*.""" proc = subprocess.run( [sys.executable, "-c", _CHILD_PROBE.format(repo=str(REPO_ROOT))], capture_output=True, text=True, env=env, timeout=60, ) assert proc.returncode == 0, proc.stderr return json.loads(proc.stdout.strip().splitlines()[-1]) def _minimal_env(**extra) -> dict: """A rebuilt-from-scratch child env — the residual-bypass pattern.""" env = { "PATH": os.environ.get("PATH", ""), "HOME": os.environ.get("HOME", ""), "SYSTEMROOT": os.environ.get("SYSTEMROOT", ""), # Windows needs it "LOCALAPPDATA": os.environ.get("LOCALAPPDATA", ""), } env = {k: v for k, v in env.items() if v} env.update(extra) return env def test_conftest_exports_the_marker(): """The hermetic conftest must export the marker before tests run.""" assert os.environ.get("HERMES_TEST_ISOLATION"), ( "HERMES_TEST_ISOLATION must be exported by tests/conftest.py so " "subprocess children inherit a test-context signal that survives " "PYTEST_* scrubbing" ) def test_marker_alone_reports_test_context(monkeypatch): monkeypatch.delenv("PYTEST_CURRENT_TEST", raising=False) monkeypatch.delenv("PYTEST_VERSION", raising=False) monkeypatch.setenv("HERMES_TEST_ISOLATION", "/tmp/some-isolation-root") assert hermes_state._running_under_pytest() is True def test_no_signals_reports_production(monkeypatch): """Marker absent + PYTEST_* absent = a real user run; guard must not arm off the env (ancestry may still arm it in a real child — not this seam).""" monkeypatch.delenv("PYTEST_CURRENT_TEST", raising=False) monkeypatch.delenv("PYTEST_VERSION", raising=False) monkeypatch.delenv("HERMES_TEST_ISOLATION", raising=False) assert hermes_state._running_under_pytest() is False def test_child_with_rebuilt_env_keeping_marker_refuses_production_db(): """THE regression: a child whose env was rebuilt from scratch (PYTEST_* stripped, HERMES_HOME lost) but which keeps the marker must still refuse to open the production state.db.""" env = _minimal_env(HERMES_TEST_ISOLATION="/tmp/pytest-isolation-root") result = _spawn_probe(env) assert result["armed"] is True assert result["fired"] is True, ( "guard did not fire in a marker-carrying child aimed at the " "production state.db — the #82770 escape is open again" ) def test_child_bypass_env_disarms_guard_even_with_marker(): """The sanctioned escape hatch for children that genuinely need a real DB: HERMES_STATE_DB_GUARD_BYPASS=1, not marker-stripping.""" env = _minimal_env( HERMES_TEST_ISOLATION="/tmp/pytest-isolation-root", HERMES_STATE_DB_GUARD_BYPASS="1", ) result = _spawn_probe(env) assert result["fired"] is False def test_child_inheriting_full_test_env_refuses_production_db(): """Default inheritance (env=None equivalent): everything rides along.""" result = _spawn_probe(dict(os.environ)) assert result["armed"] is True assert result["fired"] is True