1212
1313from __future__ import annotations
1414
15+ import json
1516import os
1617import subprocess
1718import sys
1819from pathlib import Path
1920
2021REPO_ROOT = Path (__file__ ).resolve ().parents [1 ]
2122
22- _PROBE = (
23- "import api.config as c; "
24- "print(c.STATE_DIR)"
25- )
23+ _PROBE = "import api.config as c; print(c.STATE_DIR)"
2624
2725
28- def _state_dir_for_env (** env_overrides ) -> Path :
26+ def _state_dir_for_env (platform_home : Path , ** env_overrides ) -> Path :
2927 """Import api.config in a fresh subprocess under the given env and return
3028 the resolved STATE_DIR it computes. None-valued overrides unset the var."""
3129 env = dict (os .environ )
32- # Start from a clean slate for the two vars under test so the parent
33- # process's pytest values don't bleed in.
30+ # Keep every probe inside fixture-owned platform paths. Inheriting the real
31+ # user home lets an import-only probe reconcile pytest workspace defaults
32+ # into the user's settings.json before it prints STATE_DIR.
33+ env ["HOME" ] = str (platform_home )
34+ env ["USERPROFILE" ] = str (platform_home )
35+ env ["LOCALAPPDATA" ] = str (platform_home / "AppData" / "Local" )
36+ # Start from a clean slate for the vars that can redirect state or trigger
37+ # startup settings reconciliation in the child process.
3438 env .pop ("HERMES_HOME" , None )
39+ env .pop ("HERMES_BASE_HOME" , None )
3540 env .pop ("HERMES_WEBUI_STATE_DIR" , None )
41+ env .pop ("HERMES_WEBUI_TEST_STATE_DIR" , None )
42+ env .pop ("HERMES_WEBUI_DEFAULT_WORKSPACE" , None )
43+ env .pop ("HERMES_CONFIG_PATH" , None )
3644 for key , value in env_overrides .items ():
3745 if value is None :
3846 env .pop (key , None )
@@ -50,33 +58,66 @@ def _state_dir_for_env(**env_overrides) -> Path:
5058 return Path (out .stdout .strip ()).resolve ()
5159
5260
53- def _platform_default_state_dir () -> Path :
61+ def _platform_default_state_dir (platform_home : Path ) -> Path :
5462 """What STATE_DIR resolves to with HERMES_HOME + STATE_DIR both unset."""
55- return _state_dir_for_env ()
63+ return _state_dir_for_env (platform_home )
64+
65+
66+ def test_state_dir_probe_does_not_mutate_platform_default_settings (
67+ tmp_path , monkeypatch
68+ ):
69+ """The subprocess probe must not reconcile pytest defaults into user state."""
70+ platform_home = tmp_path / "platform-home"
71+ if os .name == "nt" :
72+ settings_file = (
73+ platform_home / "AppData" / "Local" / "hermes" / "webui" / "settings.json"
74+ )
75+ else :
76+ settings_file = platform_home / ".hermes" / "webui" / "settings.json"
77+ settings_file .parent .mkdir (parents = True )
78+ persisted_workspace = tmp_path / "persisted-workspace"
79+ persisted_workspace .mkdir ()
80+ original = (
81+ json .dumps ({"default_workspace" : str (persisted_workspace )}) + "\n "
82+ ).encode ()
83+ settings_file .write_bytes (original )
84+
85+ monkeypatch .setenv ("HOME" , str (platform_home ))
86+ monkeypatch .setenv ("USERPROFILE" , str (platform_home ))
87+ monkeypatch .setenv (
88+ "HERMES_WEBUI_DEFAULT_WORKSPACE" , str (tmp_path / "pytest-workspace" )
89+ )
90+
91+ state_dir = _platform_default_state_dir (platform_home )
92+
93+ assert settings_file .read_bytes () == original
94+ assert state_dir == settings_file .parent .resolve ()
5695
5796
5897def test_config_state_dir_defaults_to_hermes_home_webui (tmp_path ):
5998 hermes_home = tmp_path / ".hermes" / "profiles" / "isolated"
6099 hermes_home .mkdir (parents = True )
61100
62- state_dir = _state_dir_for_env (HERMES_HOME = hermes_home )
101+ state_dir = _state_dir_for_env (tmp_path / "platform-home" , HERMES_HOME = hermes_home )
63102 assert state_dir == (hermes_home / "webui" ).resolve ()
64103
65104
66- def test_config_state_dir_unchanged_for_normal_install_hermes_home_unset ():
105+ def test_config_state_dir_unchanged_for_normal_install_hermes_home_unset (tmp_path ):
67106 """Backward-compat: with HERMES_HOME unset, STATE_DIR stays at the platform
68107 default `<~/.hermes>/webui` — a normal install's state must NOT relocate
69108 (the #4449/#4454 state-dir move only affects an explicitly-set HERMES_HOME).
70109
71110 Cross-check: the unset-HERMES_HOME result must NOT equal the result of
72111 pointing HERMES_HOME at an arbitrary other base — i.e. the default is
73112 genuinely the platform home, not whatever the test environment injected."""
74- default = _platform_default_state_dir ()
113+ platform_home = tmp_path / "platform-home"
114+ default = _platform_default_state_dir (platform_home )
75115 assert default .name == "webui"
76116 # Pointing HERMES_HOME elsewhere produces a DIFFERENT dir, proving the unset
77117 # case resolves to the platform default rather than echoing an injected base.
78- elsewhere = _state_dir_for_env (HERMES_HOME = "/tmp/hermes-4449-elsewhere-base" )
79- assert elsewhere == Path ("/tmp/hermes-4449-elsewhere-base/webui" ).resolve ()
118+ elsewhere_base = tmp_path / "elsewhere-base"
119+ elsewhere = _state_dir_for_env (platform_home , HERMES_HOME = elsewhere_base )
120+ assert elsewhere == (elsewhere_base / "webui" ).resolve ()
80121 assert default != elsewhere
81122
82123
@@ -88,6 +129,7 @@ def test_config_state_dir_explicit_override_takes_precedence(tmp_path):
88129 explicit = tmp_path / "custom-state"
89130
90131 state_dir = _state_dir_for_env (
132+ tmp_path / "platform-home" ,
91133 HERMES_HOME = hermes_home ,
92134 HERMES_WEBUI_STATE_DIR = explicit ,
93135 )
0 commit comments