test_cmd_symlog_stat.py
python
sha256:ce542c4d0f6dacc9520c02d0e3378bf96bc377d118a7402f2bbc8af1749b08e0
feat(code-test): audit cache internals, add cache_hit/--cac…
Sonnet 5
minor
⚠ breaking
6 hours ago
| 1 | """muse#53 item 10 -- `muse symlog stat [FILE::SYMBOL] [--file path] [--json]`. |
| 2 | |
| 3 | Summary statistics over a symbol's (or an entire file's) symlog journal. |
| 4 | """ |
| 5 | |
| 6 | from __future__ import annotations |
| 7 | |
| 8 | import json |
| 9 | import os |
| 10 | import pathlib |
| 11 | import textwrap |
| 12 | |
| 13 | from tests.cli_test_helper import CliRunner |
| 14 | |
| 15 | runner = CliRunner() |
| 16 | |
| 17 | |
| 18 | def _invoke(repo: pathlib.Path, args: list[str]): |
| 19 | saved = os.getcwd() |
| 20 | try: |
| 21 | os.chdir(repo) |
| 22 | return runner.invoke(None, args) |
| 23 | finally: |
| 24 | os.chdir(saved) |
| 25 | |
| 26 | |
| 27 | def _init_repo(repo: pathlib.Path) -> None: |
| 28 | repo.mkdir(parents=True, exist_ok=True) |
| 29 | _invoke(repo, ["init"]) |
| 30 | |
| 31 | |
| 32 | def _write_file(repo: pathlib.Path, rel: str, content: str) -> None: |
| 33 | p = repo / rel |
| 34 | p.parent.mkdir(parents=True, exist_ok=True) |
| 35 | p.write_text(textwrap.dedent(content)) |
| 36 | |
| 37 | |
| 38 | def _commit(repo: pathlib.Path, msg: str, author: str = "claude-code") -> str: |
| 39 | _invoke(repo, ["code", "add", "."]) |
| 40 | result = _invoke(repo, [ |
| 41 | "commit", "-m", msg, |
| 42 | "--agent-id", "claude-code", |
| 43 | "--model-id", "claude-sonnet-4-6", |
| 44 | "--author", author, |
| 45 | "--json", |
| 46 | ]) |
| 47 | data = json.loads(result.stdout) |
| 48 | return data["commit_id"] |
| 49 | |
| 50 | |
| 51 | def _make_repo(tmp_path: pathlib.Path) -> pathlib.Path: |
| 52 | _init_repo(tmp_path) |
| 53 | return tmp_path |
| 54 | |
| 55 | |
| 56 | def _symlog(repo: pathlib.Path, *args: str): |
| 57 | return _invoke(repo, ["symlog", *args]) |
| 58 | |
| 59 | |
| 60 | class TestSymlogStatSingleSymbol: |
| 61 | def test_empty_symlog_reports_zero(self, tmp_path: pathlib.Path) -> None: |
| 62 | repo = _make_repo(tmp_path) |
| 63 | result = _symlog(repo, "stat", "src/billing.py::compute_total", "--json") |
| 64 | data = json.loads(result.stdout) |
| 65 | assert data["symbol"] == "src/billing.py::compute_total" |
| 66 | assert data["total_entries"] == 0 |
| 67 | assert data["operation_breakdown"] == {} |
| 68 | assert data["most_active_author"] is None |
| 69 | assert data["first_commit_id"] is None |
| 70 | assert data["last_commit_id"] is None |
| 71 | |
| 72 | def test_counts_created_and_modified(self, tmp_path: pathlib.Path) -> None: |
| 73 | repo = _make_repo(tmp_path) |
| 74 | _write_file(repo, "src/billing.py", "def compute_total(items):\n return sum(items)\n") |
| 75 | _commit(repo, "add compute_total") |
| 76 | _write_file(repo, "src/billing.py", "def compute_total(items):\n return sum(items) + 1\n") |
| 77 | _commit(repo, "fix off-by-one") |
| 78 | |
| 79 | result = _symlog(repo, "stat", "src/billing.py::compute_total", "--json") |
| 80 | data = json.loads(result.stdout) |
| 81 | assert data["total_entries"] == 2 |
| 82 | assert data["operation_breakdown"]["symbol-created"] == 1 |
| 83 | assert data["operation_breakdown"]["symbol-modified"] == 1 |
| 84 | assert data["most_active_author"] == "claude-code" |
| 85 | assert data["first_commit_id"] is not None |
| 86 | assert data["last_commit_id"] is not None |
| 87 | |
| 88 | def test_missing_symbol_and_file_errors(self, tmp_path: pathlib.Path) -> None: |
| 89 | repo = _make_repo(tmp_path) |
| 90 | result = _symlog(repo, "stat", "--json") |
| 91 | assert result.exit_code == 1 |
| 92 | |
| 93 | def test_symbol_and_file_both_given_errors(self, tmp_path: pathlib.Path) -> None: |
| 94 | repo = _make_repo(tmp_path) |
| 95 | result = _symlog(repo, "stat", "src/billing.py::compute_total", "--file", "src/billing.py", "--json") |
| 96 | assert result.exit_code == 1 |
| 97 | |
| 98 | def test_invalid_symbol_address_errors(self, tmp_path: pathlib.Path) -> None: |
| 99 | repo = _make_repo(tmp_path) |
| 100 | result = _symlog(repo, "stat", "no-double-colon-here", "--json") |
| 101 | assert result.exit_code == 1 |
| 102 | |
| 103 | |
| 104 | class TestSymlogStatFileMode: |
| 105 | def test_aggregates_across_symbols_in_file(self, tmp_path: pathlib.Path) -> None: |
| 106 | repo = _make_repo(tmp_path) |
| 107 | _write_file(repo, "src/billing.py", ( |
| 108 | "def compute_total(items):\n return sum(items)\n\n" |
| 109 | "def apply_discount(total, pct):\n return total * (1 - pct)\n" |
| 110 | )) |
| 111 | _commit(repo, "add two functions") |
| 112 | |
| 113 | result = _symlog(repo, "stat", "--file", "src/billing.py", "--json") |
| 114 | data = json.loads(result.stdout) |
| 115 | assert data["file"] == "src/billing.py" |
| 116 | assert data["symbol"] is None |
| 117 | assert data["total_entries"] == 2 |
| 118 | assert data["operation_breakdown"]["symbol-created"] == 2 |
| 119 | |
| 120 | def test_file_with_no_symlogs_reports_zero(self, tmp_path: pathlib.Path) -> None: |
| 121 | repo = _make_repo(tmp_path) |
| 122 | result = _symlog(repo, "stat", "--file", "src/nonexistent.py", "--json") |
| 123 | data = json.loads(result.stdout) |
| 124 | assert data["total_entries"] == 0 |
File History
1 commit
sha256:ce542c4d0f6dacc9520c02d0e3378bf96bc377d118a7402f2bbc8af1749b08e0
feat(code-test): audit cache internals, add cache_hit/--cac…
Sonnet 5
minor
⚠
6 hours ago