gabriel / muse public
test_cmd_commit_graph_enhancements.py python
229 lines 7.6 KB
Raw
sha256:2eaa5d95f9d9383498e76947410a26e5a3ba23d182f339910c424cf88fad412b fix: try fetch/presign before fetch/mpack to avoid Cloudfla… Sonnet 4.6 patch 6 days ago
1 """Tests for the new commit-graph flags: --count, --first-parent, --ancestry-path."""
2
3 from __future__ import annotations
4
5 import datetime
6 import json
7 import pathlib
8
9 import pytest
10 from tests.cli_test_helper import CliRunner
11
12 cli = None # argparse migration — CliRunner ignores this arg
13 from muse.core.ids import hash_commit, hash_snapshot
14 from muse.core.commits import (
15 CommitRecord,
16 write_commit,
17 )
18 from muse.core.snapshots import (
19 SnapshotRecord,
20 write_snapshot,
21 )
22 from muse.core.types import Manifest
23 from muse.core.paths import muse_dir, ref_path
24
25 runner = CliRunner()
26
27 _DT = datetime.datetime(2026, 1, 1, tzinfo=datetime.timezone.utc)
28
29
30
31 def _init_repo(path: pathlib.Path) -> pathlib.Path:
32 dot_muse = muse_dir(path)
33 (dot_muse / "commits").mkdir(parents=True)
34 (dot_muse / "snapshots").mkdir(parents=True)
35 (dot_muse / "objects").mkdir(parents=True)
36 (dot_muse / "refs" / "heads").mkdir(parents=True)
37 (dot_muse / "HEAD").write_text("ref: refs/heads/main", encoding="utf-8")
38 (dot_muse / "repo.json").write_text(
39 json.dumps({"repo_id": "test-repo", "domain": "midi"}), encoding="utf-8"
40 )
41 return path
42
43
44 def _env(repo: pathlib.Path) -> Manifest:
45 return {"MUSE_REPO_ROOT": str(repo)}
46
47
48 def _snap(repo: pathlib.Path, tag: str) -> str:
49 """Write an empty-manifest snapshot; return its content-addressed ID."""
50 sid = hash_snapshot({})
51 write_snapshot(
52 repo,
53 SnapshotRecord(
54 snapshot_id=sid,
55 manifest={},
56 created_at=_DT,
57 ),
58 )
59 return sid
60
61
62 def _commit(
63 repo: pathlib.Path,
64 tag: str,
65 branch: str = "main",
66 parent: str | None = None,
67 parent2: str | None = None,
68 ) -> str:
69 """Write a commit using *tag* as message (ensures uniqueness); return real commit ID."""
70 sid = _snap(repo, tag)
71 parent_ids = [p for p in [parent, parent2] if p is not None]
72 cid = hash_commit( parent_ids=parent_ids,
73 snapshot_id=sid,
74 message=tag,
75 committed_at_iso=_DT.isoformat(),
76 author="tester",
77 )
78 write_commit(
79 repo,
80 CommitRecord(
81 commit_id=cid,
82 branch=branch,
83 snapshot_id=sid,
84 message=tag,
85 committed_at=_DT,
86 author="tester",
87 parent_commit_id=parent,
88 parent2_commit_id=parent2,
89 ),
90 )
91 branch_ref = ref_path(repo, branch)
92 branch_ref.parent.mkdir(parents=True, exist_ok=True)
93 branch_ref.write_text(cid, encoding="utf-8")
94 return cid
95
96
97 class TestCommitGraphCount:
98 def test_count_returns_integer(self, tmp_path: pathlib.Path) -> None:
99 _init_repo(tmp_path)
100 c1 = _commit(tmp_path, "a", parent=None)
101 _commit(tmp_path, "b", parent=c1)
102 result = runner.invoke(cli, ["commit-graph", "--count"], env=_env(tmp_path))
103 assert result.exit_code == 0
104 data = json.loads(result.output)
105 assert "count" in data
106 assert data["count"] == 2
107 assert "commits" not in data # full node list suppressed
108
109 def test_count_no_commits_returns_error(self, tmp_path: pathlib.Path) -> None:
110 _init_repo(tmp_path)
111 result = runner.invoke(cli, ["commit-graph", "--count"], env=_env(tmp_path))
112 assert result.exit_code != 0
113
114 def test_count_with_stop_at(self, tmp_path: pathlib.Path) -> None:
115 _init_repo(tmp_path)
116 base = _commit(tmp_path, "base", parent=None)
117 _commit(tmp_path, "feature", parent=base)
118 result = runner.invoke(
119 cli, ["commit-graph", "--stop-at", base, "--count"], env=_env(tmp_path)
120 )
121 assert result.exit_code == 0
122 data = json.loads(result.output)
123 assert data["count"] == 1 # only "feature", base excluded
124
125 def test_count_short_flag(self, tmp_path: pathlib.Path) -> None:
126 _init_repo(tmp_path)
127 _commit(tmp_path, "a")
128 result = runner.invoke(cli, ["commit-graph", "-c"], env=_env(tmp_path))
129 assert result.exit_code == 0
130 data = json.loads(result.output)
131 assert "count" in data
132
133
134 class TestCommitGraphFirstParent:
135 def test_first_parent_only(self, tmp_path: pathlib.Path) -> None:
136 _init_repo(tmp_path)
137 c1 = _commit(tmp_path, "c1", parent=None)
138 c2 = _commit(tmp_path, "c2", parent=c1)
139 result = runner.invoke(
140 cli, ["commit-graph", "--first-parent", "--count"], env=_env(tmp_path)
141 )
142 assert result.exit_code == 0
143 data = json.loads(result.output)
144 assert data["count"] == 2
145
146 def test_first_parent_excludes_merge_parent(self, tmp_path: pathlib.Path) -> None:
147 """With --first-parent, second parents of merges are not followed."""
148 _init_repo(tmp_path)
149 c1 = _commit(tmp_path, "c1", parent=None)
150 c2 = _commit(tmp_path, "branch_tip", "feat", parent=c1)
151 # merge commit with c1 as first parent, c2 as second parent
152 _commit(tmp_path, "merge", parent=c1, parent2=c2)
153 result = runner.invoke(
154 cli, ["commit-graph", "--first-parent", "--count"], env=_env(tmp_path)
155 )
156 assert result.exit_code == 0
157 data = json.loads(result.output)
158 # Should NOT follow c2 branch; only main chain: merge → c1
159 assert data["count"] == 2 # merge + c1
160
161 def test_first_parent_short_flag(self, tmp_path: pathlib.Path) -> None:
162 _init_repo(tmp_path)
163 _commit(tmp_path, "c1")
164 result = runner.invoke(
165 cli, ["commit-graph", "-1", "--count"], env=_env(tmp_path)
166 )
167 assert result.exit_code == 0
168
169
170 class TestCommitGraphAncestryPath:
171 def test_ancestry_path_requires_stop_at(self, tmp_path: pathlib.Path) -> None:
172 _init_repo(tmp_path)
173 _commit(tmp_path, "c1")
174 result = runner.invoke(
175 cli, ["commit-graph", "--ancestry-path"], env=_env(tmp_path)
176 )
177 assert result.exit_code != 0
178 data = json.loads(result.stderr)
179 assert "error" in data
180
181 def test_ancestry_path_with_stop_at_runs(self, tmp_path: pathlib.Path) -> None:
182 _init_repo(tmp_path)
183 base = _commit(tmp_path, "base", parent=None)
184 _commit(tmp_path, "feature", parent=base)
185 result = runner.invoke(
186 cli,
187 ["commit-graph", "--json", "--stop-at", base, "--ancestry-path"],
188 env=_env(tmp_path),
189 )
190 assert result.exit_code == 0
191 data = json.loads(result.output)
192 assert "commits" in data
193
194 def test_ancestry_path_short_flag(self, tmp_path: pathlib.Path) -> None:
195 _init_repo(tmp_path)
196 base = _commit(tmp_path, "base", parent=None)
197 _commit(tmp_path, "next", parent=base)
198 result = runner.invoke(
199 cli,
200 ["commit-graph", "--stop-at", base, "-a"],
201 env=_env(tmp_path),
202 )
203 assert result.exit_code == 0
204
205
206 class TestCommitGraphCombined:
207 def test_first_parent_and_count(self, tmp_path: pathlib.Path) -> None:
208 _init_repo(tmp_path)
209 c1 = _commit(tmp_path, "c1", parent=None)
210 _commit(tmp_path, "c2", parent=c1)
211 result = runner.invoke(
212 cli, ["commit-graph", "--first-parent", "--count"], env=_env(tmp_path)
213 )
214 assert result.exit_code == 0
215 data = json.loads(result.output)
216 assert data["count"] == 2
217
218 def test_count_always_emits_json(self, tmp_path: pathlib.Path) -> None:
219 """--count always emits JSON."""
220 _init_repo(tmp_path)
221 _commit(tmp_path, "c1")
222 result = runner.invoke(
223 cli,
224 ["commit-graph", "--count"],
225 env=_env(tmp_path),
226 )
227 assert result.exit_code == 0
228 data = json.loads(result.output)
229 assert "count" in data
File History 1 commit
sha256:2eaa5d95f9d9383498e76947410a26e5a3ba23d182f339910c424cf88fad412b fix: try fetch/presign before fetch/mpack to avoid Cloudfla… Sonnet 4.6 patch 6 days ago