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