From 26fb1c4130ada291608aded764242282f4527537 Mon Sep 17 00:00:00 2001 From: Godopu Date: Fri, 16 Oct 2026 03:16:36 +0000 Subject: [PATCH] test(issue31): keep every claude tmp root candidate inside the test The adapter's fixed /tmp fallback is now a module constant. The new tests point it, TMPDIR, TMP and TEMP at the test's own dirs, in the test process and (through a sitecustomize on PYTHONPATH) in every python a test starts, so no test lists the real /tmp/claude- any more (Mac review, PR #59). Co-Authored-By: Claude Opus 5.5 --- .../skills/lib_py/agents/adapters/claude.py | 8 +++- tests/test_issue31_purge_residual.py | 37 +++++++++++++++++-- 2 files changed, 40 insertions(+), 5 deletions(-) diff --git a/.agents/skills/lib_py/agents/adapters/claude.py b/.agents/skills/lib_py/agents/adapters/claude.py index 062411d..3bec452 100644 --- a/.agents/skills/lib_py/agents/adapters/claude.py +++ b/.agents/skills/lib_py/agents/adapters/claude.py @@ -22,6 +22,10 @@ def _projects_dirs(ctx: DiscoveryContext) -> list: return dirs +# Node's os.tmpdir() when no TMPDIR/TMP/TEMP is set; tests point it elsewhere. +_SYSTEM_TMP = "/tmp" + + def _tmp_roots() -> list: """The per-uid temp roots claude may have used: ${CLAUDE_CODE_TMPDIR:- os.tmpdir()}/claude-. A sandboxed tool shell sets CLAUDE_CODE_TMPDIR @@ -36,9 +40,9 @@ def _tmp_roots() -> list: cands.append(override) # Node's os.tmpdir() on POSIX, not tempfile.gettempdir() (which probes # writability and falls back to the cwd). - tmp = os.environ.get("TMPDIR") or os.environ.get("TMP") or os.environ.get("TEMP") or "/tmp" + tmp = os.environ.get("TMPDIR") or os.environ.get("TMP") or os.environ.get("TEMP") or _SYSTEM_TMP cands.append(os.path.join(tmp.rstrip("/") or "/", name)) - cands.append(os.path.join("/tmp", name)) + cands.append(os.path.join(_SYSTEM_TMP, name)) roots, seen = [], set() for c in cands: real = os.path.realpath(c) diff --git a/tests/test_issue31_purge_residual.py b/tests/test_issue31_purge_residual.py index abee332..315a74a 100644 --- a/tests/test_issue31_purge_residual.py +++ b/tests/test_issue31_purge_residual.py @@ -47,6 +47,29 @@ def _fresh_id() -> str: return ident +@pytest.fixture(autouse=True) +def own_tmp_roots(tmp_path, monkeypatch): + """Every tmp root the claude adapter may look in stays inside the test: + TMP/TEMP/TMPDIR and the fixed /tmp fallback, here and in every python a + test starts (a sitecustomize on PYTHONPATH sets the fallback there).""" + from lib_py.agents.adapters import claude as claude_mod + sys_tmp, tmpdir, shim = tmp_path / "systmp", tmp_path / "tmpdir", tmp_path / "pyshim" + for d in (sys_tmp, tmpdir, shim): + d.mkdir() + monkeypatch.setattr(claude_mod, "_SYSTEM_TMP", str(sys_tmp)) + monkeypatch.setenv("TMPDIR", str(tmpdir)) + monkeypatch.delenv("TMP", raising=False) + monkeypatch.delenv("TEMP", raising=False) + (shim / "sitecustomize.py").write_text( + "try:\n" + " from lib_py.agents.adapters import claude\n" + f" claude._SYSTEM_TMP = {str(sys_tmp)!r}\n" + "except Exception:\n" + " pass\n") + monkeypatch.setenv("PYTHONPATH", f"{shim}:{os.environ.get('PYTHONPATH', '')}") + return sys_tmp + + @pytest.fixture def conv() -> str: return _fresh_id() @@ -411,14 +434,14 @@ def test_a_job_dir_that_cannot_be_emptied_keeps_its_state_json(home, tmp_path, m assert adapter.residual_artifacts(conv, ctx) == [] -def test_claude_tmp_root_rules(tmp_path, monkeypatch): +def test_claude_tmp_root_rules(tmp_path, monkeypatch, own_tmp_roots): from lib_py.agents.adapters.claude import _tmp_roots name = f"claude-{UID}" x, y, z = tmp_path / "x", tmp_path / "y", tmp_path / "z" - for d in (x / name, y / name, z / name): + for d in (x / name, y / name, z / name, own_tmp_roots / name): d.mkdir(parents=True) real = lambda roots: [os.path.realpath(r) for r in roots] # noqa: E731 - sys_tmp = [os.path.realpath(f"/tmp/{name}")] if os.path.isdir(f"/tmp/{name}") else [] + sys_tmp = [os.path.realpath(str(own_tmp_roots / name))] monkeypatch.setenv("CLAUDE_CODE_TMPDIR", str(x)) monkeypatch.setenv("TMPDIR", str(z) + "/") @@ -438,6 +461,14 @@ def test_claude_tmp_root_rules(tmp_path, monkeypatch): assert real(_tmp_roots()) == [str(z / name)] + sys_tmp +def test_tests_never_reach_the_real_tmp_root(tmp_path, own_tmp_roots): + from lib_py.agents.adapters.claude import _tmp_roots + assert all(os.path.realpath(r).startswith(os.path.realpath(str(tmp_path))) for r in _tmp_roots()) + res = subprocess.run([sys.executable, "-c", "from lib_py.agents.adapters import claude; print(claude._SYSTEM_TMP)"], + capture_output=True, text=True, env=_env(), timeout=30) + assert res.stdout.strip() == str(own_tmp_roots), res.stdout + res.stderr + + # --------------------------------------------------------------------------- # in-use CLI and the stop gate # ---------------------------------------------------------------------------