gabriel / muse public
test_stress_graph.py python
276 lines 10.0 KB
Raw
sha256:660fcac1df3ab28f61862e961890bd2ca8b754fa0242079d93ca1e25037ec8a6 chore(tests): add docstring to tests/__init__.py so rc14 tr… Human 26 days 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.snapshot import compute_commit_id, compute_snapshot_id
22 from muse.core.store import CommitRecord, write_commit
23
24
25 # ---------------------------------------------------------------------------
26 # Helpers
27 # ---------------------------------------------------------------------------
28
29 _SNAP_ID: str = compute_snapshot_id({})
30 _BASE_TS: datetime.datetime = datetime.datetime(2026, 1, 1, tzinfo=datetime.timezone.utc)
31
32
33 def _write(
34 root: pathlib.Path,
35 label: str,
36 parent: str | None = None,
37 parent2: str | None = None,
38 ) -> str:
39 """Write a commit with a real content-addressed ID. Returns the commit_id."""
40 parent_ids = [p for p in (parent, parent2) if p is not None]
41 cid = compute_commit_id(
42 parent_ids=parent_ids,
43 snapshot_id=_SNAP_ID,
44 message=label,
45 committed_at_iso=_BASE_TS.isoformat(),
46 )
47 write_commit(root, CommitRecord(
48 commit_id=cid,
49 repo_id="repo",
50 branch="main",
51 snapshot_id=_SNAP_ID,
52 message=label,
53 committed_at=_BASE_TS,
54 parent_commit_id=parent,
55 parent2_commit_id=parent2,
56 ))
57 return cid
58
59
60 @pytest.fixture
61 def repo(tmp_path: pathlib.Path) -> pathlib.Path:
62 muse = tmp_path / ".muse"
63 (muse / "commits").mkdir(parents=True)
64 (muse / "refs" / "heads").mkdir(parents=True)
65 return tmp_path
66
67
68 # ---------------------------------------------------------------------------
69 # Linear chain
70 # ---------------------------------------------------------------------------
71
72
73 class TestLinearChain:
74 def test_chain_of_500_finds_base(self, repo: pathlib.Path) -> None:
75 """LCA of two commits on a 500-long linear chain is the shared ancestor."""
76 prev: str | None = None
77 ids: list[str] = []
78 for i in range(500):
79 cid = _write(repo, f"c{i:04d}", prev)
80 ids.append(cid)
81 prev = cid
82
83 # Branch off at the commit at index 100
84 branch_tip_id = _write(repo, "branch-tip", ids[100])
85
86 base = find_merge_base(repo, ids[499], branch_tip_id)
87 assert base == ids[100]
88
89 def test_lca_of_adjacent_commits_is_parent(self, repo: pathlib.Path) -> None:
90 root_id = _write(repo, "root")
91 child_id = _write(repo, "child", root_id)
92 assert find_merge_base(repo, root_id, child_id) == root_id
93 assert find_merge_base(repo, child_id, root_id) == root_id
94
95 def test_long_chain_lca_symmetry(self, repo: pathlib.Path) -> None:
96 """find_merge_base(a, b) == find_merge_base(b, a) on a long chain."""
97 prev: str | None = None
98 ids: list[str] = []
99 for i in range(100):
100 cid = _write(repo, f"n{i:03d}", prev)
101 ids.append(cid)
102 prev = cid
103
104 left_id = _write(repo, "left", ids[50])
105 right_id = _write(repo, "right", ids[50])
106
107 assert find_merge_base(repo, left_id, right_id) == ids[50]
108 assert find_merge_base(repo, right_id, left_id) == ids[50]
109
110 def test_same_commit_returns_itself(self, repo: pathlib.Path) -> None:
111 solo_id = _write(repo, "solo")
112 assert find_merge_base(repo, solo_id, solo_id) == solo_id
113
114 def test_one_is_ancestor_of_other(self, repo: pathlib.Path) -> None:
115 """When A is a direct ancestor of B, LCA is A."""
116 prev: str | None = None
117 ids: list[str] = []
118 for i in range(20):
119 cid = _write(repo, f"x{i:02d}", prev)
120 ids.append(cid)
121 prev = cid
122 assert find_merge_base(repo, ids[0], ids[19]) == ids[0]
123 assert find_merge_base(repo, ids[19], ids[0]) == ids[0]
124
125
126 # ---------------------------------------------------------------------------
127 # Diamond topology
128 # ---------------------------------------------------------------------------
129
130
131 class TestDiamondTopology:
132 def test_simple_diamond(self, repo: pathlib.Path) -> None:
133 """
134 root
135 / \\
136 L R
137 \\ /
138 M (merge commit, not relevant here — just find LCA of L and R)
139 """
140 root_id = _write(repo, "root")
141 l_id = _write(repo, "L", root_id)
142 r_id = _write(repo, "R", root_id)
143 assert find_merge_base(repo, l_id, r_id) == root_id
144
145 def test_double_diamond(self, repo: pathlib.Path) -> None:
146 """
147 A
148 / \\
149 B C
150 \\ /
151 D
152 / \\
153 E F
154 \\ /
155 G
156 LCA(E, F) should be D.
157 """
158 a_id = _write(repo, "A")
159 b_id = _write(repo, "B", a_id)
160 c_id = _write(repo, "C", a_id)
161 d_id = _write(repo, "D", b_id, c_id)
162 e_id = _write(repo, "E", d_id)
163 f_id = _write(repo, "F", d_id)
164 assert find_merge_base(repo, e_id, f_id) == d_id
165
166 def test_criss_cross_merge(self, repo: pathlib.Path) -> None:
167 """
168 Criss-cross: A and B are each other's ancestor via two different merge paths.
169 X → L1 → M1(L1,R1)
170 X → R1 → M2(R1,L1)
171 LCA of M1 and M2 should be either L1 or R1 (both are valid LCAs).
172 The algorithm must not return None or crash.
173 """
174 x_id = _write(repo, "X")
175 l1_id = _write(repo, "L1", x_id)
176 r1_id = _write(repo, "R1", x_id)
177 m1_id = _write(repo, "M1", l1_id, r1_id)
178 m2_id = _write(repo, "M2", r1_id, l1_id)
179
180 base = find_merge_base(repo, m1_id, m2_id)
181 # Any of X, L1, R1 is a valid common ancestor; None is not acceptable.
182 assert base is not None
183 assert base in {x_id, l1_id, r1_id}
184
185 def test_octopus_three_branch_fan_in(self, repo: pathlib.Path) -> None:
186 """Three branches that all diverged from the same root."""
187 root_id = _write(repo, "root")
188 ba_id = _write(repo, "branch-a", root_id)
189 bb_id = _write(repo, "branch-b", root_id)
190 bc_id = _write(repo, "branch-c", root_id)
191
192 assert find_merge_base(repo, ba_id, bb_id) == root_id
193 assert find_merge_base(repo, ba_id, bc_id) == root_id
194 assert find_merge_base(repo, bb_id, bc_id) == root_id
195
196
197 # ---------------------------------------------------------------------------
198 # Independent histories
199 # ---------------------------------------------------------------------------
200
201
202 class TestDisjointHistories:
203 def test_no_common_ancestor_returns_none(self, repo: pathlib.Path) -> None:
204 island_a_id = _write(repo, "island-a")
205 island_b_id = _write(repo, "island-b")
206 assert find_merge_base(repo, island_a_id, island_b_id) is None
207
208 def test_long_independent_chains_return_none(self, repo: pathlib.Path) -> None:
209 prev_a: str | None = None
210 prev_b: str | None = None
211 last_a = last_b = ""
212 for i in range(20):
213 last_a = _write(repo, f"a{i:02d}", prev_a)
214 last_b = _write(repo, f"b{i:02d}", prev_b)
215 prev_a = last_a
216 prev_b = last_b
217 assert find_merge_base(repo, last_a, last_b) is None
218
219 def test_missing_commit_id_graceful(self, repo: pathlib.Path) -> None:
220 """Asking for an LCA where one commit doesn't exist should return None, not raise."""
221 real_id = _write(repo, "real")
222 result = find_merge_base(repo, real_id, "ghost-commit-that-does-not-exist")
223 # The ghost has no ancestors, so no common ancestor found.
224 assert result is None
225
226
227 # ---------------------------------------------------------------------------
228 # Ancestor-set correctness
229 # ---------------------------------------------------------------------------
230
231
232 class TestAncestorCorrectness:
233 def test_merge_commit_has_both_parents_as_ancestors(self, repo: pathlib.Path) -> None:
234 root_id = _write(repo, "root")
235 a_id = _write(repo, "A", root_id)
236 b_id = _write(repo, "B", root_id)
237 merge_id = _write(repo, "merge", a_id, b_id)
238 feature_id = _write(repo, "feature", a_id)
239
240 # LCA of feature and merge: feature branched from A, merge contains A.
241 # So A is the common ancestor.
242 base = find_merge_base(repo, feature_id, merge_id)
243 assert base == a_id
244
245 def test_wide_history_with_shared_root(self, repo: pathlib.Path) -> None:
246 """100 branches diverging from a shared root, pairwise LCA is root."""
247 root_id = _write(repo, "root")
248 branch_ids = [_write(repo, f"br{i:03d}", root_id) for i in range(50)]
249
250 # Check a sampling of pairs
251 for i in range(0, 50, 10):
252 for j in range(i + 1, 50, 10):
253 assert find_merge_base(repo, branch_ids[i], branch_ids[j]) == root_id
254
255 def test_deep_branch_divergence(self, repo: pathlib.Path) -> None:
256 """Branches diverge at root, each has 50 commits. LCA is root."""
257 root_id = _write(repo, "root")
258 prev_a: str | None = root_id
259 prev_b: str | None = root_id
260 last_a = last_b = root_id
261 for i in range(50):
262 last_a = _write(repo, f"da{i:02d}", prev_a)
263 last_b = _write(repo, f"db{i:02d}", prev_b)
264 prev_a = last_a
265 prev_b = last_b
266
267 assert find_merge_base(repo, last_a, last_b) == root_id
268
269 def test_multiple_merge_bases_chain(self, repo: pathlib.Path) -> None:
270 """A → B → C; branch D from B. LCA of C and D is B."""
271 a_id = _write(repo, "A")
272 b_id = _write(repo, "B", a_id)
273 c_id = _write(repo, "C", b_id)
274 d_id = _write(repo, "D", b_id)
275 assert find_merge_base(repo, c_id, d_id) == b_id
276 assert find_merge_base(repo, d_id, c_id) == b_id
File History 5 commits
sha256:660fcac1df3ab28f61862e961890bd2ca8b754fa0242079d93ca1e25037ec8a6 chore(tests): add docstring to tests/__init__.py so rc14 tr… Human 26 days ago
sha256:d8316ffae901be06347e16ab55be11868eb519dd16ade3e8aa16a99e662f7e62 baseline: rc14 re-baseline after rc3 store corruption recovery Human patch 26 days ago
sha256:50d413a635f57761c3fce1f70251b957b6423821525bf330747ef4007339222e Merge branch 'dev' into main Human 29 days ago
sha256:fb67fed5a4d3e40de84bdd163de94ef1386570bef1dd1a020a732c8a038962ce Merge branch 'dev' into main Human 48 days ago
sha256:1c4b3e3a9a1f300774c3ee662b572a698d5fd405bf765a71e6011a2e9c3eaaaa feat: Muse — version control for the agent era Human 100 days ago