gabriel / muse public
test_cmd_describe.py python
254 lines 8.6 KB
Raw
sha256:51ce277f663e01a43eaffbe77509b1de7ac2d4251b55d23306304bcdeb92c90d feat(pack): delta-encode snapshots in MPackBundle wire format Sonnet 4.6 minor ⚠ breaking 121 days ago
1 """Tests for ``muse describe`` and ``muse/core/describe.py``.
2
3 Covers: no tags fallback to SHA, tag at tip, tag behind tip (distance),
4 --long format, --require-tag exit-1, --format json, core describe_commit,
5 stress: deep ancestry.
6 """
7
8 from __future__ import annotations
9
10 import datetime
11 import json
12 import pathlib
13
14 import pytest
15 from tests.cli_test_helper import CliRunner
16
17 cli = None # argparse migration — CliRunner ignores this arg
18 from muse.core.describe import describe_commit
19 from muse.core.object_store import write_object
20 from muse.core.snapshot import compute_commit_id, compute_snapshot_id
21 from muse.core.store import CommitRecord, SnapshotRecord, TagRecord, write_commit, write_snapshot, write_tag
22 from muse.core.types import Manifest, blob_id
23
24 runner = CliRunner()
25
26 from muse.core.types import content_hash as _content_hash
27 from muse.core.paths import muse_dir, ref_path
28 _REPO_ID = _content_hash({"name": "describe-test"})
29
30
31 # ---------------------------------------------------------------------------
32 # Helpers
33 # ---------------------------------------------------------------------------
34
35
36
37
38 def _init_repo(path: pathlib.Path) -> pathlib.Path:
39 dot_muse = muse_dir(path)
40 for d in ("commits", "snapshots", "objects", "refs/heads", "tags"):
41 (dot_muse / d).mkdir(parents=True, exist_ok=True)
42 (dot_muse / "HEAD").write_text("ref: refs/heads/main", encoding="utf-8")
43 (dot_muse / "repo.json").write_text(
44 json.dumps({"repo_id": _REPO_ID, "domain": "midi"}), encoding="utf-8"
45 )
46 return path
47
48
49 def _env(repo: pathlib.Path) -> Manifest:
50 return {"MUSE_REPO_ROOT": str(repo)}
51
52
53 def _make_commit(
54 root: pathlib.Path,
55 parent_id: str | None = None,
56 content: bytes = b"data",
57 branch: str = "main",
58 ) -> str:
59 obj_id = blob_id(content)
60 write_object(root, obj_id, content)
61 manifest = {f"file_{obj_id[7:15]}.txt": obj_id}
62 snap_id = compute_snapshot_id(manifest)
63 write_snapshot(root, SnapshotRecord(snapshot_id=snap_id, manifest=manifest))
64 committed_at = datetime.datetime.now(datetime.timezone.utc)
65 parent_ids = [parent_id] if parent_id else []
66 commit_id = compute_commit_id(
67 parent_ids=parent_ids,
68 snapshot_id=snap_id,
69 message=f"commit on {branch}",
70 committed_at_iso=committed_at.isoformat(),
71 )
72 write_commit(root, CommitRecord(
73 commit_id=commit_id,
74 repo_id=_REPO_ID,
75 branch=branch,
76 snapshot_id=snap_id,
77 message=f"commit on {branch}",
78 committed_at=committed_at,
79 parent_commit_id=parent_id,
80 ))
81 (ref_path(root, branch)).write_text(commit_id, encoding="utf-8")
82 return commit_id
83
84
85 def _make_tag(root: pathlib.Path, tag: str, commit_id: str) -> None:
86 write_tag(root, TagRecord(
87 tag_id=_content_hash({"tag": tag, "commit_id": commit_id}),
88 repo_id=_REPO_ID,
89 tag=tag,
90 commit_id=commit_id,
91 created_at=datetime.datetime.now(datetime.timezone.utc),
92 ))
93
94
95 # ---------------------------------------------------------------------------
96 # Unit: core describe_commit
97 # ---------------------------------------------------------------------------
98
99
100 def test_describe_no_tags_returns_shortblob_id(tmp_path: pathlib.Path) -> None:
101 _init_repo(tmp_path)
102 cid = _make_commit(tmp_path, content=b"alpha")
103 result = describe_commit(tmp_path, _REPO_ID, cid)
104 assert result["tag"] is None
105 assert result["short_sha"] == cid[:len("sha256:") + 12]
106 assert result["name"] == result["short_sha"]
107
108
109 def test_describe_tag_at_tip(tmp_path: pathlib.Path) -> None:
110 _init_repo(tmp_path)
111 cid = _make_commit(tmp_path, content=b"beta")
112 _make_tag(tmp_path, "v1.0.0", cid)
113 result = describe_commit(tmp_path, _REPO_ID, cid)
114 assert result["tag"] == "v1.0.0"
115 assert result["distance"] == 0
116 assert result["name"] == "v1.0.0"
117
118
119 def test_describe_tag_one_hop_behind(tmp_path: pathlib.Path) -> None:
120 _init_repo(tmp_path)
121 cid1 = _make_commit(tmp_path, content=b"first")
122 _make_tag(tmp_path, "v0.9.0", cid1)
123 cid2 = _make_commit(tmp_path, parent_id=cid1, content=b"second")
124 result = describe_commit(tmp_path, _REPO_ID, cid2)
125 assert result["tag"] == "v0.9.0"
126 assert result["distance"] == 1
127 assert result["name"] == "v0.9.0~1"
128
129
130 def test_describe_long_format(tmp_path: pathlib.Path) -> None:
131 _init_repo(tmp_path)
132 cid = _make_commit(tmp_path, content=b"gamma")
133 _make_tag(tmp_path, "v2.0.0", cid)
134 result = describe_commit(tmp_path, _REPO_ID, cid, long_format=True)
135 assert result["tag"] == "v2.0.0"
136 assert result["distance"] == 0
137 # Long format always includes distance + short_sha (no git-style 'g' prefix).
138 assert result["name"].startswith("v2.0.0-0-sha256:")
139
140
141 # ---------------------------------------------------------------------------
142 # CLI: muse describe
143 # ---------------------------------------------------------------------------
144
145
146 def test_describe_cli_help() -> None:
147 result = runner.invoke(cli, ["describe", "--help"])
148 assert result.exit_code == 0
149 assert "--long" in result.output or "-l" in result.output
150
151
152 def test_describe_cli_no_commits(tmp_path: pathlib.Path) -> None:
153 _init_repo(tmp_path)
154 result = runner.invoke(cli, ["describe"], env=_env(tmp_path))
155 assert result.exit_code != 0
156
157
158 def test_describe_cli_text_output(tmp_path: pathlib.Path) -> None:
159 _init_repo(tmp_path)
160 cid = _make_commit(tmp_path, content=b"cli-test")
161 _make_tag(tmp_path, "v3.0.0", cid)
162 result = runner.invoke(cli, ["describe"], env=_env(tmp_path))
163 assert result.exit_code == 0
164 assert "v3.0.0" in result.output
165
166
167 def test_describe_cli_json_output(tmp_path: pathlib.Path) -> None:
168 _init_repo(tmp_path)
169 cid = _make_commit(tmp_path, content=b"json-test")
170 _make_tag(tmp_path, "v4.0.0", cid)
171 result = runner.invoke(cli, ["describe", "--json"], env=_env(tmp_path))
172 assert result.exit_code == 0
173 data = json.loads(result.output)
174 assert data["tag"] == "v4.0.0"
175 assert data["distance"] == 0
176 assert "commit_id" in data
177
178
179 def test_describe_cli_require_tag_fails_without_tags(tmp_path: pathlib.Path) -> None:
180 _init_repo(tmp_path)
181 _make_commit(tmp_path, content=b"no-tags")
182 result = runner.invoke(cli, ["describe", "--require-tag"], env=_env(tmp_path))
183 assert result.exit_code != 0
184
185
186 def test_describe_cli_long_flag(tmp_path: pathlib.Path) -> None:
187 _init_repo(tmp_path)
188 cid = _make_commit(tmp_path, content=b"long")
189 _make_tag(tmp_path, "v5.0.0", cid)
190 result = runner.invoke(cli, ["describe", "--long"], env=_env(tmp_path))
191 assert result.exit_code == 0
192 assert "v5.0.0-0-sha256:" in result.output
193
194
195 def test_describe_cli_short_flags(tmp_path: pathlib.Path) -> None:
196 _init_repo(tmp_path)
197 cid = _make_commit(tmp_path, content=b"short-flags")
198 _make_tag(tmp_path, "v6.0.0", cid)
199 result = runner.invoke(cli, ["describe", "-l", "--json"], env=_env(tmp_path))
200 assert result.exit_code == 0
201 data = json.loads(result.output)
202 assert "v6.0.0" in data["name"]
203
204
205 # ---------------------------------------------------------------------------
206 # Stress: deep ancestry (100 commits, tag at root)
207 # ---------------------------------------------------------------------------
208
209
210 def test_describe_stress_deep_ancestry(tmp_path: pathlib.Path) -> None:
211 _init_repo(tmp_path)
212 prev: str | None = None
213 first_commit_id = ""
214 for i in range(100):
215 cid = _make_commit(tmp_path, parent_id=prev, content=f"step {i}".encode())
216 if i == 0:
217 first_commit_id = cid
218 prev = cid
219
220 _make_tag(tmp_path, "v-root", first_commit_id)
221 assert prev is not None
222 result = describe_commit(tmp_path, _REPO_ID, prev)
223 assert result["tag"] == "v-root"
224 assert result["distance"] == 99
225 assert "v-root~99" == result["name"]
226
227
228 class TestRegisterFlags:
229 def test_default_json_out_is_false(self) -> None:
230 import argparse
231 from muse.cli.commands.describe import register
232 p = argparse.ArgumentParser()
233 subs = p.add_subparsers()
234 register(subs)
235 args = p.parse_args(["describe"])
236 assert args.json_out is False
237
238 def test_json_flag_sets_json_out(self) -> None:
239 import argparse
240 from muse.cli.commands.describe import register
241 p = argparse.ArgumentParser()
242 subs = p.add_subparsers()
243 register(subs)
244 args = p.parse_args(["describe", "--json"])
245 assert args.json_out is True
246
247 def test_j_shorthand_sets_json_out(self) -> None:
248 import argparse
249 from muse.cli.commands.describe import register
250 p = argparse.ArgumentParser()
251 subs = p.add_subparsers()
252 register(subs)
253 args = p.parse_args(["describe", "-j"])
254 assert args.json_out is True
File History 1 commit
sha256:51ce277f663e01a43eaffbe77509b1de7ac2d4251b55d23306304bcdeb92c90d feat(pack): delta-encode snapshots in MPackBundle wire format Sonnet 4.6 minor 121 days ago