gabriel / musehub public
test_phase2_inMemory_walk_dag.py python
227 lines 8.8 KB
Raw
sha256:ef10830ce231e0a20efcb0e2586cb879471247e916616e6fdd0d51df459e2595 fix: typing audit — 0 violations, 0 untyped defs across all… Sonnet 4.6 minor ⚠ breaking 23 days ago
1 """TDD — Phase 2 of issue #40: in-memory BFS sites migrated to walk_dag.
2
3 Structural tests confirm each function uses walk_dag and has no inline BFS.
4 Behavioural tests confirm the migrated functions produce correct output.
5
6 P2-1 Structural — CouplingProvider.compute uses walk_dag; no inline deque
7 P2-2 Structural — EntangleProvider.compute uses walk_dag; no inline deque
8 P2-3 Structural — VelocityProvider.compute uses walk_dag; no inline deque
9 P2-4 Structural — run_gc reachability uses walk_dag; no inline while queue
10 P2-5 Structural — _is_fast_forward uses walk_dag; no inline while frontier
11 P2-6 DELETED — _is_ancestor_in_bundle replaced by _is_fast_forward
12 P2-7 Structural — find_common_ancestor uses walk_dag; no inline while
13 P2-8 Behavioural — _is_fast_forward: True when remote_head is ancestor
14 P2-9 Behavioural — _is_fast_forward: False when remote_head is unreachable
15 P2-10 DELETED — _is_ancestor_in_bundle replaced by _is_fast_forward
16 P2-11 DELETED — _is_ancestor_in_bundle replaced by _is_fast_forward
17 P2-12 Behavioural — find_common_ancestor: returns LCA on diamond graph
18 """
19 from __future__ import annotations
20
21 import datetime
22 import inspect
23 from types import SimpleNamespace
24
25 import pytest
26
27
28 # ---------------------------------------------------------------------------
29 # P2-1 Structural — CouplingProvider
30 # ---------------------------------------------------------------------------
31
32 def test_p2_1_coupling_provider_uses_walk_dag() -> None:
33 """CouplingProvider.compute must delegate BFS; no inline deque BFS.
34
35 Phase 4 extracted the walk into _load_commit_walk, so walk_dag now lives
36 in the helper rather than in compute directly. The invariant is that
37 compute itself has no inline deque and delegates to the shared helper.
38 """
39 from musehub.services import musehub_intel_providers as mod
40
41 src = inspect.getsource(mod.CouplingProvider.compute)
42 assert "deque" not in src, (
43 "CouplingProvider.compute still has an inline deque."
44 )
45 assert "_load_commit_walk" in src or "walk_dag" in src, (
46 "CouplingProvider.compute must delegate BFS to walk_dag or _load_commit_walk."
47 )
48
49
50 # ---------------------------------------------------------------------------
51 # P2-2 Structural — EntangleProvider
52 # ---------------------------------------------------------------------------
53
54 def test_p2_2_entangle_provider_uses_walk_dag() -> None:
55 """EntangleProvider.compute must delegate BFS; no inline deque BFS."""
56 from musehub.services import musehub_intel_providers as mod
57
58 src = inspect.getsource(mod.EntangleProvider.compute)
59 assert "deque" not in src, (
60 "EntangleProvider.compute still has an inline deque."
61 )
62 assert "_load_commit_walk" in src or "walk_dag" in src, (
63 "EntangleProvider.compute must delegate BFS to walk_dag or _load_commit_walk."
64 )
65
66
67 # ---------------------------------------------------------------------------
68 # P2-3 Structural — VelocityProvider
69 # ---------------------------------------------------------------------------
70
71 def test_p2_3_velocity_provider_uses_walk_dag() -> None:
72 """VelocityProvider.compute must delegate BFS; no inline deque BFS."""
73 from musehub.services import musehub_intel_providers as mod
74
75 src = inspect.getsource(mod.VelocityProvider.compute)
76 assert "deque" not in src, (
77 "VelocityProvider.compute still has an inline deque."
78 )
79 assert "_load_commit_walk" in src or "walk_dag" in src, (
80 "VelocityProvider.compute must delegate BFS to walk_dag or _load_commit_walk."
81 )
82
83
84 # ---------------------------------------------------------------------------
85 # P2-4 Structural — run_gc reachability
86 # ---------------------------------------------------------------------------
87
88 def test_p2_4_run_gc_uses_walk_dag() -> None:
89 """run_gc reachability BFS must use walk_dag; no inline while queue."""
90 from musehub.services import musehub_gc as mod
91
92 src = inspect.getsource(mod.run_gc)
93 assert "walk_dag" in src, (
94 "run_gc must delegate reachability BFS to walk_dag."
95 )
96 # The old pattern was `queue = list(heads)` then `while queue:`
97 assert "while queue" not in src, (
98 "run_gc still has an inline while-queue BFS."
99 )
100
101
102 # ---------------------------------------------------------------------------
103 # P2-5 Structural — _is_fast_forward
104 # ---------------------------------------------------------------------------
105
106 def test_p2_5_is_fast_forward_uses_walk_dag() -> None:
107 """_is_fast_forward must use walk_dag; no inline while frontier."""
108 from musehub.services import musehub_sync as mod
109
110 src = inspect.getsource(mod._is_fast_forward)
111 assert "walk_dag" in src, (
112 "_is_fast_forward must delegate BFS to walk_dag."
113 )
114 assert "while frontier" not in src, (
115 "_is_fast_forward still has an inline while-frontier BFS."
116 )
117
118
119 # ---------------------------------------------------------------------------
120 # P2-7 Structural — find_common_ancestor
121 # ---------------------------------------------------------------------------
122
123 def test_p2_7_find_common_ancestor_uses_walk_dag() -> None:
124 """find_common_ancestor must use walk_dag; no inline while frontier."""
125 from musehub.services import musehub_divergence as mod
126
127 src = inspect.getsource(mod.find_common_ancestor)
128 assert "walk_dag" in src, (
129 "find_common_ancestor must delegate graph walk to walk_dag."
130 )
131 assert "while frontier" not in src, (
132 "find_common_ancestor still has an inline while-frontier loop."
133 )
134
135
136 # ---------------------------------------------------------------------------
137 # Helpers for behavioural tests
138 # ---------------------------------------------------------------------------
139
140 def _make_commit_input(commit_id: str, parent_ids: list[str]) -> None:
141 """Minimal CommitInput stand-in (avoids DB imports)."""
142 return SimpleNamespace(commit_id=commit_id, parent_ids=parent_ids)
143
144
145 def _make_wire_commit(commit_id: str, p1: str | None = None, p2: str | None = None) -> None:
146 """Minimal WireCommit stand-in."""
147 return SimpleNamespace(
148 commit_id=commit_id,
149 parent_commit_id=p1,
150 parent2_commit_id=p2,
151 )
152
153
154 def _make_musehub_commit(commit_id: str, parent_ids: list[str], timestamp: datetime.datetime | None = None) -> SimpleNamespace:
155 """Minimal MusehubCommit stand-in."""
156 import datetime
157 ts = timestamp or datetime.datetime(2026, 1, 1, tzinfo=datetime.timezone.utc)
158 return SimpleNamespace(commit_id=commit_id, parent_ids=parent_ids, timestamp=ts)
159
160
161 # ---------------------------------------------------------------------------
162 # P2-8 Behavioural — _is_fast_forward: True case
163 # ---------------------------------------------------------------------------
164
165 def test_p2_8_is_fast_forward_true() -> None:
166 """_is_fast_forward returns True when remote_head is an ancestor.
167
168 Chain: C1 → C2 → C3 (HEAD). remote_head=C1 → True.
169 """
170 from musehub.services.musehub_sync import _is_fast_forward
171
172 commits = [
173 _make_commit_input("C3", ["C2"]),
174 _make_commit_input("C2", ["C1"]),
175 _make_commit_input("C1", []),
176 ]
177 assert _is_fast_forward("C1", "C3", commits) is True
178
179
180 # ---------------------------------------------------------------------------
181 # P2-9 Behavioural — _is_fast_forward: False case
182 # ---------------------------------------------------------------------------
183
184 def test_p2_9_is_fast_forward_false() -> None:
185 """_is_fast_forward returns False when remote_head is not in ancestry.
186
187 Chain: C1 → C2. remote_head=X (independent) → False.
188 """
189 from musehub.services.musehub_sync import _is_fast_forward
190
191 commits = [
192 _make_commit_input("C2", ["C1"]),
193 _make_commit_input("C1", []),
194 ]
195 assert _is_fast_forward("X", "C2", commits) is False
196
197
198 # ---------------------------------------------------------------------------
199 # P2-12 Behavioural — find_common_ancestor: LCA on diamond graph
200 # ---------------------------------------------------------------------------
201
202 def test_p2_12_find_common_ancestor_diamond() -> None:
203 """find_common_ancestor returns the LCA for a diamond graph.
204
205 Diamond: BASE ← L1 ← L2 (branch A tip)
206 ← R1 ← R2 (branch B tip)
207
208 LCA of A and B is BASE.
209 """
210 import datetime
211 from musehub.services.musehub_divergence import find_common_ancestor
212
213 t = lambda n: datetime.datetime(2026, 1, n, tzinfo=datetime.timezone.utc)
214
215 a_commits = [
216 _make_musehub_commit("L2", ["L1"], t(4)),
217 _make_musehub_commit("L1", ["BASE"], t(3)),
218 _make_musehub_commit("BASE", [], t(1)),
219 ]
220 b_commits = [
221 _make_musehub_commit("R2", ["R1"], t(4)),
222 _make_musehub_commit("R1", ["BASE"], t(2)),
223 _make_musehub_commit("BASE", [], t(1)),
224 ]
225
226 result = find_common_ancestor(a_commits, b_commits)
227 assert result == "BASE", f"Expected LCA=BASE, got {result}"
File History 1 commit
sha256:ef10830ce231e0a20efcb0e2586cb879471247e916616e6fdd0d51df459e2595 fix: typing audit — 0 violations, 0 untyped defs across all… Sonnet 4.6 minor 23 days ago