gabriel / muse public

test_log_branch_flag.py file-level

at sha256:c · View file ↗ · Intel ↗

History
1 files
1 commits
0 hotspots
0 🧊 dead
0 💥 blast risk
sha256:6 chore(timeline): remove unused RationalRate import in entity.py · · Jul 10, 2026
1 """TDD tests for ``muse log --branch``/``-b`` (issue #45).
2
3 ``muse log`` already reads history from an arbitrary branch/commit via the
4 positional ``ref`` argument (``muse log main``), with no working-tree or
5 HEAD side-effects. ``--branch``/``-b`` did not exist as an explicit flag,
6 so any caller reaching for the more self-documenting/git-idiomatic form
7 got ``unrecognized arguments: --branch``.
8
9 Coverage:
10 - --branch/-b resolves to the same history as the equivalent positional ref
11 - --branch does not touch HEAD or the working branch
12 - --branch composes with --oneline, --max-count, --json
13 - --branch together with a positional ref is rejected as ambiguous
14 """
15 from __future__ import annotations
16
17 import json
18 import os
19 import pathlib
20
21 from tests.cli_test_helper import CliRunner, InvokeResult
22
23 runner = CliRunner()
24
25
26 def _invoke(repo: pathlib.Path, *args: str) -> InvokeResult:
27 from muse.cli.app import main as cli
28
29 saved = os.getcwd()
30 try:
31 os.chdir(repo)
32 return runner.invoke(cli, list(args))
33 finally:
34 os.chdir(saved)
35
36
37 def _init(repo: pathlib.Path) -> None:
38 repo.mkdir(parents=True, exist_ok=True)
39 _invoke(repo, "init")
40
41
42 def _commit(repo: pathlib.Path, msg: str, filename: str | None = None) -> str:
43 fname = filename or f"f_{abs(hash(msg)) % 100000}.py"
44 (repo / fname).write_text(f"# {msg}\n")
45 _invoke(repo, "code", "add", ".")
46 result = _invoke(repo, "commit", "-m", msg, "--json")
47 return json.loads(result.output)["commit_id"]
48
49
50 def _checkout(repo: pathlib.Path, branch: str, *, new: bool = False) -> None:
51 args = ["checkout", "-b", branch] if new else ["checkout", branch]
52 _invoke(repo, *args)
53
54
55 def _current_branch(repo: pathlib.Path) -> str:
56 result = _invoke(repo, "status", "--json")
57 return json.loads(result.output)["branch"]
58
59
60 def _log_json(repo: pathlib.Path, *extra: str) -> list[dict]:
61 result = _invoke(repo, "log", "--json", *extra)
62 return json.loads(result.output)["commits"]
63
64
65 def _two_branch_repo(tmp: pathlib.Path) -> pathlib.Path:
66 """main: A -> B; feat/x branches from A, adds C. HEAD ends on main."""
67 repo = tmp / "repo"
68 _init(repo)
69 _commit(repo, "A")
70 _checkout(repo, "feat/x", new=True)
71 _commit(repo, "C")
72 _checkout(repo, "main")
73 _commit(repo, "B")
74 return repo
75
76
77 class TestBranchFlagBasic:
78 def test_branch_flag_matches_positional_ref(self, tmp_path: pathlib.Path) -> None:
79 repo = _two_branch_repo(tmp_path)
80
81 via_flag = _log_json(repo, "--branch", "feat/x")
82 via_positional = _log_json(repo, "feat/x")
83
84 assert via_flag == via_positional
85 assert any(c["message"] == "C" for c in via_flag)
86 assert not any(c["message"] == "B" for c in via_flag)
87
88 def test_short_flag_b(self, tmp_path: pathlib.Path) -> None:
89 repo = _two_branch_repo(tmp_path)
90
91 result = _log_json(repo, "-b", "feat/x")
92
93 assert any(c["message"] == "C" for c in result)
94
95 def test_branch_flag_does_not_touch_head(self, tmp_path: pathlib.Path) -> None:
96 repo = _two_branch_repo(tmp_path)
97 before = _current_branch(repo)
98
99 _invoke(repo, "log", "--branch", "feat/x", "--json")
100
101 assert _current_branch(repo) == before == "main"
102
103 def test_branch_flag_composes_with_oneline_and_max_count(
104 self, tmp_path: pathlib.Path
105 ) -> None:
106 repo = _two_branch_repo(tmp_path)
107 _checkout(repo, "feat/x")
108 _commit(repo, "D")
109 _checkout(repo, "main")
110
111 result = _invoke(
112 repo, "log", "--branch", "feat/x", "--oneline", "--max-count", "1"
113 )
114
115 assert result.exit_code == 0
116 lines = [l for l in result.output.strip().splitlines() if l.strip()]
117 assert len(lines) == 1
118
119 def test_branch_flag_with_json(self, tmp_path: pathlib.Path) -> None:
120 repo = _two_branch_repo(tmp_path)
121
122 result = _invoke(repo, "log", "--branch", "feat/x", "--json")
123 data = json.loads(result.output)
124
125 assert data["branch"] == "feat/x"
126 assert any(c["message"] == "C" for c in data["commits"])
127
128
129 class TestBranchFlagAmbiguity:
130 def test_branch_flag_and_positional_ref_together_is_rejected(
131 self, tmp_path: pathlib.Path
132 ) -> None:
133 repo = _two_branch_repo(tmp_path)
134
135 result = _invoke(repo, "log", "--branch", "feat/x", "main", "--json")
136
137 assert result.exit_code != 0