cgcardona / muse public
test_stress_graph.py python
259 lines 8.9 KB
e6786943 feat: upgrade to Python 3.14, drop from __future__ import annotations Gabriel Cardona <cgcardona@gmail.com> 1d ago
1 """Stress tests for the commit DAG and merge-base algorithm.
2
3 Exercises:
4 - Linear chains of 500 commits.
5 - Wide fan-out / fan-in (octopus merge shapes).
6 - Criss-cross merge (ambiguous LCA — should still find *some* ancestor).
7 - Independent histories (no common ancestor → None).
8 - find_merge_base symmetry: find_merge_base(a, b) == find_merge_base(b, a).
9 - Missing commit handles gracefully (None parent pointers in corrupt graphs).
10 - Diamond topology: four-node diamond always finds the root.
11 - Double diamond: two diamonds chained together.
12 - Long parallel branches that converge at a single point.
13 """
14
15 import datetime
16 import pathlib
17
18 import pytest
19
20 from muse.core.merge_engine import find_merge_base
21 from muse.core.store import CommitRecord, write_commit
22
23
24 # ---------------------------------------------------------------------------
25 # Helpers
26 # ---------------------------------------------------------------------------
27
28
29 def _write(root: pathlib.Path, cid: str, parent: str | None = None, parent2: str | None = None) -> None:
30 write_commit(root, CommitRecord(
31 commit_id=cid,
32 repo_id="repo",
33 branch="main",
34 snapshot_id=f"snap-{cid}",
35 message=cid,
36 committed_at=datetime.datetime.now(datetime.timezone.utc),
37 parent_commit_id=parent,
38 parent2_commit_id=parent2,
39 ))
40
41
42 @pytest.fixture
43 def repo(tmp_path: pathlib.Path) -> pathlib.Path:
44 muse = tmp_path / ".muse"
45 (muse / "commits").mkdir(parents=True)
46 (muse / "refs" / "heads").mkdir(parents=True)
47 return tmp_path
48
49
50 # ---------------------------------------------------------------------------
51 # Linear chain
52 # ---------------------------------------------------------------------------
53
54
55 class TestLinearChain:
56 def test_chain_of_500_finds_base(self, repo: pathlib.Path) -> None:
57 """LCA of two commits on a 500-long linear chain is the shared ancestor."""
58 prev: str | None = None
59 for i in range(500):
60 cid = f"c{i:04d}"
61 _write(repo, cid, prev)
62 prev = cid
63
64 # Branch off at c0100
65 _write(repo, "branch-tip", "c0100")
66
67 base = find_merge_base(repo, "c0499", "branch-tip")
68 assert base == "c0100"
69
70 def test_lca_of_adjacent_commits_is_parent(self, repo: pathlib.Path) -> None:
71 _write(repo, "root")
72 _write(repo, "child", "root")
73 assert find_merge_base(repo, "root", "child") == "root"
74 assert find_merge_base(repo, "child", "root") == "root"
75
76 def test_long_chain_lca_symmetry(self, repo: pathlib.Path) -> None:
77 """find_merge_base(a, b) == find_merge_base(b, a) on a long chain."""
78 prev: str | None = None
79 for i in range(100):
80 cid = f"n{i:03d}"
81 _write(repo, cid, prev)
82 prev = cid
83
84 _write(repo, "left", "n050")
85 _write(repo, "right", "n050")
86
87 assert find_merge_base(repo, "left", "right") == "n050"
88 assert find_merge_base(repo, "right", "left") == "n050"
89
90 def test_same_commit_returns_itself(self, repo: pathlib.Path) -> None:
91 _write(repo, "solo")
92 assert find_merge_base(repo, "solo", "solo") == "solo"
93
94 def test_one_is_ancestor_of_other(self, repo: pathlib.Path) -> None:
95 """When A is a direct ancestor of B, LCA is A."""
96 prev: str | None = None
97 for i in range(20):
98 cid = f"x{i:02d}"
99 _write(repo, cid, prev)
100 prev = cid
101 assert find_merge_base(repo, "x00", "x19") == "x00"
102 assert find_merge_base(repo, "x19", "x00") == "x00"
103
104
105 # ---------------------------------------------------------------------------
106 # Diamond topology
107 # ---------------------------------------------------------------------------
108
109
110 class TestDiamondTopology:
111 def test_simple_diamond(self, repo: pathlib.Path) -> None:
112 """
113 root
114 / \\
115 L R
116 \\ /
117 M (merge commit, not relevant here — just find LCA of L and R)
118 """
119 _write(repo, "root")
120 _write(repo, "L", "root")
121 _write(repo, "R", "root")
122 assert find_merge_base(repo, "L", "R") == "root"
123
124 def test_double_diamond(self, repo: pathlib.Path) -> None:
125 """
126 A
127 / \\
128 B C
129 \\ /
130 D
131 / \\
132 E F
133 \\ /
134 G
135 LCA(E, F) should be D.
136 """
137 _write(repo, "A")
138 _write(repo, "B", "A")
139 _write(repo, "C", "A")
140 _write(repo, "D", "B", "C")
141 _write(repo, "E", "D")
142 _write(repo, "F", "D")
143 assert find_merge_base(repo, "E", "F") == "D"
144
145 def test_criss_cross_merge(self, repo: pathlib.Path) -> None:
146 """
147 Criss-cross: A and B are each other's ancestor via two different merge paths.
148 X → L1 → M1(L1,R1)
149 X → R1 → M2(R1,L1)
150 LCA of M1 and M2 should be either L1 or R1 (both are valid LCAs).
151 The algorithm must not return None or crash.
152 """
153 _write(repo, "X")
154 _write(repo, "L1", "X")
155 _write(repo, "R1", "X")
156 _write(repo, "M1", "L1", "R1")
157 _write(repo, "M2", "R1", "L1")
158
159 base = find_merge_base(repo, "M1", "M2")
160 # Any of X, L1, R1 is a valid common ancestor; None is not acceptable.
161 assert base is not None
162 assert base in {"X", "L1", "R1"}
163
164 def test_octopus_three_branch_fan_in(self, repo: pathlib.Path) -> None:
165 """Three branches that all diverged from the same root."""
166 _write(repo, "root")
167 _write(repo, "branch-a", "root")
168 _write(repo, "branch-b", "root")
169 _write(repo, "branch-c", "root")
170
171 assert find_merge_base(repo, "branch-a", "branch-b") == "root"
172 assert find_merge_base(repo, "branch-a", "branch-c") == "root"
173 assert find_merge_base(repo, "branch-b", "branch-c") == "root"
174
175
176 # ---------------------------------------------------------------------------
177 # Independent histories
178 # ---------------------------------------------------------------------------
179
180
181 class TestDisjointHistories:
182 def test_no_common_ancestor_returns_none(self, repo: pathlib.Path) -> None:
183 _write(repo, "island-a")
184 _write(repo, "island-b")
185 assert find_merge_base(repo, "island-a", "island-b") is None
186
187 def test_long_independent_chains_return_none(self, repo: pathlib.Path) -> None:
188 prev_a: str | None = None
189 prev_b: str | None = None
190 for i in range(20):
191 a = f"a{i:02d}"
192 b = f"b{i:02d}"
193 _write(repo, a, prev_a)
194 _write(repo, b, prev_b)
195 prev_a = a
196 prev_b = b
197 assert find_merge_base(repo, "a19", "b19") is None
198
199 def test_missing_commit_id_graceful(self, repo: pathlib.Path) -> None:
200 """Asking for an LCA where one commit doesn't exist should return None, not raise."""
201 _write(repo, "real")
202 result = find_merge_base(repo, "real", "ghost-commit-that-does-not-exist")
203 # The ghost has no ancestors, so no common ancestor found.
204 assert result is None
205
206
207 # ---------------------------------------------------------------------------
208 # Ancestor-set correctness
209 # ---------------------------------------------------------------------------
210
211
212 class TestAncestorCorrectness:
213 def test_merge_commit_has_both_parents_as_ancestors(self, repo: pathlib.Path) -> None:
214 _write(repo, "root")
215 _write(repo, "A", "root")
216 _write(repo, "B", "root")
217 _write(repo, "merge", "A", "B")
218 _write(repo, "feature", "A")
219
220 # LCA of feature and merge: feature branched from A, merge contains A.
221 # So A is the common ancestor.
222 base = find_merge_base(repo, "feature", "merge")
223 assert base == "A"
224
225 def test_wide_history_with_shared_root(self, repo: pathlib.Path) -> None:
226 """100 branches diverging from a shared root, pairwise LCA is root."""
227 _write(repo, "root")
228 branches = [f"br{i:03d}" for i in range(50)]
229 for br in branches:
230 _write(repo, br, "root")
231
232 # Check a sampling of pairs
233 for i in range(0, 50, 10):
234 for j in range(i + 1, 50, 10):
235 assert find_merge_base(repo, branches[i], branches[j]) == "root"
236
237 def test_deep_branch_divergence(self, repo: pathlib.Path) -> None:
238 """Branches diverge at root, each has 50 commits. LCA is root."""
239 _write(repo, "root")
240 prev_a: str | None = "root"
241 prev_b: str | None = "root"
242 for i in range(50):
243 a = f"da{i:02d}"
244 b = f"db{i:02d}"
245 _write(repo, a, prev_a)
246 _write(repo, b, prev_b)
247 prev_a = a
248 prev_b = b
249
250 assert find_merge_base(repo, "da49", "db49") == "root"
251
252 def test_multiple_merge_bases_chain(self, repo: pathlib.Path) -> None:
253 """A → B → C; branch D from B. LCA of C and D is B."""
254 _write(repo, "A")
255 _write(repo, "B", "A")
256 _write(repo, "C", "B")
257 _write(repo, "D", "B")
258 assert find_merge_base(repo, "C", "D") == "B"
259 assert find_merge_base(repo, "D", "C") == "B"