gabriel / muse public
test_cmd_describe.py python
222 lines 7.5 KB
Raw
sha256:660fcac1df3ab28f61862e961890bd2ca8b754fa0242079d93ca1e25037ec8a6 chore(tests): add docstring to tests/__init__.py so rc14 tr… Human 26 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 hashlib
12 import json
13 import pathlib
14
15 import pytest
16 from tests.cli_test_helper import CliRunner
17
18 cli = None # argparse migration — CliRunner ignores this arg
19 from muse.core.describe import describe_commit
20 from muse.core.object_store import write_object
21 from muse.core.snapshot import compute_commit_id, compute_snapshot_id
22 from muse.core.store import CommitRecord, SnapshotRecord, TagRecord, write_commit, write_snapshot, write_tag
23 from muse.core._types import Manifest
24
25 runner = CliRunner()
26
27 _REPO_ID = "describe-test"
28
29
30 # ---------------------------------------------------------------------------
31 # Helpers
32 # ---------------------------------------------------------------------------
33
34
35 def _sha(data: bytes) -> str:
36 return hashlib.sha256(data).hexdigest()
37
38
39 def _init_repo(path: pathlib.Path) -> pathlib.Path:
40 muse = path / ".muse"
41 for d in ("commits", "snapshots", "objects", "refs/heads", f"tags/{_REPO_ID}"):
42 (muse / d).mkdir(parents=True, exist_ok=True)
43 (muse / "HEAD").write_text("ref: refs/heads/main", encoding="utf-8")
44 (muse / "repo.json").write_text(
45 json.dumps({"repo_id": _REPO_ID, "domain": "midi"}), encoding="utf-8"
46 )
47 return path
48
49
50 def _env(repo: pathlib.Path) -> Manifest:
51 return {"MUSE_REPO_ROOT": str(repo)}
52
53
54 def _make_commit(
55 root: pathlib.Path,
56 parent_id: str | None = None,
57 content: bytes = b"data",
58 branch: str = "main",
59 ) -> str:
60 obj_id = _sha(content)
61 write_object(root, obj_id, content)
62 manifest = {f"file_{obj_id[:8]}.txt": obj_id}
63 snap_id = compute_snapshot_id(manifest)
64 write_snapshot(root, SnapshotRecord(snapshot_id=snap_id, manifest=manifest))
65 committed_at = datetime.datetime.now(datetime.timezone.utc)
66 parent_ids = [parent_id] if parent_id else []
67 commit_id = compute_commit_id(parent_ids, snap_id, f"commit on {branch}", committed_at.isoformat())
68 write_commit(root, CommitRecord(
69 commit_id=commit_id,
70 repo_id=_REPO_ID,
71 branch=branch,
72 snapshot_id=snap_id,
73 message=f"commit on {branch}",
74 committed_at=committed_at,
75 parent_commit_id=parent_id,
76 ))
77 (root / ".muse" / "refs" / "heads" / branch).write_text(commit_id, encoding="utf-8")
78 return commit_id
79
80
81 def _make_tag(root: pathlib.Path, tag: str, commit_id: str) -> None:
82 import uuid as _uuid
83 write_tag(root, TagRecord(
84 tag_id=str(_uuid.uuid4()),
85 tag=tag,
86 commit_id=commit_id,
87 repo_id=_REPO_ID,
88 created_at=datetime.datetime.now(datetime.timezone.utc),
89 ))
90
91
92 # ---------------------------------------------------------------------------
93 # Unit: core describe_commit
94 # ---------------------------------------------------------------------------
95
96
97 def test_describe_no_tags_returns_short_sha(tmp_path: pathlib.Path) -> None:
98 _init_repo(tmp_path)
99 cid = _make_commit(tmp_path, content=b"alpha")
100 result = describe_commit(tmp_path, _REPO_ID, cid)
101 assert result["tag"] is None
102 assert result["name"] == cid[:12]
103 assert result["short_sha"] == cid[:12]
104
105
106 def test_describe_tag_at_tip(tmp_path: pathlib.Path) -> None:
107 _init_repo(tmp_path)
108 cid = _make_commit(tmp_path, content=b"beta")
109 _make_tag(tmp_path, "v1.0.0", cid)
110 result = describe_commit(tmp_path, _REPO_ID, cid)
111 assert result["tag"] == "v1.0.0"
112 assert result["distance"] == 0
113 assert result["name"] == "v1.0.0"
114
115
116 def test_describe_tag_one_hop_behind(tmp_path: pathlib.Path) -> None:
117 _init_repo(tmp_path)
118 cid1 = _make_commit(tmp_path, content=b"first")
119 _make_tag(tmp_path, "v0.9.0", cid1)
120 cid2 = _make_commit(tmp_path, parent_id=cid1, content=b"second")
121 result = describe_commit(tmp_path, _REPO_ID, cid2)
122 assert result["tag"] == "v0.9.0"
123 assert result["distance"] == 1
124 assert result["name"] == "v0.9.0~1"
125
126
127 def test_describe_long_format(tmp_path: pathlib.Path) -> None:
128 _init_repo(tmp_path)
129 cid = _make_commit(tmp_path, content=b"gamma")
130 _make_tag(tmp_path, "v2.0.0", cid)
131 result = describe_commit(tmp_path, _REPO_ID, cid, long_format=True)
132 assert result["tag"] == "v2.0.0"
133 assert result["distance"] == 0
134 # Long format always includes distance + SHA.
135 assert "v2.0.0-0-g" in result["name"]
136
137
138 # ---------------------------------------------------------------------------
139 # CLI: muse describe
140 # ---------------------------------------------------------------------------
141
142
143 def test_describe_cli_help() -> None:
144 result = runner.invoke(cli, ["describe", "--help"])
145 assert result.exit_code == 0
146 assert "--long" in result.output or "-l" in result.output
147
148
149 def test_describe_cli_no_commits(tmp_path: pathlib.Path) -> None:
150 _init_repo(tmp_path)
151 result = runner.invoke(cli, ["describe"], env=_env(tmp_path))
152 assert result.exit_code != 0
153
154
155 def test_describe_cli_text_output(tmp_path: pathlib.Path) -> None:
156 _init_repo(tmp_path)
157 cid = _make_commit(tmp_path, content=b"cli-test")
158 _make_tag(tmp_path, "v3.0.0", cid)
159 result = runner.invoke(cli, ["describe"], env=_env(tmp_path))
160 assert result.exit_code == 0
161 assert "v3.0.0" in result.output
162
163
164 def test_describe_cli_json_output(tmp_path: pathlib.Path) -> None:
165 _init_repo(tmp_path)
166 cid = _make_commit(tmp_path, content=b"json-test")
167 _make_tag(tmp_path, "v4.0.0", cid)
168 result = runner.invoke(cli, ["describe", "--json"], env=_env(tmp_path))
169 assert result.exit_code == 0
170 data = json.loads(result.output)
171 assert data["tag"] == "v4.0.0"
172 assert data["distance"] == 0
173 assert "commit_id" in data
174
175
176 def test_describe_cli_require_tag_fails_without_tags(tmp_path: pathlib.Path) -> None:
177 _init_repo(tmp_path)
178 _make_commit(tmp_path, content=b"no-tags")
179 result = runner.invoke(cli, ["describe", "--require-tag"], env=_env(tmp_path))
180 assert result.exit_code != 0
181
182
183 def test_describe_cli_long_flag(tmp_path: pathlib.Path) -> None:
184 _init_repo(tmp_path)
185 cid = _make_commit(tmp_path, content=b"long")
186 _make_tag(tmp_path, "v5.0.0", cid)
187 result = runner.invoke(cli, ["describe", "--long"], env=_env(tmp_path))
188 assert result.exit_code == 0
189 assert "v5.0.0-0-g" in result.output
190
191
192 def test_describe_cli_short_flags(tmp_path: pathlib.Path) -> None:
193 _init_repo(tmp_path)
194 cid = _make_commit(tmp_path, content=b"short-flags")
195 _make_tag(tmp_path, "v6.0.0", cid)
196 result = runner.invoke(cli, ["describe", "-l", "--json"], env=_env(tmp_path))
197 assert result.exit_code == 0
198 data = json.loads(result.output)
199 assert "v6.0.0" in data["name"]
200
201
202 # ---------------------------------------------------------------------------
203 # Stress: deep ancestry (100 commits, tag at root)
204 # ---------------------------------------------------------------------------
205
206
207 def test_describe_stress_deep_ancestry(tmp_path: pathlib.Path) -> None:
208 _init_repo(tmp_path)
209 prev: str | None = None
210 first_commit_id = ""
211 for i in range(100):
212 cid = _make_commit(tmp_path, parent_id=prev, content=f"step {i}".encode())
213 if i == 0:
214 first_commit_id = cid
215 prev = cid
216
217 _make_tag(tmp_path, "v-root", first_commit_id)
218 assert prev is not None
219 result = describe_commit(tmp_path, _REPO_ID, prev)
220 assert result["tag"] == "v-root"
221 assert result["distance"] == 99
222 assert "v-root~99" == result["name"]
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