diff --git a/tests/test_issue45_restore.py b/tests/test_issue45_restore.py index 66eb254..188e722 100644 --- a/tests/test_issue45_restore.py +++ b/tests/test_issue45_restore.py @@ -157,10 +157,22 @@ class Rig: return self.argv_log.exists() and f"--resume {session_id}" in self.argv_log.read_text() +def _process_env(pid: int) -> list: + """A process's environment as KEY=VALUE strings: /proc on Linux; on macOS + `ps -E` appends it to the command line, so the entries are matched there.""" + proc = Path(f"/proc/{pid}/environ") + if proc.exists(): + return [e.decode(errors="replace") for e in proc.read_bytes().split(b"\0") if e] + out = subprocess.run(["ps", "-E", "-ww", "-o", "command=", "-p", str(pid)], + capture_output=True, text=True, timeout=10).stdout + return out.split() + + @pytest.fixture def rig(mam_sandbox, monkeypatch): # A pytest tmp_path is too long for a unix socket path under it. - root = Path(tempfile.mkdtemp(prefix="m45", dir="/tmp")) + # Resolved: on macOS /tmp is a link to /private/tmp and herdr reports real paths. + root = Path(tempfile.mkdtemp(prefix="m45", dir="/tmp")).resolve() session = f"mamt45-{os.getpid()}-{uuid.uuid4().hex[:4]}" for k in SCOPE_ENV: monkeypatch.delenv(k, raising=False) @@ -224,8 +236,8 @@ def test_a_mam_server_does_not_relaunch_saved_agents(rig, user_session_table): info = rig.hd("pane", "process-info", "--pane", pane["pane_id"])["process_info"] assert info["foreground_process_group_id"] == info["shell_pid"], info # and it got there through HERDR_CONFIG_PATH - environ = Path(f"/proc/{rig.server_pids()[0]}/environ").read_bytes().split(b"\0") - assert f"HERDR_CONFIG_PATH={rig.user_cfg.parent / herdr_config.OUTPUT_NAME}".encode() in environ + want = f"HERDR_CONFIG_PATH={rig.user_cfg.parent / herdr_config.OUTPUT_NAME}" + assert want in _process_env(rig.server_pids()[0]), want # Positive control: the same saved layout under a plain server relaunches # the agent, so the "not resumed" above is not vacuous. diff --git a/tests/test_issue9_server_env.py b/tests/test_issue9_server_env.py index 9edca48..cc8e7c7 100644 --- a/tests/test_issue9_server_env.py +++ b/tests/test_issue9_server_env.py @@ -154,7 +154,6 @@ def test_bootstrap_hands_the_server_a_no_restore_config(mam_sandbox): """Issue #45: the server starts with HERDR_CONFIG_PATH = the user's herdr config plus [session] resume_agents_on_restore = false (that herdr obeys it is test_issue45_restore.py, on a real server).""" - import tomllib env = _install_fake_herdr(mam_sandbox) user_cfg = mam_sandbox / ".config" / "herdr" / "config.toml" user_cfg.parent.mkdir(parents=True) @@ -167,8 +166,21 @@ def test_bootstrap_hands_the_server_a_no_restore_config(mam_sandbox): server_env = json.loads(Path(env["FAKE_HERDR_ENV_DUMP"]).read_text()) assert server_env.get("HERDR_CONFIG_PATH") == str(user_cfg.parent / "mam-server-config.toml") assert "MAM_SERVER_CFG" not in server_env - cfg = tomllib.loads(Path(server_env["HERDR_CONFIG_PATH"]).read_text()) - assert cfg == {"ui": {"confirm_close": False}, "session": {"resume_agents_on_restore": False}} + # The bootstrap builds the file with the python3 on PATH; parse it with that + # same python3. Without tomllib there (Python < 3.11, macOS's 3.9) the file is + # the key-only config and the bootstrap warns that the other settings are gone. + out = Path(server_env["HERDR_CONFIG_PATH"]) + parse = subprocess.run(["python3", "-c", "import json, sys, tomllib; " + "print(json.dumps(tomllib.loads(open(sys.argv[1]).read())))", str(out)], + capture_output=True, text=True, env=env, timeout=30) + if parse.returncode == 0: + assert json.loads(parse.stdout) == {"ui": {"confirm_close": False}, + "session": {"resume_agents_on_restore": False}} + else: + assert "No module named 'tomllib'" in parse.stderr, parse.stderr + text = out.read_text() + assert "resume_agents_on_restore = false" in text and "confirm_close" not in text, text + assert "no tomllib" in res.stderr, res.stderr def test_no_autostart_skips_bootstrap_but_still_routes_the_call(mam_sandbox):