diff options
| author | Craig Jennings <c@cjennings.net> | 2026-05-06 21:59:52 -0500 |
|---|---|---|
| committer | Craig Jennings <c@cjennings.net> | 2026-05-06 21:59:52 -0500 |
| commit | d81b23ad6b6e437dfe3c338a00a4be39bc555146 (patch) | |
| tree | 2d4b0d7890fd1fc70d81282b81fed2808c28a106 /.ai/scripts/tests/test_cross_agent_discover.py | |
| parent | 201377f57430ef28d02e703a2191434bbee55c75 (diff) | |
| download | rulesets-d81b23ad6b6e437dfe3c338a00a4be39bc555146.tar.gz rulesets-d81b23ad6b6e437dfe3c338a00a4be39bc555146.zip | |
chore(ai): initialize project notes and Claude tooling surfaces
Replace the seed notes.org with project-specific context (layout, install modes, task tracker location, recent inflection point). Bring in the synced template surfaces (protocols, workflows, scripts, references, retrospectives, someday-maybe) as tracked content for this content/documentation project.
Diffstat (limited to '.ai/scripts/tests/test_cross_agent_discover.py')
| -rw-r--r-- | .ai/scripts/tests/test_cross_agent_discover.py | 204 |
1 files changed, 204 insertions, 0 deletions
diff --git a/.ai/scripts/tests/test_cross_agent_discover.py b/.ai/scripts/tests/test_cross_agent_discover.py new file mode 100644 index 0000000..f0d2bb7 --- /dev/null +++ b/.ai/scripts/tests/test_cross_agent_discover.py @@ -0,0 +1,204 @@ +"""Tests for cross-agent-discover (TDD: tests written before implementation).""" + +from __future__ import annotations + +import json +import os +import subprocess +import textwrap +from pathlib import Path + +import pytest + +SCRIPT = Path(__file__).resolve().parent.parent / "cross-agent-comms" / "cross-agent-discover" + + +def _run(args: list[str], env: dict | None = None) -> subprocess.CompletedProcess: + return subprocess.run([str(SCRIPT), *args], capture_output=True, text=True, env=env) + + +@pytest.fixture +def fake_home(tmp_path, monkeypatch): + home = tmp_path / "home" + home.mkdir() + monkeypatch.setenv("HOME", str(home)) + return home + + +def _make_project(home: Path, name: str) -> Path: + proj = home / "projects" / name + (proj / ".ai").mkdir(parents=True) + return proj + + +def _write_peers_toml(home: Path, content: str) -> Path: + cfg = home / ".config" / "cross-agent-comms" + cfg.mkdir(parents=True, exist_ok=True) + peers = cfg / "peers.toml" + peers.write_text(content) + return peers + + +def test_discover_help(fake_home): + result = _run(["--help"], env={**os.environ, "HOME": str(fake_home)}) + assert result.returncode == 0 + assert "discover" in result.stdout.lower() or "enumerate" in result.stdout.lower() + + +def test_discover_local_only_no_projects(fake_home): + """Empty home → reports zero local projects, zero peers.""" + result = _run(["--no-cache"], env={**os.environ, "HOME": str(fake_home)}) + assert result.returncode == 0 + # No crash; mentions local somehow. + assert "local" in result.stdout.lower() or "0 project" in result.stdout.lower() + + +def test_discover_lists_local_projects(fake_home): + _make_project(fake_home, "homelab") + _make_project(fake_home, "career") + _make_project(fake_home, "claude-templates") + result = _run(["--no-cache"], env={**os.environ, "HOME": str(fake_home)}) + assert result.returncode == 0 + assert "homelab" in result.stdout + assert "career" in result.stdout + assert "claude-templates" in result.stdout + + +def test_discover_excludes_dirs_without_ai_subdir(fake_home): + """Directories under ~/projects/ that lack .ai/ are NOT projects.""" + _make_project(fake_home, "real-project") + (fake_home / "projects" / "not-a-project").mkdir(parents=True) + result = _run(["--no-cache"], env={**os.environ, "HOME": str(fake_home)}) + assert result.returncode == 0 + assert "real-project" in result.stdout + assert "not-a-project" not in result.stdout + + +def test_discover_no_peers_toml_just_local(fake_home): + _make_project(fake_home, "homelab") + result = _run(["--no-cache"], env={**os.environ, "HOME": str(fake_home)}) + assert result.returncode == 0 + # No peers section since no toml. + assert "homelab" in result.stdout + + +def test_discover_lists_peers_from_toml(fake_home): + _write_peers_toml(fake_home, textwrap.dedent("""\ + [peers.velox] + host = "velox" + ssh_user = "cjennings" + + [peers.bastion] + host = "bastion.local" + ssh_user = "cjennings" + """)) + _make_project(fake_home, "homelab") + result = _run(["--no-cache"], env={**os.environ, "HOME": str(fake_home)}) + assert result.returncode == 0 + assert "velox" in result.stdout + assert "bastion" in result.stdout + + +def test_discover_malformed_peers_toml_errors_clearly(fake_home): + _write_peers_toml(fake_home, "not valid toml at all = = =") + result = _run(["--no-cache"], env={**os.environ, "HOME": str(fake_home)}) + assert result.returncode != 0 + assert "peers.toml" in result.stderr or "TOML" in result.stderr or "parse" in result.stderr.lower() + + +def test_discover_json_output_schema(fake_home): + _make_project(fake_home, "homelab") + _make_project(fake_home, "career") + _write_peers_toml(fake_home, textwrap.dedent("""\ + [peers.velox] + host = "velox" + """)) + result = _run(["--json", "--no-cache"], env={**os.environ, "HOME": str(fake_home)}) + assert result.returncode == 0 + payload = json.loads(result.stdout) + assert "local" in payload + assert "peers" in payload + assert isinstance(payload["local"], list) + assert isinstance(payload["peers"], list) + assert "homelab" in payload["local"] + assert "career" in payload["local"] + velox = next((p for p in payload["peers"] if p["name"] == "velox"), None) + assert velox is not None + # Reachability is a key — value depends on actual SSH state. + assert "reachable" in velox + + +def test_discover_peer_scope(fake_home): + _write_peers_toml(fake_home, textwrap.dedent("""\ + [peers.velox] + host = "velox" + + [peers.bastion] + host = "bastion.local" + """)) + result = _run(["--peer", "velox", "--no-cache", "--json"], env={**os.environ, "HOME": str(fake_home)}) + assert result.returncode == 0 + payload = json.loads(result.stdout) + peer_names = [p["name"] for p in payload["peers"]] + assert "velox" in peer_names + assert "bastion" not in peer_names + + +def test_discover_unreachable_peer_marked(fake_home): + """A peer with a definitely-unreachable host gets reachable=False.""" + _write_peers_toml(fake_home, textwrap.dedent("""\ + [peers.bogus] + host = "definitely-not-a-real-host.invalid" + ssh_user = "nobody" + """)) + result = _run(["--no-cache", "--json"], env={**os.environ, "HOME": str(fake_home)}, ) + assert result.returncode == 0 + payload = json.loads(result.stdout) + bogus = next((p for p in payload["peers"] if p["name"] == "bogus"), None) + assert bogus is not None + assert bogus["reachable"] is False + + +def test_discover_cache_hit_within_window(fake_home): + """Second invocation within 5 min reads cache (skip the SSH probe).""" + _make_project(fake_home, "homelab") + # First call populates cache. + result1 = _run(["--json"], env={**os.environ, "HOME": str(fake_home)}) + assert result1.returncode == 0 + cache = fake_home / ".cache" / "cross-agent-comms" / "discovery.json" + assert cache.exists() + # Tamper with the cache to a marker only the cache path can produce. + payload = json.loads(cache.read_text()) + payload["_test_marker"] = True + cache.write_text(json.dumps(payload)) + # Second call (no --no-cache) should return the tampered payload. + result2 = _run(["--json"], env={**os.environ, "HOME": str(fake_home)}) + assert result2.returncode == 0 + payload2 = json.loads(result2.stdout) + assert payload2.get("_test_marker") is True + + +def test_discover_no_cache_flag_bypasses(fake_home): + """--no-cache ignores even a fresh cache.""" + _make_project(fake_home, "homelab") + cache_dir = fake_home / ".cache" / "cross-agent-comms" + cache_dir.mkdir(parents=True) + cache_dir.joinpath("discovery.json").write_text(json.dumps({ + "_test_marker": True, "local": [], "peers": [] + })) + result = _run(["--no-cache", "--json"], env={**os.environ, "HOME": str(fake_home)}) + assert result.returncode == 0 + payload = json.loads(result.stdout) + # Cache marker should NOT appear in fresh result. + assert payload.get("_test_marker") is None or payload.get("_test_marker") is False + assert "homelab" in payload["local"] + + +def test_discover_halt_shows_banner(fake_home): + halt = fake_home / ".config" / "cross-agent-comms" / "HALT" + halt.parent.mkdir(parents=True) + halt.write_text("halted") + _make_project(fake_home, "homelab") + result = _run(["--no-cache"], env={**os.environ, "HOME": str(fake_home)}) + assert result.returncode == 0 # discover continues to print under HALT + assert "HALT" in result.stdout |
