test(issue-17): every test that sources lib.sh does it under its own WORKSPACE_ROOT (review grok, codex)
d198dd5fixed one helper; the same defect was in eleven more modules (reviews grok and codex R1/R2): their shells inherited the process environment, so each `source lib.sh` rewrote <WORKSPACE_ROOT>/.mam/shim/herdr - the checkout's own when the variable is unset or names this checkout (which pytest_configure keeps), another workspace's under --noconftest. - conftest: own_workspace_root, an opt-in fixture setting WORKSPACE_ROOT to <tmp_path>/.mam-test-state/workspace. Not autouse: runner.py and mqtt_common read WORKSPACE_ROOT in-process, and some tests unset it on purpose. mam_sandbox and explicit envs run later and still win. - pytestmark usefixtures("own_workspace_root") in test_version_consistency, test_b19_headless_reconcile_fixes, test_b8_send_keys_verification, test_issue6_fail_closed, test_loop_cli, test_issue5_phase3_loop, test_issue5_phase4_runtime, test_o3_scoped_guard, test_issue25_plan_reuse, test_orc_onboard (their shells inherit os.environ, many with no env at all). - test_orc_onboard (R2): SCRUBBED_ENV is taken at import and no longer carries WORKSPACE_ROOT; every call builds _scrubbed_env(root) with the root of the call (ws / cwd / mam_sandbox, else this test's). - test_workspace_scope (unittest) and test_issue21_worktree_prereqs._mutate pin the root they already create. Tests (test_issue17_workspace_root_isolation.py): W1 copies the checkout and runs one test of each of those modules there with WORKSPACE_ROOT naming the copy - the copy gets no .mam; W2 imports test_orc_onboard under a sentinel root, then calls it under another - the sentinel stays empty. Atd198dd5both fail (the copy gets .mam/shim/herdr; the sentinel gets it). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HCGpJXhSZasJLU38ksDghe
This commit is contained in:
1 parent
d198dd50aa
commit
8b47cc76ee
14 files changed
+176
-16
No files matched your search
+26
-14
@@ -7,19 +7,31 @@ import shutil
|
||||
import pytest
|
||||
from pathlib import Path
|
||||
|
||||
# Issue #17: shells started here inherit a WORKSPACE_ROOT of this test's own
|
||||
# (lib.sh would otherwise rewrite the checkout's .mam/shim/herdr).
|
||||
pytestmark = pytest.mark.usefixtures("own_workspace_root")
|
||||
|
||||
REPO_ROOT = Path(__file__).parent.parent
|
||||
LIB_SH = REPO_ROOT / ".agents" / "skills" / "lib.sh"
|
||||
ONBOARD_SH = REPO_ROOT / ".agents" / "skills" / "multi-agent-mux-orc-onboard" / "scripts" / "orc_onboard.sh"
|
||||
RECONCILE_SH = REPO_ROOT / ".agents" / "skills" / "multi-agent-mux-monitor" / "scripts" / "reconcile.sh"
|
||||
|
||||
SCRUBBED_ENV = dict(os.environ)
|
||||
for k in ["CLAUDE_CODE_SESSION_ID", "ANTIGRAVITY_CONVERSATION_ID", "HERMES_SESSION_ID", "MAM_ORCHESTRATOR_UUIDS"]:
|
||||
for k in ["CLAUDE_CODE_SESSION_ID", "ANTIGRAVITY_CONVERSATION_ID", "HERMES_SESSION_ID", "MAM_ORCHESTRATOR_UUIDS",
|
||||
"WORKSPACE_ROOT"]:
|
||||
SCRUBBED_ENV.pop(k, None)
|
||||
SCRUBBED_ENV["PYTHONPATH"] = f"{LIB_SH.parent}:{SCRUBBED_ENV.get('PYTHONPATH', '')}"
|
||||
|
||||
|
||||
def _scrubbed_env(root=None) -> dict:
|
||||
"""SCRUBBED_ENV (taken at import) with the WORKSPACE_ROOT of now: `root`,
|
||||
else this test's (own_workspace_root / mam_sandbox) - never the one the
|
||||
module was imported under (Issue #17: lib.sh rewrites <it>/.mam/shim/herdr)."""
|
||||
return dict(SCRUBBED_ENV, WORKSPACE_ROOT=str(root or os.environ["WORKSPACE_ROOT"]))
|
||||
|
||||
|
||||
def run_onboard(args, env=None, cwd=None):
|
||||
run_env = dict(SCRUBBED_ENV)
|
||||
run_env = _scrubbed_env(cwd)
|
||||
if cwd:
|
||||
run_env["AGENT_SESSIONS_YAML"] = f"{cwd}/.mam/agent-sessions.yaml"
|
||||
run_env["YAML_PATH"] = f"{cwd}/.mam/agent-sessions.yaml"
|
||||
@@ -31,7 +43,7 @@ def run_onboard(args, env=None, cwd=None):
|
||||
|
||||
|
||||
def run_find_uuid(ws, agent, target="", env=None):
|
||||
run_env = dict(SCRUBBED_ENV)
|
||||
run_env = _scrubbed_env(ws)
|
||||
run_env["AGENT_SESSIONS_YAML"] = f"{ws}/.mam/agent-sessions.yaml"
|
||||
run_env["YAML_PATH"] = f"{ws}/.mam/agent-sessions.yaml"
|
||||
run_env["HOME_DIR"] = ws
|
||||
@@ -44,7 +56,7 @@ def run_find_uuid(ws, agent, target="", env=None):
|
||||
|
||||
|
||||
def run_verify_uuid(ws, agent, uuid, row=None, mode="discover", env=None):
|
||||
run_env = dict(SCRUBBED_ENV)
|
||||
run_env = _scrubbed_env(ws)
|
||||
run_env["AGENT_SESSIONS_YAML"] = f"{ws}/.mam/agent-sessions.yaml"
|
||||
run_env["YAML_PATH"] = f"{ws}/.mam/agent-sessions.yaml"
|
||||
run_env["HOME_DIR"] = ws
|
||||
@@ -62,7 +74,7 @@ def dump_state(mam_sandbox, state):
|
||||
env = {"AGENT_SESSIONS_YAML": yaml_path, "YAML_PATH": yaml_path, "HOME_DIR": str(mam_sandbox)}
|
||||
state_str = json.dumps(state)
|
||||
cmd = f"source '{LIB_SH}' && atomic_dump_yaml '{yaml_path}' <<'PYEOF'\nd.update({state_str})\nPYEOF"
|
||||
subprocess.run(["bash", "-c", cmd], env={**SCRUBBED_ENV, **env}, check=True)
|
||||
subprocess.run(["bash", "-c", cmd], env={**_scrubbed_env(mam_sandbox), **env}, check=True)
|
||||
return env
|
||||
|
||||
|
||||
@@ -307,7 +319,7 @@ def test_o18_autodetect_via_env(mam_sandbox):
|
||||
}
|
||||
# Run inside subshell where command line matches claude
|
||||
cmd = f"export CLAUDE_CODE_SESSION_ID='{orc_uuid}' && exec -a claude bash '{ONBOARD_SH}'"
|
||||
res = subprocess.run(["bash", "-c", cmd], capture_output=True, text=True, env={**SCRUBBED_ENV, **env}, cwd=str(mam_sandbox))
|
||||
res = subprocess.run(["bash", "-c", cmd], capture_output=True, text=True, env={**_scrubbed_env(), **env}, cwd=str(mam_sandbox))
|
||||
assert res.returncode == 0
|
||||
|
||||
res_list = run_onboard(["--list"], env=env, cwd=str(mam_sandbox))
|
||||
@@ -327,7 +339,7 @@ def test_o20_validate_fails_on_non_list_orchestrator_uuids(mam_sandbox):
|
||||
env = {"AGENT_SESSIONS_YAML": str(mam_sandbox / ".mam" / "agent-sessions.yaml"), "YAML_PATH": str(mam_sandbox / ".mam" / "agent-sessions.yaml"), "HOME_DIR": str(mam_sandbox)}
|
||||
state_str = json.dumps(state)
|
||||
cmd = f"source '{LIB_SH}' && atomic_dump_yaml '{env['YAML_PATH']}' <<'PYEOF'\nd.update({state_str})\nPYEOF"
|
||||
res = subprocess.run(["bash", "-c", cmd], env={**SCRUBBED_ENV, **env}, capture_output=True, text=True)
|
||||
res = subprocess.run(["bash", "-c", cmd], env={**_scrubbed_env(), **env}, capture_output=True, text=True)
|
||||
assert res.returncode != 0
|
||||
assert "VALIDATE" in res.stderr or "VALIDATE" in res.stdout
|
||||
|
||||
@@ -339,7 +351,7 @@ def test_o21_validate_fails_on_duplicate_orchestrator_uuids(mam_sandbox):
|
||||
env = {"AGENT_SESSIONS_YAML": str(mam_sandbox / ".mam" / "agent-sessions.yaml"), "YAML_PATH": str(mam_sandbox / ".mam" / "agent-sessions.yaml"), "HOME_DIR": str(mam_sandbox)}
|
||||
state_str = json.dumps(state)
|
||||
cmd = f"source '{LIB_SH}' && atomic_dump_yaml '{env['YAML_PATH']}' <<'PYEOF'\nd.update({state_str})\nPYEOF"
|
||||
res = subprocess.run(["bash", "-c", cmd], env={**SCRUBBED_ENV, **env}, capture_output=True, text=True)
|
||||
res = subprocess.run(["bash", "-c", cmd], env={**_scrubbed_env(), **env}, capture_output=True, text=True)
|
||||
assert res.returncode != 0
|
||||
|
||||
|
||||
@@ -349,7 +361,7 @@ def test_o22_validate_fails_on_empty_string_orchestrator_uuid(mam_sandbox):
|
||||
env = {"AGENT_SESSIONS_YAML": str(mam_sandbox / ".mam" / "agent-sessions.yaml"), "YAML_PATH": str(mam_sandbox / ".mam" / "agent-sessions.yaml"), "HOME_DIR": str(mam_sandbox)}
|
||||
state_str = json.dumps(state)
|
||||
cmd = f"source '{LIB_SH}' && atomic_dump_yaml '{env['YAML_PATH']}' <<'PYEOF'\nd.update({state_str})\nPYEOF"
|
||||
res = subprocess.run(["bash", "-c", cmd], env={**SCRUBBED_ENV, **env}, capture_output=True, text=True)
|
||||
res = subprocess.run(["bash", "-c", cmd], env={**_scrubbed_env(), **env}, capture_output=True, text=True)
|
||||
assert res.returncode != 0
|
||||
|
||||
|
||||
@@ -367,7 +379,7 @@ if h1 == h2:
|
||||
sys.exit(1)
|
||||
PYEOF
|
||||
"""
|
||||
res = subprocess.run(["bash", "-c", cmd], env={**SCRUBBED_ENV, "AGENT_SESSIONS_YAML": yaml_path, "YAML_PATH": yaml_path}, capture_output=True, text=True)
|
||||
res = subprocess.run(["bash", "-c", cmd], env={**_scrubbed_env(), "AGENT_SESSIONS_YAML": yaml_path, "YAML_PATH": yaml_path}, capture_output=True, text=True)
|
||||
assert res.returncode == 0
|
||||
|
||||
|
||||
@@ -438,7 +450,7 @@ def test_o27_ignore_different_family_env_variable(mam_sandbox):
|
||||
"ANTIGRAVITY_CONVERSATION_ID": "72d2d251-5a06-486b-92e5-7e46a7a80d2e"
|
||||
}
|
||||
cmd = f"export ANTIGRAVITY_CONVERSATION_ID='72d2d251-5a06-486b-92e5-7e46a7a80d2e' && exec -a claude bash '{ONBOARD_SH}'"
|
||||
res = subprocess.run(["bash", "-c", cmd], capture_output=True, text=True, env={**SCRUBBED_ENV, **env}, cwd=str(mam_sandbox))
|
||||
res = subprocess.run(["bash", "-c", cmd], capture_output=True, text=True, env={**_scrubbed_env(), **env}, cwd=str(mam_sandbox))
|
||||
assert res.returncode == 3
|
||||
|
||||
|
||||
@@ -451,7 +463,7 @@ def test_o28_use_family_matched_env_variable(mam_sandbox):
|
||||
"ANTIGRAVITY_CONVERSATION_ID": orc_uuid
|
||||
}
|
||||
cmd = f"export ANTIGRAVITY_CONVERSATION_ID='{orc_uuid}' && exec -a agy bash '{ONBOARD_SH}'"
|
||||
res = subprocess.run(["bash", "-c", cmd], capture_output=True, text=True, env={**SCRUBBED_ENV, **env}, cwd=str(mam_sandbox))
|
||||
res = subprocess.run(["bash", "-c", cmd], capture_output=True, text=True, env={**_scrubbed_env(), **env}, cwd=str(mam_sandbox))
|
||||
assert res.returncode == 0
|
||||
|
||||
res_list = run_onboard(["--list"], env=env, cwd=str(mam_sandbox))
|
||||
@@ -467,7 +479,7 @@ def test_o29_fresh_claude_session_detection(mam_sandbox):
|
||||
"CLAUDE_CODE_SESSION_ID": orc_uuid
|
||||
}
|
||||
cmd = f"export CLAUDE_CODE_SESSION_ID='{orc_uuid}' && exec -a claude bash '{ONBOARD_SH}'"
|
||||
res = subprocess.run(["bash", "-c", cmd], capture_output=True, text=True, env={**SCRUBBED_ENV, **env}, cwd=str(mam_sandbox))
|
||||
res = subprocess.run(["bash", "-c", cmd], capture_output=True, text=True, env={**_scrubbed_env(), **env}, cwd=str(mam_sandbox))
|
||||
assert res.returncode == 0
|
||||
|
||||
res_list = run_onboard(["--list"], env=env, cwd=str(mam_sandbox))
|
||||
@@ -484,7 +496,7 @@ def test_o30_argv_precedes_env(mam_sandbox):
|
||||
"CLAUDE_CODE_SESSION_ID": env_uuid
|
||||
}
|
||||
cmd = f"export CLAUDE_CODE_SESSION_ID='{env_uuid}' && exec -a claude bash '{ONBOARD_SH}' --uuid {argv_uuid}"
|
||||
res = subprocess.run(["bash", "-c", cmd], capture_output=True, text=True, env={**SCRUBBED_ENV, **env}, cwd=str(mam_sandbox))
|
||||
res = subprocess.run(["bash", "-c", cmd], capture_output=True, text=True, env={**_scrubbed_env(), **env}, cwd=str(mam_sandbox))
|
||||
assert res.returncode == 0
|
||||
|
||||
res_list = run_onboard(["--list"], env=env, cwd=str(mam_sandbox))
|
||||
|
||||
Reference in new issue
Block a user