gabriel / muse public
test_cmd_for_each_ref.py python
619 lines 22.2 KB
Raw
sha256:51ce277f663e01a43eaffbe77509b1de7ac2d4251b55d23306304bcdeb92c90d feat(pack): delta-encode snapshots in MPackBundle wire format Sonnet 4.6 minor ⚠ breaking 126 days ago
1 """Tests for muse for-each-ref.
2
3 Coverage tiers
4 --------------
5 Unit — _list_all_refs (flat, hierarchical, symlink skip, bad commit ID),
6 _RefDetail + _ForEachRefResult schemas, _SORT_FIELDS completeness
7 Integration — empty repo, flat branches, hierarchical branches, pattern filter,
8 sort (all fields, asc/desc), --count limit, --no-commits fast-path,
9 text output (full / no-commits), --json shorthand
10 Security — symlinks skipped, ANSI in branch/commit/author sanitized,
11 error output to stderr (format, sort, negative count),
12 no traceback on bad format/corrupted ref, no-commits+commit-sort rejected
13 Stress — 100-branch repo, 50-hierarchical-branch repo, 200 sequential reads
14 """
15
16 from __future__ import annotations
17
18 import argparse
19 import datetime
20 import json
21 import os
22 import pathlib
23
24 import pytest
25 from tests.cli_test_helper import CliRunner, InvokeResult
26
27 from muse.cli.commands.for_each_ref import (
28 _ForEachRefResult,
29 _RefDetail,
30 _SORT_FIELDS,
31 _list_all_refs,
32 )
33 from muse.core.snapshot import compute_commit_id, compute_snapshot_id
34 from muse.core.store import CommitRecord, SnapshotRecord, write_commit, write_snapshot
35 from muse.core.types import Manifest
36 from muse.core.paths import muse_dir, heads_dir, ref_path
37
38 cli = None # argparse-based CLI; CliRunner ignores this arg
39 runner = CliRunner()
40
41
42 # ---------------------------------------------------------------------------
43 # Helpers
44 # ---------------------------------------------------------------------------
45
46
47
48 def _init_repo(path: pathlib.Path) -> pathlib.Path:
49 muse = muse_dir(path)
50 (muse / "commits").mkdir(parents=True)
51 (muse / "snapshots").mkdir(parents=True)
52 (muse / "objects").mkdir(parents=True)
53 (muse / "refs" / "heads").mkdir(parents=True)
54 (muse / "HEAD").write_text("ref: refs/heads/main\n", encoding="utf-8")
55 (muse / "repo.json").write_text(
56 json.dumps({"repo_id": "test-repo", "domain": "midi"}), encoding="utf-8"
57 )
58 return path
59
60
61 def _env(repo: pathlib.Path) -> Manifest:
62 return {"MUSE_REPO_ROOT": str(repo)}
63
64
65 def _snap(repo: pathlib.Path, tag: str = "snap") -> str:
66 sid = compute_snapshot_id({})
67 write_snapshot(
68 repo,
69 SnapshotRecord(
70 snapshot_id=sid,
71 manifest={},
72 created_at=datetime.datetime(2026, 1, 1, tzinfo=datetime.timezone.utc),
73 ),
74 )
75 return sid
76
77
78 def _commit(
79 repo: pathlib.Path,
80 tag: str,
81 branch: str = "main",
82 parent: str | None = None,
83 ts: datetime.datetime | None = None,
84 author: str = "tester",
85 ) -> str:
86 sid = _snap(repo, tag)
87 ts_actual = ts or datetime.datetime(2026, 1, 1, tzinfo=datetime.timezone.utc)
88 parent_ids: list[str] = [parent] if parent else []
89 cid = compute_commit_id( parent_ids=parent_ids,
90 snapshot_id=sid,
91 message=tag,
92 committed_at_iso=ts_actual.isoformat(),
93 author=author,
94 )
95 write_commit(
96 repo,
97 CommitRecord(
98 commit_id=cid,
99 repo_id="test-repo",
100 branch=branch,
101 snapshot_id=sid,
102 message=tag,
103 committed_at=ts_actual,
104 author=author,
105 parent_commit_id=parent,
106 parent2_commit_id=None,
107 ),
108 )
109 branch_ref = ref_path(repo, branch)
110 branch_ref.parent.mkdir(parents=True, exist_ok=True)
111 branch_ref.write_text(cid, encoding="utf-8")
112 return cid
113
114
115 def _fer(repo: pathlib.Path, *args: str) -> InvokeResult:
116 return runner.invoke(cli, ["for-each-ref", "--json", *args], env=_env(repo))
117
118
119 def _fer_text(repo: pathlib.Path, *args: str) -> InvokeResult:
120 return runner.invoke(cli, ["for-each-ref", *args], env=_env(repo))
121
122
123 # ---------------------------------------------------------------------------
124 # Unit — flag registration
125 # ---------------------------------------------------------------------------
126
127
128 class TestRegisterFlags:
129 def _parse(self, *args: str) -> "argparse.Namespace":
130 import argparse
131 from muse.cli.commands.for_each_ref import register
132 p = argparse.ArgumentParser()
133 sub = p.add_subparsers()
134 register(sub)
135 return p.parse_args(["for-each-ref", *args])
136
137 def test_default_json_out_is_false(self) -> None:
138 ns = self._parse()
139 assert ns.json_out is False
140
141 def test_json_flag_sets_json_out(self) -> None:
142 ns = self._parse("--json")
143 assert ns.json_out is True
144
145 def test_j_shorthand_sets_json_out(self) -> None:
146 ns = self._parse("-j")
147 assert ns.json_out is True
148
149
150 # ---------------------------------------------------------------------------
151 # Unit — schema
152 # ---------------------------------------------------------------------------
153
154
155 class TestSchemas:
156 def test_sort_fields_includes_snapshot_id(self) -> None:
157 assert "snapshot_id" in _SORT_FIELDS
158
159 def test_sort_fields_includes_all_expected(self) -> None:
160 for f in ("ref", "branch", "commit_id", "author", "committed_at", "message"):
161 assert f in _SORT_FIELDS
162
163 def test_for_each_ref_result_fields(self) -> None:
164 keys = _ForEachRefResult.__annotations__
165 assert "refs" in keys
166 assert "count" in keys
167
168 def test_ref_detail_is_total_false(self) -> None:
169 # total=False allows partial dicts for --no-commits mode.
170 # __required_keys__ is empty when total=False.
171 assert len(_RefDetail.__required_keys__) == 0
172
173
174 # ---------------------------------------------------------------------------
175 # Unit — _list_all_refs
176 # ---------------------------------------------------------------------------
177
178
179 class TestListAllRefs:
180 def test_empty_heads_dir(self, tmp_path: pathlib.Path) -> None:
181 _init_repo(tmp_path)
182 assert _list_all_refs(tmp_path) == []
183
184 def test_flat_branch(self, tmp_path: pathlib.Path) -> None:
185 _init_repo(tmp_path)
186 _commit(tmp_path, "c", "main")
187 pairs = _list_all_refs(tmp_path)
188 assert len(pairs) == 1
189 assert pairs[0][0] == "main"
190
191 def test_hierarchical_branch_discovered(self, tmp_path: pathlib.Path) -> None:
192 """feat/my-thing must be found — requires rglob, not iterdir."""
193 _init_repo(tmp_path)
194 _commit(tmp_path, "c-main", "main")
195 _commit(tmp_path, "c-feat", "feat/my-thing")
196 pairs = _list_all_refs(tmp_path)
197 branch_names = [b for b, _ in pairs]
198 assert "feat/my-thing" in branch_names
199 assert "main" in branch_names
200
201 def test_symlink_ref_skipped(self, tmp_path: pathlib.Path) -> None:
202 _init_repo(tmp_path)
203 _commit(tmp_path, "c", "main")
204 real = heads_dir(tmp_path) / "main"
205 link = heads_dir(tmp_path) / "sym"
206 link.symlink_to(real)
207 pairs = _list_all_refs(tmp_path)
208 names = [b for b, _ in pairs]
209 assert "sym" not in names
210 assert "main" in names
211
212 def test_invalid_commit_id_skipped(self, tmp_path: pathlib.Path) -> None:
213 _init_repo(tmp_path)
214 _commit(tmp_path, "c", "main")
215 # Write a ref file with garbage content
216 bad = heads_dir(tmp_path) / "bad-ref"
217 bad.write_text("not-a-sha256\n", encoding="utf-8")
218 pairs = _list_all_refs(tmp_path)
219 names = [b for b, _ in pairs]
220 assert "bad-ref" not in names
221 assert "main" in names
222
223 def test_missing_heads_dir_returns_empty(self, tmp_path: pathlib.Path) -> None:
224 _init_repo(tmp_path)
225 import shutil
226 shutil.rmtree(heads_dir(tmp_path))
227 assert _list_all_refs(tmp_path) == []
228
229 def test_sorted_output(self, tmp_path: pathlib.Path) -> None:
230 _init_repo(tmp_path)
231 for b in ["zzz", "aaa", "mmm"]:
232 _commit(tmp_path, f"c-{b}", b)
233 pairs = _list_all_refs(tmp_path)
234 names = [b for b, _ in pairs]
235 assert names == sorted(names)
236
237
238 # ---------------------------------------------------------------------------
239 # Integration — basic JSON output
240 # ---------------------------------------------------------------------------
241
242
243 class TestJsonOutput:
244 def test_empty_repo(self, tmp_path: pathlib.Path) -> None:
245 _init_repo(tmp_path)
246 r = _fer(tmp_path)
247 assert r.exit_code == 0
248 data = json.loads(r.output)
249 assert data["count"] == 0
250 assert data["refs"] == []
251
252 def test_single_branch(self, tmp_path: pathlib.Path) -> None:
253 _init_repo(tmp_path)
254 cid = _commit(tmp_path, "c1")
255 r = _fer(tmp_path)
256 assert r.exit_code == 0
257 data = json.loads(r.output)
258 assert data["count"] == 1
259 ref = data["refs"][0]
260 assert ref["commit_id"] == cid
261 assert ref["branch"] == "main"
262 assert ref["ref"] == "refs/heads/main"
263
264 def test_all_fields_present(self, tmp_path: pathlib.Path) -> None:
265 _init_repo(tmp_path)
266 _commit(tmp_path, "c1")
267 r = _fer(tmp_path)
268 ref = json.loads(r.output)["refs"][0]
269 for key in ("ref", "branch", "commit_id", "author", "message", "committed_at", "snapshot_id"):
270 assert key in ref, f"missing field: {key}"
271
272 def test_json_shorthand_alias(self, tmp_path: pathlib.Path) -> None:
273 _init_repo(tmp_path)
274 _commit(tmp_path, "c1")
275 r = _fer(tmp_path, "--json")
276 assert r.exit_code == 0
277 data = json.loads(r.output)
278 assert "refs" in data
279
280 def test_hierarchical_branch_in_output(self, tmp_path: pathlib.Path) -> None:
281 """Branches with slashes in name must appear in the output."""
282 _init_repo(tmp_path)
283 _commit(tmp_path, "c-main", "main")
284 _commit(tmp_path, "c-feat", "feat/my-thing")
285 r = _fer(tmp_path)
286 assert r.exit_code == 0
287 data = json.loads(r.output)
288 branches = [ref["branch"] for ref in data["refs"]]
289 assert "feat/my-thing" in branches
290 assert data["count"] == 2
291
292 def test_multiple_branches_counted(self, tmp_path: pathlib.Path) -> None:
293 _init_repo(tmp_path)
294 for b in ["main", "dev", "feat/x", "feat/y"]:
295 _commit(tmp_path, f"c-{b}", b)
296 r = _fer(tmp_path)
297 assert r.exit_code == 0
298 data = json.loads(r.output)
299 assert data["count"] == 4
300
301
302 # ---------------------------------------------------------------------------
303 # Integration — --no-commits fast path
304 # ---------------------------------------------------------------------------
305
306
307 class TestNoCommits:
308 def test_no_commits_omits_commit_fields(self, tmp_path: pathlib.Path) -> None:
309 _init_repo(tmp_path)
310 _commit(tmp_path, "c1")
311 r = _fer(tmp_path, "--no-commits")
312 assert r.exit_code == 0
313 data = json.loads(r.output)
314 ref = data["refs"][0]
315 assert "ref" in ref
316 assert "branch" in ref
317 assert "commit_id" in ref
318 # These must be absent in --no-commits mode
319 assert "author" not in ref
320 assert "message" not in ref
321 assert "committed_at" not in ref
322
323 def test_no_commits_count_correct(self, tmp_path: pathlib.Path) -> None:
324 _init_repo(tmp_path)
325 for b in ["main", "dev", "feat/x"]:
326 _commit(tmp_path, f"c-{b}", b)
327 r = _fer(tmp_path, "--no-commits")
328 assert r.exit_code == 0
329 data = json.loads(r.output)
330 assert data["count"] == 3
331
332 def test_no_commits_text_format(self, tmp_path: pathlib.Path) -> None:
333 _init_repo(tmp_path)
334 cid = _commit(tmp_path, "c1")
335 r = _fer_text(tmp_path, "--no-commits")
336 assert r.exit_code == 0
337 line = r.output.strip()
338 assert cid in line
339 assert "refs/heads/main" in line
340 # Should NOT have 4 columns (no author column)
341 parts = line.split(" ")
342 assert len(parts) == 2
343
344 def test_no_commits_rejected_with_commit_sort_field(self, tmp_path: pathlib.Path) -> None:
345 _init_repo(tmp_path)
346 _commit(tmp_path, "c1")
347 for field in ("author", "message", "committed_at", "snapshot_id"):
348 r = _fer(tmp_path, "--no-commits", "--sort", field)
349 assert r.exit_code != 0
350 assert r.stdout_bytes == b""
351 assert "error" in r.stderr.lower()
352
353 def test_no_commits_allows_ref_level_sort(self, tmp_path: pathlib.Path) -> None:
354 _init_repo(tmp_path)
355 for b in ["zzz", "aaa"]:
356 _commit(tmp_path, f"c-{b}", b)
357 for field in ("ref", "branch", "commit_id"):
358 r = _fer(tmp_path, "--no-commits", "--sort", field)
359 assert r.exit_code == 0
360
361
362 # ---------------------------------------------------------------------------
363 # Integration — sorting
364 # ---------------------------------------------------------------------------
365
366
367 class TestSorting:
368 def test_sort_by_ref_ascending(self, tmp_path: pathlib.Path) -> None:
369 _init_repo(tmp_path)
370 for b in ["zzz", "aaa", "mmm"]:
371 _commit(tmp_path, f"c-{b}", b)
372 r = _fer(tmp_path, "--sort", "ref")
373 data = json.loads(r.output)
374 refs = [d["ref"] for d in data["refs"]]
375 assert refs == sorted(refs)
376
377 def test_sort_by_ref_descending(self, tmp_path: pathlib.Path) -> None:
378 _init_repo(tmp_path)
379 for b in ["zzz", "aaa", "mmm"]:
380 _commit(tmp_path, f"c-{b}", b)
381 r = _fer(tmp_path, "--sort", "ref", "--desc")
382 data = json.loads(r.output)
383 refs = [d["ref"] for d in data["refs"]]
384 assert refs == sorted(refs, reverse=True)
385
386 def test_sort_by_committed_at(self, tmp_path: pathlib.Path) -> None:
387 _init_repo(tmp_path)
388 base = datetime.datetime(2026, 1, 1, tzinfo=datetime.timezone.utc)
389 _commit(tmp_path, "c-b", "bbb", ts=base + datetime.timedelta(hours=2))
390 _commit(tmp_path, "c-a", "aaa", ts=base + datetime.timedelta(hours=1))
391 r = _fer(tmp_path, "--sort", "committed_at")
392 data = json.loads(r.output)
393 timestamps = [d["committed_at"] for d in data["refs"]]
394 assert timestamps == sorted(timestamps)
395
396 def test_sort_by_author(self, tmp_path: pathlib.Path) -> None:
397 _init_repo(tmp_path)
398 _commit(tmp_path, "c-main", "main", author="zara")
399 _commit(tmp_path, "c-dev", "dev", author="alice")
400 r = _fer(tmp_path, "--sort", "author")
401 data = json.loads(r.output)
402 authors = [d["author"] for d in data["refs"]]
403 assert authors == sorted(authors)
404
405 def test_sort_by_snapshot_id(self, tmp_path: pathlib.Path) -> None:
406 _init_repo(tmp_path)
407 for b in ["a", "b", "c"]:
408 _commit(tmp_path, f"snap-{b}", b)
409 r = _fer(tmp_path, "--sort", "snapshot_id")
410 assert r.exit_code == 0
411 data = json.loads(r.output)
412 sids = [d["snapshot_id"] for d in data["refs"]]
413 assert sids == sorted(sids)
414
415
416 # ---------------------------------------------------------------------------
417 # Integration — --count and --pattern
418 # ---------------------------------------------------------------------------
419
420
421 class TestCountAndPattern:
422 def test_count_limits_output(self, tmp_path: pathlib.Path) -> None:
423 _init_repo(tmp_path)
424 for b in ["aaa", "bbb", "ccc", "ddd"]:
425 _commit(tmp_path, f"c-{b}", b)
426 r = _fer(tmp_path, "--count", "2")
427 data = json.loads(r.output)
428 assert data["count"] == 2
429 assert len(data["refs"]) == 2
430
431 def test_count_zero_is_unlimited(self, tmp_path: pathlib.Path) -> None:
432 _init_repo(tmp_path)
433 for b in ["a", "b", "c"]:
434 _commit(tmp_path, f"c-{b}", b)
435 r = _fer(tmp_path, "--count", "0")
436 data = json.loads(r.output)
437 assert data["count"] == 3
438
439 def test_negative_count_errors(self, tmp_path: pathlib.Path) -> None:
440 _init_repo(tmp_path)
441 r = _fer(tmp_path, "--count", "-1")
442 assert r.exit_code != 0
443 assert r.stdout_bytes == b""
444 assert "error" in r.stderr.lower()
445
446 def test_pattern_filter_flat(self, tmp_path: pathlib.Path) -> None:
447 _init_repo(tmp_path)
448 _commit(tmp_path, "c-main", "main")
449 _commit(tmp_path, "c-dev", "dev")
450 r = _fer(tmp_path, "--pattern", "refs/heads/main")
451 data = json.loads(r.output)
452 assert data["count"] == 1
453 assert data["refs"][0]["branch"] == "main"
454
455 def test_pattern_filter_hierarchical(self, tmp_path: pathlib.Path) -> None:
456 _init_repo(tmp_path)
457 _commit(tmp_path, "c-main", "main")
458 _commit(tmp_path, "c-feat1", "feat/one")
459 _commit(tmp_path, "c-feat2", "feat/two")
460 r = _fer(tmp_path, "--pattern", "refs/heads/feat/*")
461 data = json.loads(r.output)
462 assert data["count"] == 2
463 for ref in data["refs"]:
464 assert ref["branch"].startswith("feat/")
465
466 def test_pattern_no_match_returns_empty(self, tmp_path: pathlib.Path) -> None:
467 _init_repo(tmp_path)
468 _commit(tmp_path, "c-main", "main")
469 r = _fer(tmp_path, "--pattern", "refs/heads/nonexistent/*")
470 data = json.loads(r.output)
471 assert data["count"] == 0
472
473
474 # ---------------------------------------------------------------------------
475 # Integration — text output
476 # ---------------------------------------------------------------------------
477
478
479 class TestTextOutput:
480 def test_text_format_four_columns(self, tmp_path: pathlib.Path) -> None:
481 _init_repo(tmp_path)
482 cid = _commit(tmp_path, "c1", author="alice")
483 r = _fer_text(tmp_path)
484 assert r.exit_code == 0
485 line = r.output.strip()
486 assert cid in line
487 assert "refs/heads/main" in line
488 assert "alice" in line
489
490 def test_text_multiple_lines(self, tmp_path: pathlib.Path) -> None:
491 _init_repo(tmp_path)
492 for b in ["aaa", "bbb"]:
493 _commit(tmp_path, f"c-{b}", b)
494 r = _fer_text(tmp_path)
495 lines = [l for l in r.output.strip().splitlines() if l]
496 assert len(lines) == 2
497
498
499 # ---------------------------------------------------------------------------
500 # Security
501 # ---------------------------------------------------------------------------
502
503
504 class TestSecurity:
505 def test_ansi_in_branch_name_sanitized_text(self, tmp_path: pathlib.Path) -> None:
506 """Branch names with ANSI must not appear raw in text output."""
507 _init_repo(tmp_path)
508 cid = _commit(tmp_path, "c1", "main")
509 # Directly write a ref file with ANSI in its name (via the raw FS)
510 ansi_branch_dir = heads_dir(tmp_path) / "safe"
511 ansi_branch_dir.mkdir(parents=True, exist_ok=True)
512 # Can't create filename with ANSI; instead verify author field sanitized
513 _commit(tmp_path, "c-dev", "dev", author="\x1b[31mred\x1b[0m")
514 r = _fer_text(tmp_path)
515 assert "\x1b" not in r.output
516
517 def test_unknown_flag_exits_nonzero(self, tmp_path: pathlib.Path) -> None:
518 _init_repo(tmp_path)
519 r = _fer_text(tmp_path, "--format", "xml")
520 assert r.exit_code != 0
521
522 def test_error_sort_goes_to_stderr(self, tmp_path: pathlib.Path) -> None:
523 _init_repo(tmp_path)
524 r = _fer(tmp_path, "--sort", "invalid_field")
525 assert r.exit_code != 0
526 assert r.stdout_bytes == b""
527 assert "error" in r.stderr.lower()
528
529 def test_negative_count_error_to_stderr(self, tmp_path: pathlib.Path) -> None:
530 _init_repo(tmp_path)
531 r = _fer(tmp_path, "--count", "-5")
532 assert r.exit_code != 0
533 assert r.stdout_bytes == b""
534
535 def test_no_traceback_on_unknown_flag(self, tmp_path: pathlib.Path) -> None:
536 _init_repo(tmp_path)
537 r = _fer_text(tmp_path, "--format", "bad")
538 assert "Traceback" not in r.output
539 assert "Traceback" not in r.stderr
540
541 def test_symlink_ref_skipped_in_output(self, tmp_path: pathlib.Path) -> None:
542 _init_repo(tmp_path)
543 _commit(tmp_path, "c", "main")
544 real = heads_dir(tmp_path) / "main"
545 link = heads_dir(tmp_path) / "linked"
546 link.symlink_to(real)
547 r = _fer(tmp_path)
548 data = json.loads(r.output)
549 branches = [ref["branch"] for ref in data["refs"]]
550 assert "linked" not in branches
551
552 def test_corrupted_ref_skipped_in_output(self, tmp_path: pathlib.Path) -> None:
553 _init_repo(tmp_path)
554 _commit(tmp_path, "c", "main")
555 bad = heads_dir(tmp_path) / "corrupted"
556 bad.write_text("not-a-sha\n", encoding="utf-8")
557 r = _fer(tmp_path)
558 data = json.loads(r.output)
559 branches = [ref["branch"] for ref in data["refs"]]
560 assert "corrupted" not in branches
561
562 def test_no_repo_exits_cleanly(self, tmp_path: pathlib.Path) -> None:
563 r = runner.invoke(
564 cli,
565 ["for-each-ref"],
566 env={"MUSE_REPO_ROOT": str(tmp_path / "norepo")},
567 )
568 assert r.exit_code != 0
569 assert "Traceback" not in r.output
570 assert "Traceback" not in r.stderr
571
572
573 # ---------------------------------------------------------------------------
574 # Stress
575 # ---------------------------------------------------------------------------
576
577
578 class TestStress:
579 def test_100_flat_branches(self, tmp_path: pathlib.Path) -> None:
580 _init_repo(tmp_path)
581 for i in range(100):
582 _commit(tmp_path, f"c-{i:03d}", f"branch-{i:03d}")
583 r = _fer(tmp_path)
584 assert r.exit_code == 0
585 data = json.loads(r.output)
586 assert data["count"] == 100
587
588 def test_50_hierarchical_branches(self, tmp_path: pathlib.Path) -> None:
589 """All 50 branches with slashes must be discovered via rglob."""
590 _init_repo(tmp_path)
591 for i in range(50):
592 _commit(tmp_path, f"c-{i}", f"feat/task-{i:03d}")
593 r = _fer(tmp_path)
594 assert r.exit_code == 0
595 data = json.loads(r.output)
596 assert data["count"] == 50
597 for ref in data["refs"]:
598 assert ref["branch"].startswith("feat/")
599
600 def test_no_commits_100_branches_fast(self, tmp_path: pathlib.Path) -> None:
601 _init_repo(tmp_path)
602 for i in range(100):
603 _commit(tmp_path, f"c-{i}", f"b-{i:03d}")
604 r = _fer(tmp_path, "--no-commits")
605 assert r.exit_code == 0
606 data = json.loads(r.output)
607 assert data["count"] == 100
608 # Confirm no commit metadata fields
609 for ref in data["refs"]:
610 assert "author" not in ref
611
612 def test_200_sequential_reads(self, tmp_path: pathlib.Path) -> None:
613 _init_repo(tmp_path)
614 for b in ["main", "dev"]:
615 _commit(tmp_path, f"c-{b}", b)
616 for _ in range(200):
617 r = _fer(tmp_path)
618 assert r.exit_code == 0
619 assert json.loads(r.output)["count"] == 2
File History 1 commit
sha256:51ce277f663e01a43eaffbe77509b1de7ac2d4251b55d23306304bcdeb92c90d feat(pack): delta-encode snapshots in MPackBundle wire format Sonnet 4.6 minor 126 days ago