test_phase2_inMemory_walk_dag.py
python
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