Skip to content

Commit 2c472b3

Browse files
aponamarevclaude
andcommitted
refactor(code): consolidate Tier 0 discover-repos tests into scenario harness
Replace six near-identical Tier 0 test functions with a single parametrized test driven by a declarative Tier0Scenario registry (RepoSpec + PeerExpect dataclasses). Adding a new case is now one registry entry instead of a new test function with duplicated setup. Work in progress — no version bump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent e560112 commit 2c472b3

1 file changed

Lines changed: 155 additions & 89 deletions

File tree

plugins/code/tools/python/test_discover_repos.py

Lines changed: 155 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,11 @@
44
import json
55
import os
66
import subprocess
7+
from dataclasses import dataclass, field
78
from pathlib import Path
89

10+
import pytest
11+
912
SCRIPT_PATH = Path(__file__).resolve().parent.parent.parent / "scripts" / "discover-repos.sh"
1013

1114

@@ -88,97 +91,160 @@ def _make_repo(parent: Path, name: str, identity: dict | None = None) -> Path:
8891
return repo
8992

9093

91-
def test_tier0_add_dir_appears_in_peers(tmp_path: Path) -> None:
92-
"""A path in CLOSEDLOOP_ADD_DIRS should appear in peers with discoveryMethod add_dir."""
93-
current = _make_repo(tmp_path, "current")
94-
extra = _make_repo(tmp_path, "extra", {"name": "extra-svc", "type": "service"})
95-
96-
result = _run_discover_with_env(current, {"CLOSEDLOOP_ADD_DIRS": str(extra)})
97-
98-
assert result.returncode == 0, result.stderr
99-
payload = json.loads(result.stdout)
100-
peer_paths = {p["path"] for p in payload["peers"]}
101-
assert str(extra) in peer_paths
102-
103-
extra_peer = next(p for p in payload["peers"] if p["path"] == str(extra))
104-
assert extra_peer["discoveryMethod"] == "add_dir"
105-
assert extra_peer["name"] == "extra-svc"
106-
assert extra_peer["type"] == "service"
107-
108-
109-
def test_tier0_add_dir_falls_back_to_basename_without_identity(tmp_path: Path) -> None:
110-
"""Tier 0 peer with no identity file should use the directory basename as name."""
111-
current = _make_repo(tmp_path, "current")
112-
anon = _make_repo(tmp_path, "my-anon-repo")
113-
114-
result = _run_discover_with_env(current, {"CLOSEDLOOP_ADD_DIRS": str(anon)})
115-
116-
assert result.returncode == 0, result.stderr
117-
payload = json.loads(result.stdout)
118-
peer = next((p for p in payload["peers"] if p["path"] == str(anon)), None)
119-
assert peer is not None, f"Expected peer for {anon}, got {payload['peers']}"
120-
assert peer["name"] == "my-anon-repo"
121-
122-
123-
def test_tier0_multiple_add_dirs_pipe_separated(tmp_path: Path) -> None:
124-
"""Multiple pipe-separated paths in CLOSEDLOOP_ADD_DIRS should all appear as peers."""
125-
current = _make_repo(tmp_path, "current")
126-
repo_a = _make_repo(tmp_path, "repo-a")
127-
repo_b = _make_repo(tmp_path, "repo-b")
128-
129-
add_dirs = f"{repo_a}|{repo_b}"
130-
result = _run_discover_with_env(current, {"CLOSEDLOOP_ADD_DIRS": add_dirs})
131-
132-
assert result.returncode == 0, result.stderr
133-
payload = json.loads(result.stdout)
134-
peer_paths = {p["path"] for p in payload["peers"]}
135-
assert str(repo_a) in peer_paths
136-
assert str(repo_b) in peer_paths
137-
138-
139-
def test_tier0_skips_current_repo(tmp_path: Path) -> None:
140-
"""A CLOSEDLOOP_ADD_DIRS entry equal to the current repo path should be skipped."""
141-
current = _make_repo(tmp_path, "current")
142-
143-
result = _run_discover_with_env(current, {"CLOSEDLOOP_ADD_DIRS": str(current)})
144-
145-
assert result.returncode == 0, result.stderr
146-
payload = json.loads(result.stdout)
147-
peer_paths = [p["path"] for p in payload["peers"]]
148-
assert str(current) not in peer_paths, f"Current repo should not appear in peers: {peer_paths}"
149-
150-
151-
def test_tier0_deduplicates_with_tier2_sibling_scan(tmp_path: Path) -> None:
152-
"""A sibling that is also in CLOSEDLOOP_ADD_DIRS should appear only once in peers."""
153-
workspace = tmp_path / "workspace"
154-
current = _make_repo(workspace, "current", {"name": "current", "type": "service"})
155-
sibling = _make_repo(
156-
workspace, "sibling-svc", {"name": "sibling-svc", "type": "library", "discoverable": True}
157-
)
94+
# ---------------------------------------------------------------------------
95+
# Tier 0 harness: scenarios are declarative — one test drives them all.
96+
#
97+
# Each Tier0Scenario builds a set of repos under a temp dir, runs
98+
# discover-repos.sh with CLOSEDLOOP_ADD_DIRS derived from scenario keys, and
99+
# validates the peer list against declarative PeerExpect entries.
100+
# ---------------------------------------------------------------------------
158101

159-
# The sibling is both a Tier 0 add-dir AND a Tier 2 sibling
160-
result = _run_discover_with_env(current, {"CLOSEDLOOP_ADD_DIRS": str(sibling)})
161102

103+
# Sentinel used in `add_dir_keys` to reference the current repo's own path.
104+
_CURRENT = "__current__"
105+
106+
107+
@dataclass(frozen=True)
108+
class RepoSpec:
109+
"""Declarative description of a repo to create on disk for a scenario."""
110+
111+
key: str # identifier used to reference the repo within the scenario
112+
dirname: str # directory name under the scenario root
113+
identity: dict | None = None # contents of .closedloop-ai/.repo-identity.json, or None to skip
114+
is_current: bool = False # exactly one RepoSpec per scenario must set this
115+
116+
117+
@dataclass(frozen=True)
118+
class PeerExpect:
119+
"""Declarative assertion over a peer entry in the discover-repos.sh output."""
120+
121+
key: str # references a RepoSpec.key in the same scenario
122+
count: int = 1 # expected number of peer entries with this repo's path
123+
discovery_method: str | None = None
124+
name: str | None = None
125+
type: str | None = None
126+
127+
128+
@dataclass(frozen=True)
129+
class Tier0Scenario:
130+
id: str
131+
repos: tuple[RepoSpec, ...]
132+
add_dir_keys: tuple[str, ...] # repo keys (or _CURRENT) to join into CLOSEDLOOP_ADD_DIRS
133+
workspace_subdir: bool = False # place repos under tmp_path/workspace/ (enables Tier 2 sibling scan)
134+
expect_peers: tuple[PeerExpect, ...] = field(default_factory=tuple)
135+
forbidden_keys: tuple[str, ...] = field(default_factory=tuple) # repo keys that must NOT appear as peers
136+
137+
138+
TIER0_SCENARIOS: tuple[Tier0Scenario, ...] = (
139+
Tier0Scenario(
140+
id="add_dir_appears_in_peers",
141+
repos=(
142+
RepoSpec("current", "current", is_current=True),
143+
RepoSpec("extra", "extra", identity={"name": "extra-svc", "type": "service"}),
144+
),
145+
add_dir_keys=("extra",),
146+
expect_peers=(
147+
PeerExpect("extra", discovery_method="add_dir", name="extra-svc", type="service"),
148+
),
149+
),
150+
Tier0Scenario(
151+
id="basename_fallback_without_identity",
152+
repos=(
153+
RepoSpec("current", "current", is_current=True),
154+
RepoSpec("anon", "my-anon-repo"), # no identity → name falls back to basename
155+
),
156+
add_dir_keys=("anon",),
157+
expect_peers=(PeerExpect("anon", name="my-anon-repo"),),
158+
),
159+
Tier0Scenario(
160+
id="multiple_add_dirs_pipe_separated",
161+
repos=(
162+
RepoSpec("current", "current", is_current=True),
163+
RepoSpec("a", "repo-a"),
164+
RepoSpec("b", "repo-b"),
165+
),
166+
add_dir_keys=("a", "b"),
167+
expect_peers=(PeerExpect("a"), PeerExpect("b")),
168+
),
169+
Tier0Scenario(
170+
id="skips_current_repo",
171+
repos=(RepoSpec("current", "current", is_current=True),),
172+
add_dir_keys=(_CURRENT,),
173+
forbidden_keys=("current",),
174+
),
175+
# A sibling that is ALSO listed in CLOSEDLOOP_ADD_DIRS must appear exactly
176+
# once AND be marked `add_dir` (Tier 0 wins over Tier 2 sibling scan).
177+
Tier0Scenario(
178+
id="add_dir_wins_over_sibling_scan",
179+
workspace_subdir=True,
180+
repos=(
181+
RepoSpec("current", "current", identity={"name": "current", "type": "service"}, is_current=True),
182+
RepoSpec(
183+
"sibling",
184+
"sibling-svc",
185+
identity={"name": "sibling-svc", "type": "library", "discoverable": True},
186+
),
187+
),
188+
add_dir_keys=("sibling",),
189+
expect_peers=(PeerExpect("sibling", count=1, discovery_method="add_dir"),),
190+
),
191+
)
192+
193+
194+
@pytest.mark.parametrize("scenario", TIER0_SCENARIOS, ids=lambda s: s.id)
195+
def test_tier0_add_dirs(tmp_path: Path, scenario: Tier0Scenario) -> None:
196+
"""Drives every Tier 0 scenario through a single harness.
197+
198+
Build repos, invoke discover-repos.sh with the scenario's CLOSEDLOOP_ADD_DIRS,
199+
then validate peer count and per-field attributes declaratively.
200+
"""
201+
# 1. Materialize repos on disk
202+
root = tmp_path / "workspace" if scenario.workspace_subdir else tmp_path
203+
paths: dict[str, Path] = {
204+
spec.key: _make_repo(root, spec.dirname, spec.identity) for spec in scenario.repos
205+
}
206+
current_specs = [s for s in scenario.repos if s.is_current]
207+
assert len(current_specs) == 1, f"Scenario {scenario.id!r} must declare exactly one current repo"
208+
current_path = paths[current_specs[0].key]
209+
210+
# 2. Build CLOSEDLOOP_ADD_DIRS, resolving _CURRENT sentinel against the current repo
211+
def _resolve(key: str) -> Path:
212+
return current_path if key == _CURRENT else paths[key]
213+
214+
add_dirs = "|".join(str(_resolve(k)) for k in scenario.add_dir_keys)
215+
216+
# 3. Invoke the script
217+
result = _run_discover_with_env(current_path, {"CLOSEDLOOP_ADD_DIRS": add_dirs})
162218
assert result.returncode == 0, result.stderr
163219
payload = json.loads(result.stdout)
164-
paths = [p["path"] for p in payload["peers"]]
165-
assert paths.count(str(sibling)) == 1, (
166-
f"Sibling should appear exactly once; got peers: {payload['peers']}"
167-
)
168-
169-
170-
def test_tier0_peer_marked_as_add_dir_not_sibling_scan(tmp_path: Path) -> None:
171-
"""When a sibling is in Tier 0, the peer's discoveryMethod must be 'add_dir', not sibling_scan."""
172-
workspace = tmp_path / "workspace"
173-
current = _make_repo(workspace, "current", {"name": "current", "type": "service"})
174-
sibling = _make_repo(
175-
workspace, "shared-lib", {"name": "shared-lib", "type": "library", "discoverable": True}
176-
)
177-
178-
result = _run_discover_with_env(current, {"CLOSEDLOOP_ADD_DIRS": str(sibling)})
220+
peers = payload["peers"]
221+
222+
# 4. Forbidden paths must not appear at all
223+
peer_paths = [p["path"] for p in peers]
224+
for key in scenario.forbidden_keys:
225+
forbidden = str(paths[key])
226+
assert forbidden not in peer_paths, (
227+
f"[{scenario.id}] {key!r} should not appear in peers; got: {peer_paths}"
228+
)
179229

180-
assert result.returncode == 0, result.stderr
181-
payload = json.loads(result.stdout)
182-
peer = next((p for p in payload["peers"] if p["path"] == str(sibling)), None)
183-
assert peer is not None
184-
assert peer["discoveryMethod"] == "add_dir"
230+
# 5. Each expectation: check occurrence count and per-field attributes
231+
for exp in scenario.expect_peers:
232+
target = str(paths[exp.key])
233+
matches = [p for p in peers if p["path"] == target]
234+
assert len(matches) == exp.count, (
235+
f"[{scenario.id}] expected {exp.count} peer(s) for {exp.key!r}, "
236+
f"got {len(matches)}; peers={peers}"
237+
)
238+
if exp.count == 0:
239+
continue
240+
peer = matches[0]
241+
for attr, field_name in (
242+
(exp.discovery_method, "discoveryMethod"),
243+
(exp.name, "name"),
244+
(exp.type, "type"),
245+
):
246+
if attr is not None:
247+
assert peer.get(field_name) == attr, (
248+
f"[{scenario.id}] peer {exp.key!r} {field_name}: "
249+
f"expected {attr!r}, got {peer.get(field_name)!r}"
250+
)

0 commit comments

Comments
 (0)