gabriel / muse public
test_cmd_describe_hardening.py python
847 lines 30.5 KB
Raw
sha256:b636f72dcba9e190afb980bece906fa5b717fbde014b76ef023df8cb96e01eb9 docs: expand cache plan with all seven testing tiers and do… Sonnet 4.6 139 days ago
1 """Hardening test suite for ``muse describe``.
2
3 Coverage:
4 - Unit: describe_commit core — no tags, exact, distance, long, match_pattern,
5 first_parent, abbrev, exact_match, _MAX_WALK budget, multi-tag tie-break
6 - Security: ANSI injection in tag names sanitized in text output,
7 raw in JSON; --ref ANSI passthrough in error message
8 - Error routing: all user errors routed to stderr
9 - JSON schema: _DescribeJson shape, all fields present, repo_id + branch
10 - New flags: --match, --exact-match, --first-parent, --abbrev, --json
11 - Integration: tag walk across merge commits, --first-parent vs full walk
12 - E2E: help output, combined flags
13 - Stress: 5 000-commit chain, 200-tag repo, concurrent reads
14 """
15
16 from __future__ import annotations
17
18 import datetime
19 import json
20 import pathlib
21 import threading
22 from typing import TypedDict
23 from unittest.mock import patch
24
25 import pytest
26 from tests.cli_test_helper import CliRunner, InvokeResult
27
28 from muse.core.describe import describe_commit, _MAX_WALK
29 from muse.core.object_store import write_object
30 from muse.core.snapshot import compute_commit_id, compute_snapshot_id
31 from muse.core.store import CommitRecord, SnapshotRecord, TagRecord, write_commit, write_snapshot, write_tag
32 from muse.core._types import Manifest, blob_id, fake_id, content_hash
33
34 runner = CliRunner()
35 _REPO_ID = content_hash({"name": "describe-hard-test"})
36
37
38 # ---------------------------------------------------------------------------
39 # Helpers
40 # ---------------------------------------------------------------------------
41
42
43 def _sha(data: bytes) -> str:
44 return blob_id(data)
45
46
47 def _init_repo(path: pathlib.Path, *, domain: str = "midi") -> pathlib.Path:
48 muse = path / ".muse"
49 for sub in ("commits", "snapshots", "objects", "refs/heads", "tags"):
50 (muse / sub).mkdir(parents=True, exist_ok=True)
51 (muse / "HEAD").write_text("ref: refs/heads/main", encoding="utf-8")
52 (muse / "repo.json").write_text(
53 json.dumps({"repo_id": _REPO_ID, "domain": domain}),
54 encoding="utf-8",
55 )
56 return path
57
58
59 def _make_commit(
60 root: pathlib.Path,
61 parent_id: str | None = None,
62 parent2_id: str | None = None,
63 content: bytes = b"data",
64 branch: str = "main",
65 ) -> str:
66 obj_id = _sha(content)
67 write_object(root, obj_id, content)
68 manifest = {f"f_{obj_id[len('sha256:'):len('sha256:') + 8]}.txt": obj_id}
69 snap_id = compute_snapshot_id(manifest)
70 write_snapshot(root, SnapshotRecord(snapshot_id=snap_id, manifest=manifest))
71 committed_at = datetime.datetime.now(datetime.timezone.utc)
72 parent_ids = [pid for pid in (parent_id, parent2_id) if pid is not None]
73 commit_id = compute_commit_id(
74 repo_id=_REPO_ID,
75 parent_ids=parent_ids,
76 snapshot_id=snap_id,
77 message="msg",
78 committed_at_iso=committed_at.isoformat(),
79 )
80 rec = CommitRecord(
81 commit_id=commit_id,
82 repo_id=_REPO_ID,
83 created_on_branch=branch,
84 snapshot_id=snap_id,
85 message="msg",
86 committed_at=committed_at,
87 parent_commit_id=parent_id,
88 parent2_commit_id=parent2_id,
89 )
90 write_commit(root, rec)
91 (root / ".muse" / "refs" / "heads" / branch).write_text(
92 commit_id, encoding="utf-8"
93 )
94 return commit_id
95
96
97 def _make_tag(root: pathlib.Path, tag: str, commit_id: str) -> None:
98 write_tag(
99 root,
100 TagRecord(
101 tag_id=content_hash({"tag": tag, "commit_id": commit_id}),
102 tag=tag,
103 commit_id=commit_id,
104 repo_id=_REPO_ID,
105 created_at=datetime.datetime.now(datetime.timezone.utc),
106 ),
107 )
108
109
110 def _env(repo: pathlib.Path) -> Manifest:
111 return {"MUSE_REPO_ROOT": str(repo)}
112
113
114 def _invoke(args: list[str], env: Manifest) -> InvokeResult:
115 return runner.invoke(None, args, env=env)
116
117
118 class _DescribeOut(TypedDict):
119 commit_id: str
120 tag: str | None
121 distance: int
122 short_sha: str
123 name: str
124 exact: bool
125 repo_id: str
126 branch: str
127 duration_ms: float
128 exit_code: int
129
130
131 def _parse_json(result: InvokeResult) -> _DescribeOut:
132 raw = json.loads(result.output.strip())
133 return _DescribeOut(
134 commit_id=raw["commit_id"],
135 tag=raw["tag"],
136 distance=raw["distance"],
137 short_sha=raw["short_sha"],
138 name=raw["name"],
139 exact=raw["exact"],
140 repo_id=raw["repo_id"],
141 branch=raw["branch"],
142 duration_ms=raw["duration_ms"],
143 exit_code=raw["exit_code"],
144 )
145
146
147 # ---------------------------------------------------------------------------
148 # Unit: describe_commit core
149 # ---------------------------------------------------------------------------
150
151
152 def test_core_no_tags_returns_short_sha(tmp_path: pathlib.Path) -> None:
153 _init_repo(tmp_path)
154 cid = _make_commit(tmp_path, content=b"a")
155 r = describe_commit(tmp_path, _REPO_ID, cid)
156 assert r["tag"] is None
157 assert r["short_sha"] == cid[:len("sha256:") + 12]
158 assert r["name"] == r["short_sha"]
159 assert r["exact"] is False
160
161
162 def test_core_exact_tag(tmp_path: pathlib.Path) -> None:
163 _init_repo(tmp_path)
164 cid = _make_commit(tmp_path, content=b"b")
165 _make_tag(tmp_path, "v1.0.0", cid)
166 r = describe_commit(tmp_path, _REPO_ID, cid)
167 assert r["tag"] == "v1.0.0"
168 assert r["distance"] == 0
169 assert r["exact"] is True
170 assert r["name"] == "v1.0.0"
171
172
173 def test_core_distance_one(tmp_path: pathlib.Path) -> None:
174 _init_repo(tmp_path)
175 c1 = _make_commit(tmp_path, content=b"c1")
176 _make_tag(tmp_path, "v0.9", c1)
177 c2 = _make_commit(tmp_path, parent_id=c1, content=b"c2")
178 r = describe_commit(tmp_path, _REPO_ID, c2)
179 assert r["tag"] == "v0.9"
180 assert r["distance"] == 1
181 assert r["exact"] is False
182 assert r["name"] == "v0.9~1"
183
184
185 def test_core_long_format_on_tag(tmp_path: pathlib.Path) -> None:
186 _init_repo(tmp_path)
187 cid = _make_commit(tmp_path, content=b"long")
188 _make_tag(tmp_path, "v2.0.0", cid)
189 r = describe_commit(tmp_path, _REPO_ID, cid, long_format=True)
190 assert r["name"].startswith("v2.0.0-0-sha256:")
191
192
193 def test_core_long_format_with_distance(tmp_path: pathlib.Path) -> None:
194 _init_repo(tmp_path)
195 c1 = _make_commit(tmp_path, content=b"root")
196 _make_tag(tmp_path, "v1.0", c1)
197 c2 = _make_commit(tmp_path, parent_id=c1, content=b"next")
198 r = describe_commit(tmp_path, _REPO_ID, c2, long_format=True)
199 assert "-1-sha256:" in r["name"]
200
201
202 def test_core_abbrev_controls_sha_length(tmp_path: pathlib.Path) -> None:
203 _init_repo(tmp_path)
204 cid = _make_commit(tmp_path, content=b"abbrev")
205 r = describe_commit(tmp_path, _REPO_ID, cid, abbrev=8)
206 assert r["short_sha"] == cid[:len("sha256:") + 8]
207 assert r["short_sha"].startswith("sha256:")
208 assert len(r["short_sha"]) == len("sha256:") + 8
209
210
211 def test_core_match_pattern_filters_tags(tmp_path: pathlib.Path) -> None:
212 _init_repo(tmp_path)
213 cid = _make_commit(tmp_path, content=b"match")
214 _make_tag(tmp_path, "release-1", cid)
215 _make_tag(tmp_path, "v1.0.0", cid)
216 # Only semver tags — "release-1" excluded.
217 r = describe_commit(tmp_path, _REPO_ID, cid, match_pattern="v*")
218 assert r["tag"] == "v1.0.0"
219
220
221 def test_core_match_pattern_no_match_returns_sha(tmp_path: pathlib.Path) -> None:
222 _init_repo(tmp_path)
223 cid = _make_commit(tmp_path, content=b"nomatch")
224 _make_tag(tmp_path, "nightly-123", cid)
225 r = describe_commit(tmp_path, _REPO_ID, cid, match_pattern="v*")
226 assert r["tag"] is None
227 assert r["name"] == cid[:len("sha256:") + 12]
228
229
230 def test_core_exact_match_on_tag(tmp_path: pathlib.Path) -> None:
231 _init_repo(tmp_path)
232 cid = _make_commit(tmp_path, content=b"exact")
233 _make_tag(tmp_path, "v3.0", cid)
234 r = describe_commit(tmp_path, _REPO_ID, cid, exact_match=True)
235 assert r["tag"] == "v3.0"
236 assert r["exact"] is True
237
238
239 def test_core_exact_match_off_tag_returns_sha(tmp_path: pathlib.Path) -> None:
240 _init_repo(tmp_path)
241 c1 = _make_commit(tmp_path, content=b"root")
242 _make_tag(tmp_path, "v3.0", c1)
243 c2 = _make_commit(tmp_path, parent_id=c1, content=b"after")
244 r = describe_commit(tmp_path, _REPO_ID, c2, exact_match=True)
245 assert r["tag"] is None
246 assert r["name"] == c2[:len("sha256:") + 12]
247
248
249 def test_core_first_parent_skips_merge_branch(tmp_path: pathlib.Path) -> None:
250 """With --first-parent the tag on a merged branch is invisible."""
251 _init_repo(tmp_path)
252 # main: c1 → c3 (merge of feat)
253 # feat: c1 → c2 (tag here)
254 c1 = _make_commit(tmp_path, content=b"root")
255 c2 = _make_commit(tmp_path, parent_id=c1, content=b"feat", branch="feat")
256 _make_tag(tmp_path, "feat-tag", c2)
257 c3 = _make_commit(
258 tmp_path, parent_id=c1, parent2_id=c2, content=b"merge", branch="main"
259 )
260 # Without first_parent: feat-tag is reachable via second parent.
261 r_full = describe_commit(tmp_path, _REPO_ID, c3)
262 # With first_parent: only first parent chain; feat-tag not reachable.
263 r_fp = describe_commit(tmp_path, _REPO_ID, c3, first_parent=True)
264 assert r_fp["tag"] is None
265 # Full walk should find the tag (via c2).
266 assert r_full["tag"] == "feat-tag"
267
268
269 def test_core_multi_tag_same_commit_lex_greatest(tmp_path: pathlib.Path) -> None:
270 """When multiple tags point at the same commit, greatest lex name wins."""
271 _init_repo(tmp_path)
272 cid = _make_commit(tmp_path, content=b"multi")
273 _make_tag(tmp_path, "v1.0.0", cid)
274 _make_tag(tmp_path, "v2.0.0", cid)
275 _make_tag(tmp_path, "v1.5.0", cid)
276 r = describe_commit(tmp_path, _REPO_ID, cid)
277 assert r["tag"] == "v2.0.0"
278
279
280 def test_core_max_walk_budget(tmp_path: pathlib.Path) -> None:
281 """Walk stops at _MAX_WALK without crashing; returns short-SHA fallback."""
282 _init_repo(tmp_path)
283 # Inject a fake read_commit that always returns a parent so BFS never
284 # finds a commit-store miss — we just want to trigger the budget guard.
285 cid = _make_commit(tmp_path, content=b"budget")
286 _make_tag(tmp_path, "very-far", cid)
287
288 call_count = 0
289
290 import muse.core.store as _store
291 from muse.core.store import CommitRecord as _CR
292 import datetime as _dt
293
294 orig = _store.read_commit
295
296 def _fake_read(root: pathlib.Path, cid: str) -> _CR | None:
297 nonlocal call_count
298 call_count += 1
299 if call_count > _MAX_WALK + 5:
300 return None
301 fake_parent = fake_id(cid)
302 return _CR(
303 commit_id=cid,
304 repo_id=_REPO_ID,
305 created_on_branch="main",
306 snapshot_id="snap",
307 message="x",
308 committed_at=_dt.datetime.now(_dt.timezone.utc),
309 parent_commit_id=fake_parent,
310 )
311
312 with patch.object(_store, "read_commit", side_effect=_fake_read):
313 # Start from a commit far from any tag — BFS will exhaust budget.
314 far_commit = blob_id(b"far")
315 r = describe_commit(tmp_path, _REPO_ID, far_commit)
316
317 # Budget exhausted → tag not found → name is short SHA.
318 assert r["tag"] is None
319
320
321 # ---------------------------------------------------------------------------
322 # Security: ANSI injection in tag names
323 # ---------------------------------------------------------------------------
324
325
326 def test_ansi_in_tag_name_stripped_in_text_output(tmp_path: pathlib.Path) -> None:
327 _init_repo(tmp_path)
328 cid = _make_commit(tmp_path, content=b"ansi")
329 evil_tag = "v1.0\x1b[31mRED\x1b[0m"
330 _make_tag(tmp_path, evil_tag, cid)
331 result = _invoke(["describe"], _env(tmp_path))
332 assert result.exit_code == 0
333 assert "\x1b[31m" not in result.output
334
335
336 def test_ansi_in_tag_name_preserved_in_json(tmp_path: pathlib.Path) -> None:
337 """JSON output must not sanitize so callers see the raw value."""
338 _init_repo(tmp_path)
339 cid = _make_commit(tmp_path, content=b"ansi-json")
340 evil_tag = "v1.0\x1b[31mRED\x1b[0m"
341 _make_tag(tmp_path, evil_tag, cid)
342 result = _invoke(["describe", "--json"], _env(tmp_path))
343 assert result.exit_code == 0
344 data = _parse_json(result)
345 assert data["tag"] == evil_tag
346
347
348 # ---------------------------------------------------------------------------
349 # Error routing: all user errors go to stderr
350 # ---------------------------------------------------------------------------
351
352
353 def test_no_commits_error_on_stderr(tmp_path: pathlib.Path) -> None:
354 _init_repo(tmp_path)
355 result = _invoke(["describe"], _env(tmp_path))
356 assert result.exit_code != 0
357 assert result.stderr != "" or "commits" in result.output.lower()
358
359
360 def test_ref_not_found_error_on_stderr(tmp_path: pathlib.Path) -> None:
361 _init_repo(tmp_path)
362 _make_commit(tmp_path, content=b"x")
363 result = _invoke(["describe", "--ref", "nonexistent"], _env(tmp_path))
364 assert result.exit_code != 0
365
366
367 def test_require_tag_no_tags_error(tmp_path: pathlib.Path) -> None:
368 _init_repo(tmp_path)
369 _make_commit(tmp_path, content=b"no-tag")
370 result = _invoke(["describe", "--require-tag"], _env(tmp_path))
371 assert result.exit_code != 0
372
373
374 def test_exact_match_not_on_tag_error(tmp_path: pathlib.Path) -> None:
375 _init_repo(tmp_path)
376 c1 = _make_commit(tmp_path, content=b"c1")
377 _make_tag(tmp_path, "v1", c1)
378 _make_commit(tmp_path, parent_id=c1, content=b"c2")
379 result = _invoke(["describe", "--exact-match"], _env(tmp_path))
380 assert result.exit_code != 0
381
382
383 def test_abbrev_too_small_error(tmp_path: pathlib.Path) -> None:
384 _init_repo(tmp_path)
385 _make_commit(tmp_path, content=b"ab")
386 result = _invoke(["describe", "--abbrev", "2"], _env(tmp_path))
387 assert result.exit_code != 0
388
389
390 def test_abbrev_too_large_error(tmp_path: pathlib.Path) -> None:
391 _init_repo(tmp_path)
392 _make_commit(tmp_path, content=b"ab")
393 result = _invoke(["describe", "--abbrev", "65"], _env(tmp_path))
394 assert result.exit_code != 0
395
396
397 # ---------------------------------------------------------------------------
398 # JSON schema: _DescribeJson
399 # ---------------------------------------------------------------------------
400
401
402 def test_json_schema_all_fields(tmp_path: pathlib.Path) -> None:
403 _init_repo(tmp_path)
404 cid = _make_commit(tmp_path, content=b"schema")
405 _make_tag(tmp_path, "v1.0.0", cid)
406 result = _invoke(["describe", "--json"], _env(tmp_path))
407 assert result.exit_code == 0
408 data = _parse_json(result)
409 assert data["tag"] == "v1.0.0"
410 assert data["distance"] == 0
411 assert data["exact"] is True
412 assert data["repo_id"] == _REPO_ID
413 assert data["branch"] == "main"
414 assert data["commit_id"] == cid
415 assert data["short_sha"] == cid[:len("sha256:") + 12]
416
417
418 def test_json_schema_no_tag(tmp_path: pathlib.Path) -> None:
419 _init_repo(tmp_path)
420 cid = _make_commit(tmp_path, content=b"no-tag-json")
421 result = _invoke(["describe", "--json"], _env(tmp_path))
422 assert result.exit_code == 0
423 data = _parse_json(result)
424 assert data["tag"] is None
425 assert data["name"] == cid[:len("sha256:") + 12]
426 assert data["exact"] is False
427
428
429 def test_json_schema_with_distance(tmp_path: pathlib.Path) -> None:
430 _init_repo(tmp_path)
431 c1 = _make_commit(tmp_path, content=b"root")
432 _make_tag(tmp_path, "v0.1", c1)
433 _make_commit(tmp_path, parent_id=c1, content=b"next")
434 result = _invoke(["describe", "--json"], _env(tmp_path))
435 assert result.exit_code == 0
436 data = _parse_json(result)
437 assert data["tag"] == "v0.1"
438 assert data["distance"] == 1
439 assert data["exact"] is False
440
441
442 def test_json_abbrev_reflected(tmp_path: pathlib.Path) -> None:
443 _init_repo(tmp_path)
444 cid = _make_commit(tmp_path, content=b"abbrev-json")
445 result = _invoke(["describe", "--abbrev", "8", "--json"], _env(tmp_path))
446 assert result.exit_code == 0
447 data = _parse_json(result)
448 assert data["short_sha"].startswith("sha256:")
449 assert len(data["short_sha"]) == len("sha256:") + 8
450
451
452 # ---------------------------------------------------------------------------
453 # New flags: --match, --exact-match, --first-parent, --abbrev
454 # ---------------------------------------------------------------------------
455
456
457 def test_flag_match_filters_tags(tmp_path: pathlib.Path) -> None:
458 _init_repo(tmp_path)
459 cid = _make_commit(tmp_path, content=b"match-flag")
460 _make_tag(tmp_path, "nightly-1", cid)
461 _make_tag(tmp_path, "v1.0.0", cid)
462 result = _invoke(["describe", "--match", "v*", "--json"], _env(tmp_path))
463 assert result.exit_code == 0
464 data = _parse_json(result)
465 assert data["tag"] == "v1.0.0"
466
467
468 def test_flag_match_no_matching_tag(tmp_path: pathlib.Path) -> None:
469 _init_repo(tmp_path)
470 cid = _make_commit(tmp_path, content=b"match-none")
471 _make_tag(tmp_path, "nightly-1", cid)
472 result = _invoke(["describe", "--match", "v*", "--json"], _env(tmp_path))
473 assert result.exit_code == 0
474 data = _parse_json(result)
475 assert data["tag"] is None
476
477
478 def test_flag_exact_match_on_tag(tmp_path: pathlib.Path) -> None:
479 _init_repo(tmp_path)
480 cid = _make_commit(tmp_path, content=b"exact-flag")
481 _make_tag(tmp_path, "v1.0", cid)
482 result = _invoke(["describe", "--exact-match", "--json"], _env(tmp_path))
483 assert result.exit_code == 0
484 data = _parse_json(result)
485 assert data["exact"] is True
486
487
488 def test_flag_exact_match_off_tag_fails(tmp_path: pathlib.Path) -> None:
489 _init_repo(tmp_path)
490 c1 = _make_commit(tmp_path, content=b"em-root")
491 _make_tag(tmp_path, "v1.0", c1)
492 _make_commit(tmp_path, parent_id=c1, content=b"em-next")
493 result = _invoke(["describe", "--exact-match"], _env(tmp_path))
494 assert result.exit_code != 0
495
496
497 def test_flag_first_parent(tmp_path: pathlib.Path) -> None:
498 _init_repo(tmp_path)
499 c1 = _make_commit(tmp_path, content=b"fp-root")
500 c2 = _make_commit(tmp_path, parent_id=c1, content=b"feat-side", branch="feat")
501 _make_tag(tmp_path, "side-tag", c2)
502 c3 = _make_commit(
503 tmp_path, parent_id=c1, parent2_id=c2, content=b"fp-merge", branch="main"
504 )
505 # --first-parent should not see side-tag.
506 result = _invoke(["describe", "--first-parent", "--json"], _env(tmp_path))
507 assert result.exit_code == 0
508 data = _parse_json(result)
509 assert data["tag"] is None # side-tag not reachable via first-parent
510
511
512 def test_flag_abbrev(tmp_path: pathlib.Path) -> None:
513 _init_repo(tmp_path)
514 _make_commit(tmp_path, content=b"abbrev-flag")
515 result = _invoke(["describe", "--abbrev", "16", "--json"], _env(tmp_path))
516 assert result.exit_code == 0
517 data = _parse_json(result)
518 assert len(data["short_sha"]) == len("sha256:") + 16
519
520
521 # ---------------------------------------------------------------------------
522 # Integration
523 # ---------------------------------------------------------------------------
524
525
526 def test_integration_ref_to_branch_tip(tmp_path: pathlib.Path) -> None:
527 _init_repo(tmp_path)
528 c1 = _make_commit(tmp_path, content=b"ref-root")
529 _make_tag(tmp_path, "v10.0", c1)
530 _make_commit(tmp_path, parent_id=c1, content=b"ref-next")
531 # Describe the HEAD (which is 1 hop past the tag).
532 result = _invoke(["describe", "--json"], _env(tmp_path))
533 assert result.exit_code == 0
534 data = _parse_json(result)
535 assert data["distance"] == 1
536 assert data["tag"] == "v10.0"
537
538
539 def test_integration_long_and_match_combined(tmp_path: pathlib.Path) -> None:
540 _init_repo(tmp_path)
541 cid = _make_commit(tmp_path, content=b"combo")
542 _make_tag(tmp_path, "v5.0.0", cid)
543 result = _invoke(
544 ["describe", "--long", "--match", "v*", "--json"], _env(tmp_path)
545 )
546 assert result.exit_code == 0
547 data = _parse_json(result)
548 assert data["name"].startswith("v5.0.0-0-sha256:")
549
550
551 def test_integration_require_tag_passes_when_tag_exists(
552 tmp_path: pathlib.Path,
553 ) -> None:
554 _init_repo(tmp_path)
555 cid = _make_commit(tmp_path, content=b"req-tag")
556 _make_tag(tmp_path, "v7.0", cid)
557 result = _invoke(["describe", "--require-tag", "--json"], _env(tmp_path))
558 assert result.exit_code == 0
559
560
561 def test_integration_text_output_sanitized(tmp_path: pathlib.Path) -> None:
562 _init_repo(tmp_path)
563 cid = _make_commit(tmp_path, content=b"text-sanitize")
564 _make_tag(tmp_path, "v1.0\x1b[1mBOLD\x1b[0m", cid)
565 result = _invoke(["describe"], _env(tmp_path))
566 assert result.exit_code == 0
567 assert "\x1b[1m" not in result.output
568
569
570 # ---------------------------------------------------------------------------
571 # E2E: help output
572 # ---------------------------------------------------------------------------
573
574
575 def test_help_contains_new_flags() -> None:
576 result = _invoke(["describe", "--help"], {})
577 assert result.exit_code == 0
578 for flag in ("--match", "--exact-match", "--first-parent", "--abbrev", "--json"):
579 assert flag in result.output, f"Missing flag in help: {flag}"
580
581
582 def test_help_mentions_json_schema() -> None:
583 result = _invoke(["describe", "--help"], {})
584 assert "json" in result.output.lower()
585
586
587 # ---------------------------------------------------------------------------
588 # Stress: deep ancestry + many tags + concurrent reads
589 # ---------------------------------------------------------------------------
590
591
592 def test_stress_5000_commit_chain(tmp_path: pathlib.Path) -> None:
593 _init_repo(tmp_path)
594 prev: str | None = None
595 root_cid = ""
596 for i in range(5_000):
597 cid = _make_commit(tmp_path, parent_id=prev, content=f"s{i}".encode())
598 if i == 0:
599 root_cid = cid
600 prev = cid
601
602 _make_tag(tmp_path, "v-deep", root_cid)
603 assert prev is not None
604 r = describe_commit(tmp_path, _REPO_ID, prev)
605 assert r["tag"] == "v-deep"
606 assert r["distance"] == 4_999
607
608
609 def test_stress_200_tags_repo(tmp_path: pathlib.Path) -> None:
610 """Many tags — describe still picks the nearest one efficiently."""
611 _init_repo(tmp_path)
612 commits: list[str] = []
613 prev: str | None = None
614 for i in range(200):
615 cid = _make_commit(tmp_path, parent_id=prev, content=f"t{i}".encode())
616 commits.append(cid)
617 # Tag every 10th commit.
618 if i % 10 == 0:
619 _make_tag(tmp_path, f"v{i}.0", cid)
620 prev = cid
621
622 # HEAD is commits[-1], nearest tag is v190.0 (at commits[190]).
623 r = describe_commit(tmp_path, _REPO_ID, commits[-1])
624 assert r["tag"] == "v190.0"
625 assert r["distance"] == 9
626
627
628 def test_stress_concurrent_describe(tmp_path: pathlib.Path) -> None:
629 """Concurrent --json calls must all return consistent, valid JSON."""
630 _init_repo(tmp_path)
631 c1 = _make_commit(tmp_path, content=b"conc-root")
632 _make_tag(tmp_path, "v-conc", c1)
633 _make_commit(tmp_path, parent_id=c1, content=b"conc-next")
634
635 invoke_lock = threading.Lock()
636 errors: list[str] = []
637
638 def _worker() -> None:
639 with invoke_lock:
640 r = _invoke(["describe", "--json"], _env(tmp_path))
641 try:
642 assert r.exit_code == 0
643 data = _parse_json(r)
644 assert data["tag"] == "v-conc"
645 assert data["distance"] == 1
646 except Exception as exc:
647 errors.append(str(exc))
648
649 threads = [threading.Thread(target=_worker) for _ in range(8)]
650 for t in threads:
651 t.start()
652 for t in threads:
653 t.join()
654
655 assert errors == [], f"Concurrent failures: {errors}"
656
657
658 # ---------------------------------------------------------------------------
659 # JSON schema: duration_ms + exit_code always present
660 # ---------------------------------------------------------------------------
661
662
663 class TestJsonSchemaComplete:
664 """Every --json path includes duration_ms and exit_code."""
665
666 def test_elapsed_present_on_tag(self, tmp_path: pathlib.Path) -> None:
667 _init_repo(tmp_path)
668 cid = _make_commit(tmp_path, content=b"sc-tag")
669 _make_tag(tmp_path, "v1.0.0", cid)
670 r = _invoke(["describe", "--json"], _env(tmp_path))
671 assert r.exit_code == 0
672 raw = json.loads(r.output)
673 assert "duration_ms" in raw
674 assert "exit_code" in raw
675
676 def test_elapsed_present_no_tag(self, tmp_path: pathlib.Path) -> None:
677 _init_repo(tmp_path)
678 _make_commit(tmp_path, content=b"sc-notag")
679 r = _invoke(["describe", "--json"], _env(tmp_path))
680 assert r.exit_code == 0
681 raw = json.loads(r.output)
682 assert "duration_ms" in raw
683 assert "exit_code" in raw
684
685 def test_elapsed_present_with_distance(self, tmp_path: pathlib.Path) -> None:
686 _init_repo(tmp_path)
687 c1 = _make_commit(tmp_path, content=b"sc-dist-root")
688 _make_tag(tmp_path, "v0.1", c1)
689 _make_commit(tmp_path, parent_id=c1, content=b"sc-dist-next")
690 r = _invoke(["describe", "--json"], _env(tmp_path))
691 assert r.exit_code == 0
692 raw = json.loads(r.output)
693 assert "duration_ms" in raw
694 assert raw["exit_code"] == 0
695
696 def test_elapsed_present_long_format(self, tmp_path: pathlib.Path) -> None:
697 _init_repo(tmp_path)
698 cid = _make_commit(tmp_path, content=b"sc-long")
699 _make_tag(tmp_path, "v2.0.0", cid)
700 r = _invoke(["describe", "--long", "--json"], _env(tmp_path))
701 assert r.exit_code == 0
702 raw = json.loads(r.output)
703 assert "duration_ms" in raw
704 assert raw["exit_code"] == 0
705
706 def test_elapsed_present_with_match(self, tmp_path: pathlib.Path) -> None:
707 _init_repo(tmp_path)
708 cid = _make_commit(tmp_path, content=b"sc-match")
709 _make_tag(tmp_path, "v3.0.0", cid)
710 r = _invoke(["describe", "--match", "v*", "--json"], _env(tmp_path))
711 assert r.exit_code == 0
712 raw = json.loads(r.output)
713 assert "duration_ms" in raw
714
715 def test_elapsed_present_with_abbrev(self, tmp_path: pathlib.Path) -> None:
716 _init_repo(tmp_path)
717 _make_commit(tmp_path, content=b"sc-abbrev")
718 r = _invoke(["describe", "--abbrev", "8", "--json"], _env(tmp_path))
719 assert r.exit_code == 0
720 raw = json.loads(r.output)
721 assert "duration_ms" in raw
722 assert raw["exit_code"] == 0
723
724 def test_all_eight_base_fields_present(self, tmp_path: pathlib.Path) -> None:
725 _init_repo(tmp_path)
726 cid = _make_commit(tmp_path, content=b"sc-all")
727 _make_tag(tmp_path, "v9.0.0", cid)
728 r = _invoke(["describe", "--json"], _env(tmp_path))
729 assert r.exit_code == 0
730 raw = json.loads(r.output)
731 for field in ("commit_id", "tag", "distance", "short_sha", "name",
732 "exact", "repo_id", "branch", "duration_ms", "exit_code"):
733 assert field in raw, f"Missing field: {field}"
734
735 def test_exit_code_field_is_zero(self, tmp_path: pathlib.Path) -> None:
736 _init_repo(tmp_path)
737 cid = _make_commit(tmp_path, content=b"sc-exit")
738 _make_tag(tmp_path, "v10.0.0", cid)
739 r = _invoke(["describe", "--json"], _env(tmp_path))
740 assert r.exit_code == 0
741 raw = json.loads(r.output)
742 assert raw["exit_code"] == 0
743
744
745 # ---------------------------------------------------------------------------
746 # duration_ms: type and magnitude checks
747 # ---------------------------------------------------------------------------
748
749
750 class TestElapsedSeconds:
751 """duration_ms is a non-negative float in a reasonable range."""
752
753 def test_elapsed_is_float(self, tmp_path: pathlib.Path) -> None:
754 _init_repo(tmp_path)
755 _make_commit(tmp_path, content=b"el-float")
756 r = _invoke(["describe", "--json"], _env(tmp_path))
757 raw = json.loads(r.output)
758 assert isinstance(raw["duration_ms"], float)
759
760 def test_elapsed_non_negative(self, tmp_path: pathlib.Path) -> None:
761 _init_repo(tmp_path)
762 _make_commit(tmp_path, content=b"el-nonneg")
763 r = _invoke(["describe", "--json"], _env(tmp_path))
764 raw = json.loads(r.output)
765 assert raw["duration_ms"] >= 0.0
766
767 def test_elapsed_under_ten_seconds(self, tmp_path: pathlib.Path) -> None:
768 _init_repo(tmp_path)
769 _make_commit(tmp_path, content=b"el-under")
770 r = _invoke(["describe", "--json"], _env(tmp_path))
771 raw = json.loads(r.output)
772 assert raw["duration_ms"] < 10.0
773
774 def test_elapsed_with_tag(self, tmp_path: pathlib.Path) -> None:
775 _init_repo(tmp_path)
776 cid = _make_commit(tmp_path, content=b"el-tag")
777 _make_tag(tmp_path, "v1.2.3", cid)
778 r = _invoke(["describe", "--json"], _env(tmp_path))
779 raw = json.loads(r.output)
780 assert raw["duration_ms"] >= 0.0
781
782 def test_elapsed_with_require_tag(self, tmp_path: pathlib.Path) -> None:
783 _init_repo(tmp_path)
784 cid = _make_commit(tmp_path, content=b"el-req")
785 _make_tag(tmp_path, "v1.0", cid)
786 r = _invoke(["describe", "--require-tag", "--json"], _env(tmp_path))
787 assert r.exit_code == 0
788 raw = json.loads(r.output)
789 assert "duration_ms" in raw
790
791 def test_elapsed_six_decimal_places(self, tmp_path: pathlib.Path) -> None:
792 _init_repo(tmp_path)
793 _make_commit(tmp_path, content=b"el-prec")
794 r = _invoke(["describe", "--json"], _env(tmp_path))
795 raw = json.loads(r.output)
796 # round(..., 6) produces at most 6 decimal places — str check
797 s = str(raw["duration_ms"])
798 dec = s.split(".")[-1] if "." in s else ""
799 assert len(dec) <= 6
800
801
802 # ---------------------------------------------------------------------------
803 # exit_code field
804 # ---------------------------------------------------------------------------
805
806
807 class TestExitCode:
808 """exit_code field mirrors process exit code; always 0 on success."""
809
810 def test_exit_code_zero_no_tag(self, tmp_path: pathlib.Path) -> None:
811 _init_repo(tmp_path)
812 _make_commit(tmp_path, content=b"ec-notag")
813 r = _invoke(["describe", "--json"], _env(tmp_path))
814 assert r.exit_code == 0
815 assert json.loads(r.output)["exit_code"] == 0
816
817 def test_exit_code_zero_on_tag(self, tmp_path: pathlib.Path) -> None:
818 _init_repo(tmp_path)
819 cid = _make_commit(tmp_path, content=b"ec-tag")
820 _make_tag(tmp_path, "v1.0.0", cid)
821 r = _invoke(["describe", "--json"], _env(tmp_path))
822 assert r.exit_code == 0
823 assert json.loads(r.output)["exit_code"] == 0
824
825 def test_exit_code_zero_with_distance(self, tmp_path: pathlib.Path) -> None:
826 _init_repo(tmp_path)
827 c1 = _make_commit(tmp_path, content=b"ec-dist-root")
828 _make_tag(tmp_path, "v0.5", c1)
829 _make_commit(tmp_path, parent_id=c1, content=b"ec-dist-next")
830 r = _invoke(["describe", "--json"], _env(tmp_path))
831 assert r.exit_code == 0
832 assert json.loads(r.output)["exit_code"] == 0
833
834 def test_exit_code_zero_long_format(self, tmp_path: pathlib.Path) -> None:
835 _init_repo(tmp_path)
836 cid = _make_commit(tmp_path, content=b"ec-long")
837 _make_tag(tmp_path, "v2.0.0", cid)
838 r = _invoke(["describe", "--long", "--json"], _env(tmp_path))
839 assert r.exit_code == 0
840 assert json.loads(r.output)["exit_code"] == 0
841
842 def test_exit_code_zero_with_abbrev(self, tmp_path: pathlib.Path) -> None:
843 _init_repo(tmp_path)
844 _make_commit(tmp_path, content=b"ec-abbrev")
845 r = _invoke(["describe", "--abbrev", "10", "--json"], _env(tmp_path))
846 assert r.exit_code == 0
847 assert json.loads(r.output)["exit_code"] == 0
File History 3 commits
sha256:b636f72dcba9e190afb980bece906fa5b717fbde014b76ef023df8cb96e01eb9 docs: expand cache plan with all seven testing tiers and do… Sonnet 4.6 139 days ago
sha256:7f9e2ef5286aedad9c1e6011b4c46ca27f39dbdad6e3409357e36b26e46b3b7c docs: docstring sprint for-each-ref→hotspots — idiomatic ru… Sonnet 4.6 patch 145 days ago
sha256:a09b1b4f6838754495547f200aa0ce88e2f56ffc5b20b900f6f0cff2c3cdede9 fix(cursorignore): remove git-ism (.git/worktrees) Human minor 148 days ago