"""Supercharge tests for ``muse branch``. Gaps filled versus the baseline test_cmd_branch.py --------------------------------------------------- 1. ``committed_at`` field present in list --json schema (baseline missed it) 2. ``--sort committeddate`` actual ordering (baseline only checked exit 0) 3. ``-r`` remote-tracking listing E2E 4. ``-a`` combined local + remote listing E2E 5. ``-dr`` remote-tracking ref deletion — success path 6. ``-vv`` upstream shown in text output 7. Diamond-merge DAG correctness for ``--merged`` 8. Data integrity: empty parent dirs cleaned after nested branch delete 9. Rename into nested path creates parent dirs 10. Force-rename/copy leaves destination at correct tip 11. JSON error schemas for delete/rename/copy operations 12. Performance: ``--sort committeddate`` with 50 branches under 3 s 13. Security: ANSI injection in ``--merged`` / ``--no-merged`` / ``--contains`` 14. Docstring coverage for all public helpers Test categories --------------- - unit : _cleanup_empty_dirs, _ref_file, _list_remotes - integration : remote ops (-r/-a/-dr), committeddate ordering, DAG, -vv - e2e : full CLI round-trips for new scenarios - security : ANSI in filter flags, error output sanitisation - data_integrity: empty-dir cleanup, atomic rename into deep paths, force overwrites - performance : --sort committeddate with 50 branches - docstrings : public helper docstring coverage """ from __future__ import annotations from collections.abc import Mapping import json import os import pathlib import time import pytest from tests.cli_test_helper import CliRunner, InvokeResult from muse.core.refs import ( get_head_commit_id, read_current_branch, ) from muse.core.types import long_id from muse.core.paths import config_toml_path, heads_dir, muse_dir, remotes_dir runner = CliRunner() type _AnyObj = object # --------------------------------------------------------------------------- # Helpers # --------------------------------------------------------------------------- def _invoke(repo: pathlib.Path, args: list[str]) -> InvokeResult: saved = os.getcwd() try: os.chdir(repo) return runner.invoke(None, args) finally: os.chdir(saved) def _branch(repo: pathlib.Path, *extra: str) -> InvokeResult: return _invoke(repo, ["branch", *extra]) def _commit(repo: pathlib.Path, *extra: str) -> InvokeResult: _invoke(repo, ["code", "add", "."]) return _invoke(repo, ["commit", *extra]) @pytest.fixture() def repo(tmp_path: pathlib.Path) -> pathlib.Path: """Initialised repo with one commit on ``main``.""" saved = os.getcwd() try: os.chdir(tmp_path) runner.invoke(None, ["init"]) finally: os.chdir(saved) (tmp_path / "a.py").write_text("x = 1\n") _commit(tmp_path, "-m", "initial") return tmp_path @pytest.fixture() def two_commit_repo(repo: pathlib.Path) -> pathlib.Path: (repo / "b.py").write_text("y = 2\n") _commit(repo, "-m", "second") return repo def _first_json(result: InvokeResult) -> Mapping[str, object]: """Extract the first JSON object from mixed stdout+stderr output.""" for line in result.output.splitlines(): stripped = line.strip() if stripped.startswith("{"): return json.loads(stripped) raise ValueError(f"No JSON object in output:\n{result.output!r}") def _make_remote_ref( repo: pathlib.Path, remote: str, branch: str, commit_id: str ) -> None: """Write a local remote-tracking ref to simulate a previous push.""" ref_dir = remotes_dir(repo) / remote ref_file = ref_dir / branch ref_file.parent.mkdir(parents=True, exist_ok=True) ref_file.write_text(commit_id + "\n", encoding="utf-8") # --------------------------------------------------------------------------- # Unit: _ref_file # --------------------------------------------------------------------------- class TestRefFile: def test_simple_branch(self, tmp_path: pathlib.Path) -> None: from muse.cli.commands.branch import _ref_file p = _ref_file(tmp_path, "main") assert p == heads_dir(tmp_path) / "main" def test_nested_branch(self, tmp_path: pathlib.Path) -> None: from muse.cli.commands.branch import _ref_file p = _ref_file(tmp_path, "feat/sub/task") assert p == heads_dir(tmp_path) / "feat" / "sub" / "task" def test_returns_pathlib_path(self, tmp_path: pathlib.Path) -> None: from muse.cli.commands.branch import _ref_file p = _ref_file(tmp_path, "dev") assert isinstance(p, pathlib.Path) # --------------------------------------------------------------------------- # Unit: _cleanup_empty_dirs # --------------------------------------------------------------------------- class TestCleanupEmptyDirs: def test_removes_empty_parent_dir(self, tmp_path: pathlib.Path) -> None: from muse.cli.commands.branch import _cleanup_empty_dirs heads = tmp_path / "heads" ref = heads / "feat" / "task" ref.parent.mkdir(parents=True) ref.write_text("") ref.unlink() _cleanup_empty_dirs(ref, heads) assert not (heads / "feat").exists() def test_stops_at_heads_dir(self, tmp_path: pathlib.Path) -> None: from muse.cli.commands.branch import _cleanup_empty_dirs heads = tmp_path / "heads" heads.mkdir() ref = heads / "main" ref.write_text("") ref.unlink() _cleanup_empty_dirs(ref, heads) assert heads.exists() def test_leaves_non_empty_parent(self, tmp_path: pathlib.Path) -> None: from muse.cli.commands.branch import _cleanup_empty_dirs heads = tmp_path / "heads" ref_a = heads / "feat" / "a" ref_b = heads / "feat" / "b" ref_a.parent.mkdir(parents=True) ref_a.write_text("") ref_b.write_text("") ref_a.unlink() _cleanup_empty_dirs(ref_a, heads) # feat/ still has b, so it must not be removed assert (heads / "feat").exists() assert (heads / "feat" / "b").exists() def test_removes_multiple_levels(self, tmp_path: pathlib.Path) -> None: from muse.cli.commands.branch import _cleanup_empty_dirs heads = tmp_path / "heads" ref = heads / "a" / "b" / "c" ref.parent.mkdir(parents=True) ref.write_text("") ref.unlink() _cleanup_empty_dirs(ref, heads) assert not (heads / "a").exists() # --------------------------------------------------------------------------- # Unit: _list_remotes # --------------------------------------------------------------------------- class TestListRemotes: def test_empty_when_no_remotes_dir(self, tmp_path: pathlib.Path) -> None: from muse.cli.commands.branch import _list_remotes muse_dir(tmp_path).mkdir() assert _list_remotes(tmp_path) == [] def test_lists_single_remote(self, tmp_path: pathlib.Path) -> None: from muse.cli.commands.branch import _list_remotes ref = remotes_dir(tmp_path) / "origin" / "main" ref.parent.mkdir(parents=True) ref.write_text(long_id("a" * 64)) remotes = _list_remotes(tmp_path) assert "origin/main" in remotes def test_lists_nested_remote_branch(self, tmp_path: pathlib.Path) -> None: from muse.cli.commands.branch import _list_remotes ref = remotes_dir(tmp_path) / "origin" / "feat" / "task" ref.parent.mkdir(parents=True) ref.write_text(long_id("b" * 64)) remotes = _list_remotes(tmp_path) assert "origin/feat/task" in remotes def test_skips_hidden_files(self, tmp_path: pathlib.Path) -> None: from muse.cli.commands.branch import _list_remotes ref_dir = remotes_dir(tmp_path) / "origin" ref_dir.mkdir(parents=True) (ref_dir / ".hidden").write_text("ignored") (ref_dir / "main").write_text(long_id("c" * 64)) remotes = _list_remotes(tmp_path) assert all(not r.endswith(".hidden") for r in remotes) def test_sorted_output(self, tmp_path: pathlib.Path) -> None: from muse.cli.commands.branch import _list_remotes for name in ("z-branch", "a-branch", "m-branch"): ref = remotes_dir(tmp_path) / "origin" / name ref.parent.mkdir(parents=True, exist_ok=True) ref.write_text(long_id("d" * 64)) remotes = _list_remotes(tmp_path) assert remotes == sorted(remotes) # --------------------------------------------------------------------------- # Integration: JSON schema — committed_at field (gap in baseline) # --------------------------------------------------------------------------- class TestListJsonCommittedAt: """committed_at must be present in the listing JSON schema.""" def test_committed_at_present_in_schema(self, repo: pathlib.Path) -> None: result = _branch(repo, "--json") data = json.loads(result.output) assert len(data) >= 1 assert "committed_at" in data[0], ( "committed_at missing from branch --json output" ) def test_committed_at_is_iso8601(self, repo: pathlib.Path) -> None: import datetime result = _branch(repo, "--json") data = json.loads(result.output) main = next(b for b in data if b["name"] == "main") ts = main["committed_at"] assert ts is not None # Must parse as ISO 8601 datetime.datetime.fromisoformat(ts) def test_committed_at_null_for_empty_branch(self, repo: pathlib.Path) -> None: """Branches pointing at no commit (empty branch) get null committed_at.""" # Force an empty branch by writing an empty ref file directly ref = heads_dir(repo) / "empty-branch" ref.write_text("") result = _branch(repo, "--json") data = json.loads(result.output) eb = next((b for b in data if b["name"] == "empty-branch"), None) assert eb is not None assert eb["committed_at"] is None def test_committed_at_schema_complete(self, repo: pathlib.Path) -> None: result = _branch(repo, "--json") data = json.loads(result.output) required = {"name", "current", "commit_id", "committed_at", "last_message", "upstream"} missing = required - set(data[0].keys()) assert not missing, f"branch --json missing fields: {missing}" # --------------------------------------------------------------------------- # Integration: --sort committeddate actual ordering # --------------------------------------------------------------------------- class TestSortCommittedDateOrdering: """--sort committeddate must emit branches newest-first.""" def test_newer_branch_first(self, repo: pathlib.Path) -> None: # Create a branch at initial commit, then add another commit on main _branch(repo, "older-branch") (repo / "c.py").write_text("c=3\n") _commit(repo, "-m", "newer commit") _branch(repo, "newer-branch") result = _branch(repo, "--sort", "committeddate", "--json") assert result.exit_code == 0 data = json.loads(result.output) names = [b["name"] for b in data] assert names.index("newer-branch") < names.index("older-branch"), ( f"newer-branch should sort before older-branch; got order: {names}" ) def test_timestamp_order_consistent_on_ties(self, repo: pathlib.Path) -> None: """Branches at the same commit produce a stable (name-secondary) order.""" for name in ("z-same", "a-same", "m-same"): _branch(repo, name) result = _branch(repo, "--sort", "committeddate", "--json") data = json.loads(result.output) assert result.exit_code == 0 assert len(data) >= 4 def test_empty_branches_sorted_last(self, repo: pathlib.Path) -> None: """Branches with no commit (committed_at=null) come after dated branches.""" _branch(repo, "real-commit-branch") # Write an empty ref manually (heads_dir(repo) / "no-commit").write_text("") result = _branch(repo, "--sort", "committeddate", "--json") data = json.loads(result.output) names_with_ts = [b["name"] for b in data if b.get("committed_at")] names_without_ts = [b["name"] for b in data if not b.get("committed_at")] if names_with_ts and names_without_ts: last_with_ts = data.index(next(b for b in data if b["name"] == names_with_ts[-1])) first_without_ts = data.index(next(b for b in data if b["name"] == names_without_ts[0])) assert last_with_ts < first_without_ts, ( "Branches with no commit should sort after dated branches" ) # --------------------------------------------------------------------------- # Integration: remote-tracking branches (-r / -a / -dr) # --------------------------------------------------------------------------- class TestRemoteTrackingBranches: def test_list_r_shows_only_remotes(self, repo: pathlib.Path) -> None: cid = get_head_commit_id(repo, "main") _make_remote_ref(repo, "origin", "main", cid) result = _branch(repo, "-r", "--json") assert result.exit_code == 0 data = json.loads(result.output) names = [b["name"] for b in data] # Remote entries are prefixed with remotes/ assert all(n.startswith("remotes/") for n in names), ( f"-r listing must only contain remote entries; got: {names}" ) assert "remotes/origin/main" in names def test_list_r_excludes_local_branches(self, repo: pathlib.Path) -> None: cid = get_head_commit_id(repo, "main") _make_remote_ref(repo, "origin", "main", cid) _branch(repo, "local-only") result = _branch(repo, "-r", "--json") data = json.loads(result.output) names = [b["name"] for b in data] assert "local-only" not in names def test_list_a_includes_both(self, repo: pathlib.Path) -> None: cid = get_head_commit_id(repo, "main") _make_remote_ref(repo, "origin", "dev", cid) _branch(repo, "local-feat") result = _branch(repo, "-a", "--json") assert result.exit_code == 0 data = json.loads(result.output) names = [b["name"] for b in data] assert "main" in names assert "local-feat" in names assert "remotes/origin/dev" in names def test_list_r_empty_when_no_remotes(self, repo: pathlib.Path) -> None: result = _branch(repo, "-r", "--json") assert result.exit_code == 0 assert json.loads(result.output) == [] def test_list_r_nested_remote_branch(self, repo: pathlib.Path) -> None: cid = get_head_commit_id(repo, "main") _make_remote_ref(repo, "origin", "feat/task", cid) result = _branch(repo, "-r", "--json") data = json.loads(result.output) names = [b["name"] for b in data] assert "remotes/origin/feat/task" in names def test_list_a_sorted_by_name(self, repo: pathlib.Path) -> None: cid = get_head_commit_id(repo, "main") _make_remote_ref(repo, "origin", "z-remote", cid) _make_remote_ref(repo, "origin", "a-remote", cid) _branch(repo, "m-local") result = _branch(repo, "-a", "--json") data = json.loads(result.output) names = [b["name"] for b in data] # Local branches come first (alphabetically), then remotes/ local_names = [n for n in names if not n.startswith("remotes/")] assert local_names == sorted(local_names) def test_dr_deletes_remote_tracking_ref(self, repo: pathlib.Path) -> None: cid = get_head_commit_id(repo, "main") _make_remote_ref(repo, "origin", "stale", cid) # Verify it's visible before = json.loads(_branch(repo, "-r", "--json").output) assert any(b["name"] == "remotes/origin/stale" for b in before) # Delete it result = _branch(repo, "-d", "-r", "origin/stale") assert result.exit_code == 0 # Gone now after = json.loads(_branch(repo, "-r", "--json").output) assert not any(b["name"] == "remotes/origin/stale" for b in after) def test_dr_json_schema(self, repo: pathlib.Path) -> None: cid = get_head_commit_id(repo, "main") _make_remote_ref(repo, "origin", "old", cid) result = _branch(repo, "-d", "-r", "origin/old", "--json") assert result.exit_code == 0 data = json.loads(result.output) assert data["action"] == "deleted_remote_tracking" assert data["remote"] == "origin" assert data["branch"] == "old" def test_dr_remotes_prefix_accepted(self, repo: pathlib.Path) -> None: """remotes/origin/branch spelling accepted in addition to origin/branch.""" cid = get_head_commit_id(repo, "main") _make_remote_ref(repo, "origin", "with-prefix", cid) result = _branch(repo, "-d", "-r", "remotes/origin/with-prefix") assert result.exit_code == 0 def test_dr_nonexistent_exits_1(self, repo: pathlib.Path) -> None: result = _branch(repo, "-d", "-r", "origin/ghost") assert result.exit_code == 1 def test_dr_no_slash_exits_1(self, repo: pathlib.Path) -> None: result = _branch(repo, "-d", "-r", "justaname") assert result.exit_code == 1 # --------------------------------------------------------------------------- # Integration: -vv upstream display # --------------------------------------------------------------------------- class TestVerboseUpstream: def _set_upstream(self, repo: pathlib.Path, branch: str, remote: str, remote_branch: str) -> None: config_path = config_toml_path(repo) existing = config_path.read_text() if config_path.exists() else "" existing += ( f'\n[branch."{branch}"]\n' f'remote = "{remote}"\n' f'merge = "refs/heads/{remote_branch}"\n' ) config_path.write_text(existing) def test_vv_shows_upstream(self, repo: pathlib.Path) -> None: self._set_upstream(repo, "main", "origin", "main") result = _branch(repo, "-vv") assert result.exit_code == 0 assert "origin/main" in result.output def test_vv_upstream_in_brackets(self, repo: pathlib.Path) -> None: self._set_upstream(repo, "main", "origin", "main") result = _branch(repo, "-vv") assert "[origin/main]" in result.output def test_v_does_not_show_upstream(self, repo: pathlib.Path) -> None: self._set_upstream(repo, "main", "origin", "main") result = _branch(repo, "-v") # -v shows commit SHA + message but NOT upstream brackets assert "[origin/main]" not in result.output def test_vv_no_upstream_no_brackets(self, repo: pathlib.Path) -> None: result = _branch(repo, "-vv") assert "[" not in result.output # --------------------------------------------------------------------------- # Integration: Diamond-merge DAG correctness for --merged # --------------------------------------------------------------------------- class TestDiamondMergeDag: """--merged must handle merge commits with two parents (parent2_commit_id).""" def test_merged_branch_included_after_diamond_merge( self, repo: pathlib.Path ) -> None: """ Build diamond: main ← feat-a, main ← feat-b, then merge feat-a into feat-b. After merging feat-a into main via feat-b, --merged should include feat-a. main ── C1 \\ feat-a ── C2 \\ main (merged feat-a) ── C3 """ # Create and diverge feat-a _branch(repo, "feat-a") _invoke(repo, ["checkout", "feat-a"]) (repo / "fa.py").write_text("fa=1\n") _commit(repo, "-m", "feat-a commit") _invoke(repo, ["checkout", "main"]) # Merge feat-a into main _invoke(repo, ["merge", "feat-a"]) # Now --merged on main should include feat-a result = _branch(repo, "--merged", "--json") assert result.exit_code == 0 data = json.loads(result.output) names = [b["name"] for b in data] assert "feat-a" in names, f"feat-a should be merged into main; got: {names}" def test_unmerged_sibling_excluded_from_diamond( self, repo: pathlib.Path ) -> None: """Two branches from same point; merging one doesn't include the other.""" _branch(repo, "merged-branch") _branch(repo, "unmerged-branch") _invoke(repo, ["checkout", "merged-branch"]) (repo / "mb.py").write_text("mb=1\n") _commit(repo, "-m", "merged commit") _invoke(repo, ["checkout", "unmerged-branch"]) (repo / "ub.py").write_text("ub=1\n") _commit(repo, "-m", "unmerged commit") _invoke(repo, ["checkout", "main"]) _invoke(repo, ["merge", "merged-branch"]) result = _branch(repo, "--merged", "--json") data = json.loads(result.output) names = [b["name"] for b in data] assert "merged-branch" in names assert "unmerged-branch" not in names # --------------------------------------------------------------------------- # Data integrity: nested branch cleanup + deep rename # --------------------------------------------------------------------------- class TestDataIntegrityNested: def test_delete_nested_cleans_parent_dirs(self, repo: pathlib.Path) -> None: """Deleting feat/sub/task must remove the now-empty feat/sub/ and feat/ dirs.""" _branch(repo, "feat/sub/task") result = _branch(repo, "-D", "feat/sub/task") assert result.exit_code == 0 heads = heads_dir(repo) assert not (heads / "feat").exists(), ( "feat/ directory should be removed after deleting feat/sub/task" ) def test_delete_nested_keeps_sibling_dir(self, repo: pathlib.Path) -> None: """Deleting one nested branch must not remove a sibling.""" _branch(repo, "feat/sub/a") _branch(repo, "feat/sub/b") _branch(repo, "-D", "feat/sub/a") heads = heads_dir(repo) assert (heads / "feat" / "sub" / "b").exists() def test_rename_into_nested_path_creates_dirs(self, repo: pathlib.Path) -> None: """Renaming a flat branch to a nested path must create intermediate dirs.""" _branch(repo, "flat-branch") result = _branch(repo, "-m", "flat-branch", "deep/nested/branch") assert result.exit_code == 0 heads = heads_dir(repo) assert (heads / "deep" / "nested" / "branch").is_file() def test_force_rename_preserves_tip(self, repo: pathlib.Path) -> None: """Force-rename must not lose the commit pointer.""" cid_before = get_head_commit_id(repo, "main") _branch(repo, "original") _branch(repo, "destination") _branch(repo, "-M", "original", "destination") cid_after = get_head_commit_id(repo, "destination") assert cid_after == cid_before def test_force_copy_preserves_src_tip(self, repo: pathlib.Path) -> None: """Force-copy must not modify the source branch.""" cid_src = get_head_commit_id(repo, "main") _branch(repo, "src-branch") (repo / "x.py").write_text("x=99\n") _commit(repo, "-m", "extra commit") _branch(repo, "dst-branch") # Force-copy src-branch (old tip) onto dst-branch _branch(repo, "-C", "src-branch", "dst-branch") assert get_head_commit_id(repo, "src-branch") == cid_src def test_rename_current_branch_updates_head(self, repo: pathlib.Path) -> None: """Renaming the currently checked-out branch must update HEAD.""" _invoke(repo, ["checkout", "-b", "temp-branch"]) _branch(repo, "-m", "temp-branch", "renamed-branch") assert read_current_branch(repo) == "renamed-branch" # --------------------------------------------------------------------------- # Data integrity: JSON error schemas # --------------------------------------------------------------------------- class TestJsonErrorSchemas: """Mutation errors must emit structured JSON with error + message keys.""" def test_delete_not_found_json_schema(self, repo: pathlib.Path) -> None: result = _branch(repo, "-d", "ghost", "--json") assert result.exit_code == 1 data = _first_json(result) assert "error" in data assert "message" in data def test_delete_not_merged_json_schema(self, repo: pathlib.Path) -> None: _branch(repo, "unmerged") _invoke(repo, ["checkout", "unmerged"]) (repo / "z.py").write_text("z=1\n") _commit(repo, "-m", "diverge") _invoke(repo, ["checkout", "main"]) result = _branch(repo, "-d", "unmerged", "--json") assert result.exit_code == 1 data = _first_json(result) assert data.get("error") == "not_merged" assert "hint" in data # must tell user about -D def test_delete_current_branch_json_schema(self, repo: pathlib.Path) -> None: result = _branch(repo, "-d", "main", "--json") assert result.exit_code == 1 data = _first_json(result) assert data.get("error") == "current_branch" def test_rename_not_found_json_schema(self, repo: pathlib.Path) -> None: result = _branch(repo, "-m", "ghost", "new", "--json") assert result.exit_code == 1 data = _first_json(result) assert data.get("error") == "not_found" def test_rename_already_exists_json_schema(self, repo: pathlib.Path) -> None: _branch(repo, "a") _branch(repo, "b") result = _branch(repo, "-m", "a", "b", "--json") assert result.exit_code == 1 data = _first_json(result) assert data.get("error") == "already_exists" assert "hint" in data # must tell user about -M def test_copy_not_found_json_schema(self, repo: pathlib.Path) -> None: result = _branch(repo, "-c", "ghost", "copy", "--json") assert result.exit_code == 1 data = _first_json(result) assert data.get("error") == "not_found" def test_create_already_exists_json_schema(self, repo: pathlib.Path) -> None: result = _branch(repo, "main", "--json") assert result.exit_code == 1 data = _first_json(result) assert data.get("error") == "already_exists" # --------------------------------------------------------------------------- # Integration: create JSON schema # --------------------------------------------------------------------------- class TestCreateJsonSchema: def test_create_json_schema_complete(self, repo: pathlib.Path) -> None: result = _branch(repo, "new-branch", "--json") assert result.exit_code == 0 data = json.loads(result.output) assert data["action"] == "created" assert "branch" in data assert "commit_id" in data assert "from" in data def test_create_commit_id_is_sha256_prefixed(self, repo: pathlib.Path) -> None: result = _branch(repo, "sha-check", "--json") data = json.loads(result.output) cid = data.get("commit_id") assert cid is not None assert cid.startswith("sha256:"), f"commit_id should have sha256: prefix; got {cid!r}" def test_create_from_is_null_when_no_start_point(self, repo: pathlib.Path) -> None: result = _branch(repo, "no-sp", "--json") data = json.loads(result.output) assert data.get("from") is None def test_create_from_set_when_start_point_given(self, repo: pathlib.Path) -> None: cid = get_head_commit_id(repo, "main") result = _branch(repo, "from-sp", cid, "--json") data = json.loads(result.output) assert data.get("from") == cid # --------------------------------------------------------------------------- # Security: ANSI in filter flags # --------------------------------------------------------------------------- class TestSecurityFilterFlags: def _has_ansi(self, s: str) -> bool: return "\x1b[" in s def test_ansi_in_merged_ref_rejected_or_sanitized(self, repo: pathlib.Path) -> None: result = _branch(repo, "--merged", "\x1b[31mmalicious\x1b[0m") assert not self._has_ansi(result.output) def test_ansi_in_no_merged_ref(self, repo: pathlib.Path) -> None: result = _branch(repo, "--no-merged", "\x1b[31mmalicious\x1b[0m") assert not self._has_ansi(result.output) def test_ansi_in_contains_ref(self, repo: pathlib.Path) -> None: result = _branch(repo, "--contains", "\x1b[31mmalicious\x1b[0m") assert not self._has_ansi(result.output) def test_newline_in_branch_name_rejected(self, repo: pathlib.Path) -> None: result = _branch(repo, "branch\nmalicious") assert result.exit_code == 1 def test_ansi_in_delete_json_error_sanitized(self, repo: pathlib.Path) -> None: result = _branch(repo, "-d", "\x1b[31mmalicious\x1b[0m", "--json") assert result.exit_code == 1 assert not self._has_ansi(result.output) # --------------------------------------------------------------------------- # Performance: --sort committeddate with 50 branches # --------------------------------------------------------------------------- class TestSortCommittedDatePerformance: def test_50_branches_committeddate_under_3s(self, repo: pathlib.Path) -> None: for i in range(50): _branch(repo, f"perf/branch-{i:03d}") start = time.monotonic() result = _branch(repo, "--sort", "committeddate", "--json") elapsed = time.monotonic() - start assert result.exit_code == 0 data = json.loads(result.output) assert len(data) == 51 # main + 50 assert elapsed < 3.0, f"--sort committeddate with 51 branches took {elapsed:.2f}s" # --------------------------------------------------------------------------- # Docstrings # --------------------------------------------------------------------------- class TestDocstrings: def _has_doc(self, obj: _AnyObj) -> bool: import inspect doc = inspect.getdoc(obj) return bool(doc and len(doc.strip()) > 10) def test_module_docstring(self) -> None: import muse.cli.commands.branch as m assert self._has_doc(m) def test_ref_file_docstring(self) -> None: from muse.cli.commands.branch import _ref_file assert self._has_doc(_ref_file) def test_list_local_branches_docstring(self) -> None: from muse.cli.commands.branch import _list_local_branches assert self._has_doc(_list_local_branches) def test_list_remotes_docstring(self) -> None: from muse.cli.commands.branch import _list_remotes assert self._has_doc(_list_remotes) def test_upstream_for_docstring(self) -> None: from muse.cli.commands.branch import _upstream_for assert self._has_doc(_upstream_for) def test_commit_ancestors_docstring(self) -> None: from muse.cli.commands.branch import _commit_ancestors assert self._has_doc(_commit_ancestors) def test_is_merged_docstring(self) -> None: from muse.cli.commands.branch import _is_merged assert self._has_doc(_is_merged) def test_contains_commit_docstring(self) -> None: from muse.cli.commands.branch import _contains_commit assert self._has_doc(_contains_commit) def test_cleanup_empty_dirs_docstring(self) -> None: from muse.cli.commands.branch import _cleanup_empty_dirs assert self._has_doc(_cleanup_empty_dirs) def test_resolve_start_point_docstring(self) -> None: from muse.cli.commands.branch import _resolve_start_point assert self._has_doc(_resolve_start_point) def test_run_docstring(self) -> None: from muse.cli.commands.branch import run assert self._has_doc(run)