"""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