test(issue-17): item-4 helpers run lib.sh in the test's own WORKSPACE_ROOT

_env kept an inherited WORKSPACE_ROOT, so every helper that sources the
wrapper (lib.sh's _init_herdr_isolation) rewrote <that workspace>/.mam/shim/
herdr - another workspace's during the PR #51 review (v4.8.7 review, claude).
Only dropping it is not enough: lib.sh then derives the checkout itself, and
conftest keeps a WORKSPACE_ROOT that names this checkout, so the run would
rewrite the checkout's own shim (the real one when this is the main
checkout). _env now sets WORKSPACE_ROOT to the test's tmp dir.

Test: with a sentinel WORKSPACE_ROOT in the environment, _run_both and a
direct _env run leave the sentinel empty and the checkout's shim untouched,
and the shell sees the tmp dir. With the old _env the sentinel gets
.mam/shim/herdr.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HCGpJXhSZasJLU38ksDghe
This commit is contained in:
GodopuandClaude Opus 5.5 committed 2026-10-08 23:10:35 +09:00
1 parent 6b00b6e06f
commit d198dd50aa
1 file changed
+36 -2
+36 -2
View File
@@ -222,10 +222,13 @@ def _shim(tmp_path: Path) -> Path:
def _env(tmp_path: Path, **extra) -> dict: def _env(tmp_path: Path, **extra) -> dict:
# WORKSPACE_ROOT is this test's tmp_path, never inherited: sourcing the
# wrapper sources lib.sh, which rewrites $WORKSPACE_ROOT/.mam/shim/herdr -
# and unset, lib.sh would derive the checkout itself (Issue #17 follow-up).
env = {k: v for k, v in os.environ.items() env = {k: v for k, v in os.environ.items()
if not k.startswith(("HERDR_", "MAM_", "SKS_")) and k not in ("PYTHONPATH",)} if not k.startswith(("HERDR_", "MAM_", "SKS_")) and k not in ("PYTHONPATH", "WORKSPACE_ROOT")}
env.update(MAM_DELEGATE_JOB_NO_MAIN="1", MAM_ENV_FILE=str(tmp_path / ".mam.env.absent"), HOME=str(tmp_path), env.update(MAM_DELEGATE_JOB_NO_MAIN="1", MAM_ENV_FILE=str(tmp_path / ".mam.env.absent"), HOME=str(tmp_path),
DELEGATE_JOB_LOGS_DIR=str(tmp_path / "logs")) WORKSPACE_ROOT=str(tmp_path), DELEGATE_JOB_LOGS_DIR=str(tmp_path / "logs"))
env.update({k: str(v) for k, v in extra.items() if v is not None}) env.update({k: str(v) for k, v in extra.items() if v is not None})
return env return env
@@ -464,6 +467,37 @@ def test_c_mam_int_and_knob(tmp_path, locale, minimum):
assert outs[0] == outs[1], outs assert outs[0] == outs[1], outs
def _mtime(path: Path):
try:
return path.stat().st_mtime_ns
except OSError:
return None
def test_c_helpers_write_nothing_to_an_inherited_workspace(tmp_path, monkeypatch):
"""v4.8.7 review (claude): _env kept an inherited WORKSPACE_ROOT, so every
helper that sources the wrapper (lib.sh) rewrote <that workspace>/.mam/
shim/herdr - another workspace's, during the PR #51 review. With a sentinel
workspace in the environment, nothing appears under it, the checkout's own
shim is not rewritten either, and the shell sees the test's tmp dir."""
sentinel = tmp_path / "sentinel-ws"
sentinel.mkdir()
monkeypatch.setenv("WORKSPACE_ROOT", str(sentinel))
checkout_shim = REPO_ROOT / ".mam" / "shim" / "herdr"
before = _mtime(checkout_shim)
both = _run_both(tmp_path / "both", 'echo "ws=$WORKSPACE_ROOT"\n', {})
work = tmp_path / "direct"
work.mkdir()
res = subprocess.run(["bash", "-c", f'source "{WRAPPER}"; echo "ws=$WORKSPACE_ROOT"'], capture_output=True,
text=True, cwd=str(work), timeout=60, env=_env(work))
assert sorted(str(p.relative_to(sentinel)) for p in sentinel.rglob("*")) == [], "written into the sentinel"
assert _mtime(checkout_shim) == before, "the checkout's shim was rewritten"
for rc, out, err, _ in both:
assert rc == 0 and "ws=<W>" in out, (rc, out, err)
assert res.returncode == 0 and f"ws={work}" in res.stdout, (res.stdout, res.stderr)
assert (work / ".mam" / "shim" / "herdr").is_file(), "lib.sh wrote its shim into the test's own root"
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
# S - static guard # S - static guard
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------