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