From 46ea4cc1e4f664ca35711c05c3fa71258e169697 Mon Sep 17 00:00:00 2001 From: Godopu Date: Tue, 15 Sep 2026 02:02:52 +0000 Subject: [PATCH] test(issue-20): state isolation regression tests S1/S2 per-test defaults and the mam_sandbox override, S3 submit with WORKSPACE_ROOT on a checkout leaves its .mam alone, S6 a nested pytest started with a pane's binding sees none of it (import-time copy included) and leaves the target untouched, S7 lib.sh takes the session registry from a test's WORKSPACE_ROOT. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01Sdga4A8i1iSc8DaaW9iKU2 --- tests/test_issue20_state_isolation.py | 99 +++++++++++++++++++++++++++ 1 file changed, 99 insertions(+) diff --git a/tests/test_issue20_state_isolation.py b/tests/test_issue20_state_isolation.py index b2fbef2..128bf38 100644 --- a/tests/test_issue20_state_isolation.py +++ b/tests/test_issue20_state_isolation.py @@ -101,3 +101,102 @@ def test_s5_watch_roots_follow_the_recorded_binding(tmp_path): rec = {"MAM_JOBS_DIR": str(odd)} jobs, logs = guard.state_dirs(guard.watch_roots(repo, rec), rec) assert str(odd.resolve()) in jobs and str(odd.resolve()) not in logs + + +# --------------------------------------------------------------------------- +# per-test defaults and the dropped binding (S1-S3, S6, S7) +# --------------------------------------------------------------------------- +# Copied at import, like test_orc_onboard.SCRUBBED_ENV: the binding has to be +# gone before collection, not merely before the first test (S6). +_IMPORT_ENV = dict(os.environ) +PROBE_ENV = "MAM_ISSUE20_PROBE" +BINDING = ("WORKSPACE_ROOT", "AGENT_SESSIONS_YAML", "MAM_JOBS_DIR", "DELEGATE_JOB_LOGS_DIR") + + +def _tree(root: Path) -> dict: + return {str(p.relative_to(root)): hashlib.sha256(p.read_bytes()).hexdigest() if p.is_file() else None + for p in sorted(root.rglob("*"))} + + +def _binding(root: Path) -> dict: + """The four variables a worktree creator's pane carries, aimed at `root`.""" + mam = root / ".mam" + return {"WORKSPACE_ROOT": str(root), "AGENT_SESSIONS_YAML": str(mam / "agent-sessions.yaml"), + "MAM_JOBS_DIR": str(mam / "jobs"), "DELEGATE_JOB_LOGS_DIR": str(mam / "delegate_job_logs")} + + +def test_s1_every_test_defaults_to_state_dirs_under_tmp_path(tmp_path, monkeypatch): + from lib_py.delegation import mqtt_common + + state = tmp_path / ".mam-test-state" + assert os.environ["MAM_JOBS_DIR"] == str(state / "jobs") + assert os.environ["DELEGATE_JOB_LOGS_DIR"] == str(state / "delegate_job_logs") + # a WORKSPACE_ROOT on a checkout with a .mam no longer decides where state goes + monkeypatch.setenv("WORKSPACE_ROOT", str(_fake_control_plane(tmp_path / "repo"))) + assert mqtt_common.default_registry_dir() == str(state / "jobs") + assert mqtt_common.get_logs_dir() == str(state / "delegate_job_logs") + + +def test_s2_mam_sandbox_overrides_the_default(mam_sandbox): + assert os.environ["MAM_JOBS_DIR"] == str(mam_sandbox / ".mam" / "jobs") + assert os.environ["DELEGATE_JOB_LOGS_DIR"] == str(mam_sandbox / ".mam" / "delegate_job_logs") + + +def test_s3_submit_with_workspace_root_on_a_checkout_leaves_its_mam_alone(tmp_path): + """The issue's acceptance check in small: WORKSPACE_ROOT names a checkout + with a real-looking .mam and the test sets nothing else.""" + repo = _fake_control_plane(tmp_path / "repo", sessions=("creator-real",)) + before = _tree(repo) + env = dict(os.environ, WORKSPACE_ROOT=str(repo), MAM_ENV_FILE=str(tmp_path / ".mam.env.absent"), + PYTHONPATH=f"{SKILLS}:{os.environ.get('PYTHONPATH', '')}") + res = subprocess.run( + [sys.executable, "-m", "lib_py.delegation.cli", "submit", "--prompt", "issue 20 probe", + "--agent", "claude-code", "--agent-session", "herdr:issue20-probe"], + capture_output=True, text=True, env=env, cwd=str(REPO_ROOT), timeout=60, + ) + assert res.returncode == 0, res.stderr + state = tmp_path / ".mam-test-state" + records = list((state / "jobs").glob("*.json")) + assert len(records) == 1, records + assert (state / "delegate_job_logs" / records[0].stem).is_dir() + assert _tree(repo) == before + + +def test_s6_a_pane_binding_is_dropped_before_collection(tmp_path): + """A pytest started with a worktree creator pane's binding - all four at + another checkout F - sees none of it, not even in a module-level copy of + os.environ, and leaves F untouched.""" + fake_main = _fake_control_plane(tmp_path / "main", sessions=("creator-real",)) + before = _tree(fake_main) + env = dict(os.environ, **_binding(fake_main), **{PROBE_ENV: "1"}) + # An explicit basetemp: the inner run must not clean up the outer one's. + res = subprocess.run( + [sys.executable, "-m", "pytest", f"{__file__}::test_s6_probe", "-q", "-p", "no:cacheprovider", + f"--basetemp={tmp_path / 'inner'}"], + capture_output=True, text=True, env=env, cwd=str(REPO_ROOT), timeout=300, + ) + assert res.returncode == 0, res.stdout[-4000:] + res.stderr[-2000:] + assert "1 passed" in res.stdout, res.stdout[-2000:] + assert _tree(fake_main) == before + + +@pytest.mark.skipif(not os.environ.get(PROBE_ENV), reason="the inner half of S6 (nested pytest) only") +def test_s6_probe(tmp_path): + assert not [k for k in BINDING if k in _IMPORT_ENV], {k: _IMPORT_ENV[k] for k in BINDING if k in _IMPORT_ENV} + assert "AGENT_SESSIONS_YAML" not in os.environ + assert "WORKSPACE_ROOT" not in os.environ # it named another checkout + state = tmp_path / ".mam-test-state" + assert os.environ["MAM_JOBS_DIR"] == str(state / "jobs") + assert os.environ["DELEGATE_JOB_LOGS_DIR"] == str(state / "delegate_job_logs") + + +def test_s7_lib_sh_takes_the_session_registry_from_a_tests_workspace_root(tmp_path): + """An ambient AGENT_SESSIONS_YAML (a worktree pane's) would win over the + test's WORKSPACE_ROOT in lib.sh; conftest dropped it before collection.""" + assert "AGENT_SESSIONS_YAML" not in os.environ + repo = tmp_path / "repo" + repo.mkdir() + script = f'source "{SKILLS / "lib.sh"}" >/dev/null 2>&1; printf %s "$AGENT_SESSIONS_YAML"' + res = subprocess.run(["bash", "-c", script], capture_output=True, text=True, + env=dict(os.environ, WORKSPACE_ROOT=str(repo)), timeout=60) + assert res.stdout == str(repo / ".mam" / "agent-sessions.yaml"), (res.stdout, res.stderr)