test(issue-20): issue-15 leak checks judge only their own job ids and session
The onboarding tests compared whole listings of the repo's .mam, so a concurrent run's entries failed them. Session names are now unique per run; the check is that the sandbox's own job ids are absent from every real root (this checkout's and the recorded control plane's) and that no new entry there names the session. cli_defaults also checks that the audit log follows WORKSPACE_ROOT. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Sdga4A8i1iSc8DaaW9iKU2
This commit is contained in:
1 parent
c5844a8e12
commit
a2be70df1b
1 file changed
+59
-16
@@ -16,12 +16,13 @@ import json
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
import uuid
|
||||
from pathlib import Path
|
||||
|
||||
import _mam_state_guard
|
||||
|
||||
REPO_ROOT = Path(__file__).resolve().parent.parent
|
||||
SKILLS = REPO_ROOT / ".agents" / "skills"
|
||||
REAL_JOBS = REPO_ROOT / ".mam" / "jobs"
|
||||
REAL_LOGS = REPO_ROOT / ".mam" / "delegate_job_logs"
|
||||
|
||||
sys.path.insert(0, str(SKILLS))
|
||||
from lib_py.delegation import mqtt_common # noqa: E402
|
||||
@@ -67,8 +68,10 @@ def test_logs_dir_follows_the_same_order(monkeypatch, tmp_path):
|
||||
|
||||
def test_cli_defaults_are_resolved_per_process(monkeypatch, tmp_path):
|
||||
"""registry.py / publish_event.py build their argparse defaults at call
|
||||
time, so a process started with WORKSPACE_ROOT set registers there."""
|
||||
time, so a process started with WORKSPACE_ROOT set registers there - and
|
||||
seeds its audit log there too."""
|
||||
monkeypatch.delenv("MAM_JOBS_DIR", raising=False)
|
||||
monkeypatch.delenv("DELEGATE_JOB_LOGS_DIR", raising=False)
|
||||
env = dict(os.environ, WORKSPACE_ROOT=str(tmp_path), PYTHONPATH=f"{SKILLS}:{os.environ.get('PYTHONPATH', '')}")
|
||||
env.pop("MAM_JOBS_DIR", None)
|
||||
res = subprocess.run(
|
||||
@@ -80,6 +83,7 @@ def test_cli_defaults_are_resolved_per_process(monkeypatch, tmp_path):
|
||||
records = list((tmp_path / ".mam" / "jobs").glob("*.json"))
|
||||
assert len(records) == 1, records
|
||||
assert json.loads(records[0].read_text())["agent_session"] == "herdr:issue15-probe"
|
||||
assert (tmp_path / ".mam" / "delegate_job_logs" / records[0].stem).is_dir()
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -95,26 +99,65 @@ def _create_session_from_repo_root(sandbox: Path, session: str) -> subprocess.Co
|
||||
)
|
||||
|
||||
|
||||
def _assert_registered_in_sandbox_only(sandbox: Path, session: str, before_jobs: set, before_logs: set, res):
|
||||
def _real_state_dirs(recorded: dict) -> list:
|
||||
"""Every real registry and audit-log root this run could leak into: this
|
||||
checkout's .mam and the control plane the ambient binding named (conftest
|
||||
records it before dropping it)."""
|
||||
jobs, logs = _mam_state_guard.state_dirs(_mam_state_guard.watch_roots(REPO_ROOT, recorded), recorded)
|
||||
return jobs + logs
|
||||
|
||||
|
||||
def _entries(dirs: list) -> set:
|
||||
return {(d, name) for d in dirs for name in _listing(Path(d))}
|
||||
|
||||
|
||||
def _unique(prefix: str) -> str:
|
||||
"""A session name no concurrent run shares (Issue #20)."""
|
||||
return f"{prefix}-{uuid.uuid4().hex[:8]}"
|
||||
|
||||
|
||||
def _assert_registered_in_sandbox_only(sandbox: Path, session: str, real_dirs: list, before: set, res):
|
||||
"""Judged on this test's own job ids and session only (Issue #20): live
|
||||
delegations and other test runs may add entries to the real .mam
|
||||
meanwhile, so comparing whole listings gave false failures."""
|
||||
assert res.returncode == 0, res.stderr + res.stdout
|
||||
assert _listing(REAL_JOBS) == before_jobs, "onboarding job leaked into the repo's .mam/jobs"
|
||||
assert _listing(REAL_LOGS) == before_logs, "audit log leaked into the repo's .mam/delegate_job_logs"
|
||||
records = [json.loads(p.read_text()) for p in (sandbox / ".mam" / "jobs").glob("*.json")]
|
||||
assert any(r.get("agent_session") == f"herdr:{session}" for r in records), records
|
||||
records = {p.stem: json.loads(p.read_text()) for p in (sandbox / ".mam" / "jobs").glob("*.json")}
|
||||
own = [jid for jid, r in records.items() if r.get("agent_session") == f"herdr:{session}"]
|
||||
assert own, records
|
||||
for d in real_dirs:
|
||||
for jid in own:
|
||||
assert not (Path(d) / f"{jid}.json").exists(), f"onboarding job {jid} leaked into {d}"
|
||||
assert not (Path(d) / jid).exists(), f"audit log {jid} leaked into {d}"
|
||||
# A job that never reached the sandbox has an id we do not know: look for
|
||||
# the session in whatever appeared in the real roots meanwhile.
|
||||
for d, name in _entries(real_dirs) - before:
|
||||
entry = Path(d) / name
|
||||
meta = entry if name.endswith(".json") else entry / "meta.json"
|
||||
try:
|
||||
session_of = json.loads(meta.read_text()).get("agent_session")
|
||||
except (OSError, ValueError):
|
||||
continue
|
||||
assert session_of != f"herdr:{session}", f"onboarding job for {session} leaked: {entry}"
|
||||
|
||||
|
||||
def test_onboarding_job_stays_in_sandbox_with_explicit_env(mam_sandbox, mock_herdr, mock_agents):
|
||||
before_jobs, before_logs = _listing(REAL_JOBS), _listing(REAL_LOGS)
|
||||
res = _create_session_from_repo_root(mam_sandbox, "issue15-explicit-creator-claude")
|
||||
_assert_registered_in_sandbox_only(mam_sandbox, "issue15-explicit-creator-claude", before_jobs, before_logs, res)
|
||||
def test_onboarding_job_stays_in_sandbox_with_explicit_env(mam_sandbox, mock_herdr, mock_agents,
|
||||
_ambient_control_plane):
|
||||
session = _unique("issue15-explicit-creator-claude")
|
||||
real_dirs = _real_state_dirs(_ambient_control_plane)
|
||||
before = _entries(real_dirs)
|
||||
res = _create_session_from_repo_root(mam_sandbox, session)
|
||||
_assert_registered_in_sandbox_only(mam_sandbox, session, real_dirs, before, res)
|
||||
|
||||
|
||||
def test_onboarding_job_follows_workspace_root_alone(mam_sandbox, mock_herdr, mock_agents, monkeypatch):
|
||||
def test_onboarding_job_follows_workspace_root_alone(mam_sandbox, mock_herdr, mock_agents, monkeypatch,
|
||||
_ambient_control_plane):
|
||||
"""Without the explicit MAM_JOBS_DIR / DELEGATE_JOB_LOGS_DIR overrides the
|
||||
WORKSPACE_ROOT that lib.sh exports must be enough to keep the record out
|
||||
of the repo's registry."""
|
||||
monkeypatch.delenv("MAM_JOBS_DIR", raising=False)
|
||||
monkeypatch.delenv("DELEGATE_JOB_LOGS_DIR", raising=False)
|
||||
before_jobs, before_logs = _listing(REAL_JOBS), _listing(REAL_LOGS)
|
||||
res = _create_session_from_repo_root(mam_sandbox, "issue15-wsroot-creator-claude")
|
||||
_assert_registered_in_sandbox_only(mam_sandbox, "issue15-wsroot-creator-claude", before_jobs, before_logs, res)
|
||||
session = _unique("issue15-wsroot-creator-claude")
|
||||
real_dirs = _real_state_dirs(_ambient_control_plane)
|
||||
before = _entries(real_dirs)
|
||||
res = _create_session_from_repo_root(mam_sandbox, session)
|
||||
_assert_registered_in_sandbox_only(mam_sandbox, session, real_dirs, before, res)
|
||||
Reference in new issue
Block a user