"""Seven-tier tests for ``muse/cli/commands/symbols.py``. Tiers ----- Unit — TypedDict fields; _c colour helper; _normalise_language; _file_matches exact/suffix/separator anchoring; _resolve_file_filter match/miss/ambiguous; _lang_counts; _print_human empty/non-empty; _emit_json structure and field names. Integration — -j alias parity; JSON envelope (schema_version, exit_code, duration_ms); --kind / --language / --file filters in JSON; --hashes in JSON; --count still works alongside envelope. End-to-end — full CLI round-trips: basic, count, json, filters, commit ref, invalid kind, ambiguous file, working-tree vs committed. Stress — 1 000 _file_matches calls; 10 000 _lang_counts calls; _emit_json on 500-symbol map. Data integrity — total_symbols accurate; JSON result order by lineno; schema_version, exit_code, duration_ms types correct; all 9 entry fields present; working_tree bool invariant. Security — ANSI in file path/symbol name; hostile --file value; long language name; null byte in kind. Performance — 10 000 _file_matches under 0.5 s; duration_ms field < 30 000ms. """ from __future__ import annotations from collections.abc import Mapping import json import os import pathlib import textwrap import threading import time from typing import get_type_hints import pytest from muse.core.types import fake_id from tests.cli_test_helper import CliRunner, InvokeResult runner = CliRunner() # ────────────────────────────────────────────────────────────────────────────── # Fixture helpers # ────────────────────────────────────────────────────────────────────────────── def _commit(repo: pathlib.Path, files: Mapping[str, str], message: str) -> None: for name, content in files.items(): path = repo / name path.parent.mkdir(parents=True, exist_ok=True) path.write_text(content, encoding="utf-8") saved = os.getcwd() try: os.chdir(repo) runner.invoke(None, ["code", "add", "."]) runner.invoke(None, ["commit", "-m", message]) finally: os.chdir(saved) 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 _syms(repo: pathlib.Path, *args: str) -> InvokeResult: return _invoke(repo, ["code", "symbols", *args]) @pytest.fixture() def sym_repo(tmp_path: pathlib.Path) -> pathlib.Path: """Repo with two Python files and distinct symbol kinds.""" saved = os.getcwd() try: os.chdir(tmp_path) runner.invoke(None, ["init"]) finally: os.chdir(saved) _commit(tmp_path, { "billing.py": textwrap.dedent("""\ class Invoice: def __init__(self, amount): self.amount = amount def total(self): return self.amount * 1.1 def process_order(inv): return inv.total() async def send_email(to): pass """), "utils.py": textwrap.dedent("""\ def helper(): return True class Config: debug = False """), }, "feat: add billing and utils") return tmp_path # ────────────────────────────────────────────────────────────────────────────── # Unit — TypedDict # ────────────────────────────────────────────────────────────────────────────── class TestTypedDict: def test_symbols_json_typeddict_exists(self) -> None: from muse.cli.commands.symbols import _SymbolsJson # noqa: F401 def test_has_schema_version(self) -> None: from muse.cli.commands.symbols import _SymbolsJson assert "schema" in get_type_hints(_SymbolsJson) def test_has_exit_code(self) -> None: from muse.cli.commands.symbols import _SymbolsJson assert "exit_code" in get_type_hints(_SymbolsJson) def test_has_duration_ms(self) -> None: from muse.cli.commands.symbols import _SymbolsJson assert "duration_ms" in get_type_hints(_SymbolsJson) def test_has_core_fields(self) -> None: from muse.cli.commands.symbols import _SymbolsJson hints = get_type_hints(_SymbolsJson) for field in ("source_ref", "working_tree", "total_symbols", "results"): assert field in hints, f"missing field: {field}" # ────────────────────────────────────────────────────────────────────────────── # Unit — _c colour helper # ────────────────────────────────────────────────────────────────────────────── class TestColorHelper: def test_no_tty_returns_plain_text(self) -> None: from muse.cli.commands.symbols import _c, _BLUE assert _c("hello", _BLUE, tty=False) == "hello" def test_tty_wraps_with_ansi(self) -> None: from muse.cli.commands.symbols import _c, _BLUE, _RESET result = _c("hello", _BLUE, tty=True) assert _BLUE in result assert _RESET in result assert "hello" in result def test_multiple_codes_all_applied(self) -> None: from muse.cli.commands.symbols import _c, _BOLD, _YELLOW, _RESET result = _c("x", _BOLD, _YELLOW, tty=True) assert _BOLD in result assert _YELLOW in result # ────────────────────────────────────────────────────────────────────────────── # Unit — _normalise_language # ────────────────────────────────────────────────────────────────────────────── class TestNormaliseLanguage: def test_python_lowercase_normalised(self) -> None: from muse.cli.commands.symbols import _normalise_language assert _normalise_language("python") == "Python" def test_python_uppercase_normalised(self) -> None: from muse.cli.commands.symbols import _normalise_language assert _normalise_language("PYTHON") == "Python" def test_python_mixed_normalised(self) -> None: from muse.cli.commands.symbols import _normalise_language assert _normalise_language("Python") == "Python" def test_unknown_language_returned_unchanged(self) -> None: from muse.cli.commands.symbols import _normalise_language assert _normalise_language("Brainfuck") == "Brainfuck" def test_strips_whitespace(self) -> None: from muse.cli.commands.symbols import _normalise_language result = _normalise_language(" python ") assert result == "Python" # ────────────────────────────────────────────────────────────────────────────── # Unit — _file_matches # ────────────────────────────────────────────────────────────────────────────── class TestFileMatches: def test_exact_path_matches(self) -> None: from muse.cli.commands.symbols import _file_matches assert _file_matches("src/billing.py", "src/billing.py") is True def test_suffix_with_slash_anchor_matches(self) -> None: from muse.cli.commands.symbols import _file_matches assert _file_matches("src/billing.py", "billing.py") is True def test_partial_name_does_not_match(self) -> None: """'y.py' must NOT match 'billy.py' — separator anchor required.""" from muse.cli.commands.symbols import _file_matches assert _file_matches("billy.py", "y.py") is False def test_no_match_returns_false(self) -> None: from muse.cli.commands.symbols import _file_matches assert _file_matches("src/utils.py", "billing.py") is False def test_windows_backslash_normalised(self) -> None: """Backslash in the suffix filter is normalised to slash before matching.""" from muse.cli.commands.symbols import _file_matches assert _file_matches("a/b/billing.py", "b\\billing.py") is True def test_deep_path_suffix_matches(self) -> None: from muse.cli.commands.symbols import _file_matches assert _file_matches("a/b/c/billing.py", "billing.py") is True # ────────────────────────────────────────────────────────────────────────────── # Unit — _resolve_file_filter # ────────────────────────────────────────────────────────────────────────────── class TestResolveFileFilter: def _manifest(self, *paths: str) -> Mapping[str, object]: return {p: fake_id("aa") for p in paths} def test_exact_match_returns_path(self) -> None: from muse.cli.commands.symbols import _resolve_file_filter result = _resolve_file_filter("billing.py", self._manifest("billing.py")) assert result == "billing.py" def test_suffix_match_returns_full_path(self) -> None: from muse.cli.commands.symbols import _resolve_file_filter result = _resolve_file_filter("billing.py", self._manifest("src/billing.py")) assert result == "src/billing.py" def test_no_match_returns_none(self) -> None: from muse.cli.commands.symbols import _resolve_file_filter result = _resolve_file_filter("nope.py", self._manifest("billing.py")) assert result is None def test_ambiguous_raises_system_exit(self) -> None: from muse.cli.commands.symbols import _resolve_file_filter with pytest.raises(SystemExit): _resolve_file_filter( "billing.py", self._manifest("a/billing.py", "b/billing.py"), ) # ────────────────────────────────────────────────────────────────────────────── # Unit — _lang_counts # ────────────────────────────────────────────────────────────────────────────── class TestLangCounts: def _tree(self, n: int) -> Mapping[str, object]: """Fake SymbolTree with n symbols.""" return {f"sym_{i}": {"lineno": i} for i in range(n)} def test_single_python_file(self) -> None: from muse.cli.commands.symbols import _lang_counts counts = _lang_counts({"billing.py": self._tree(3)}) assert counts.get("Python") == 3 def test_multiple_files_summed_per_language(self) -> None: from muse.cli.commands.symbols import _lang_counts counts = _lang_counts({ "a.py": self._tree(2), "b.py": self._tree(4), }) assert counts.get("Python") == 6 def test_empty_map_returns_empty(self) -> None: from muse.cli.commands.symbols import _lang_counts assert _lang_counts({}) == {} def test_unknown_extension_grouped_correctly(self) -> None: from muse.cli.commands.symbols import _lang_counts counts = _lang_counts({"thing.xyz": self._tree(1)}) assert sum(counts.values()) == 1 # ────────────────────────────────────────────────────────────────────────────── # Unit — _print_human # ────────────────────────────────────────────────────────────────────────────── class TestPrintHuman: def test_empty_map_prints_no_symbols(self, capsys: pytest.CaptureFixture[str]) -> None: from muse.cli.commands.symbols import _print_human _print_human({}, show_hashes=False, tty=False) captured = capsys.readouterr() assert "no semantic symbols found" in captured.out def test_non_empty_map_shows_file_and_symbol(self, capsys: pytest.CaptureFixture[str]) -> None: from muse.cli.commands.symbols import _print_human tree = { "billing.py::Invoice": { "kind": "class", "name": "Invoice", "qualified_name": "Invoice", "lineno": 1, "content_id": fake_id("ab"), } } _print_human({"billing.py": tree}, show_hashes=False, tty=False) out = capsys.readouterr().out assert "billing.py" in out assert "Invoice" in out def test_show_hashes_appends_hash_suffix(self, capsys: pytest.CaptureFixture[str]) -> None: from muse.cli.commands.symbols import _print_human tree = { "f.py::fn": { "kind": "function", "name": "fn", "qualified_name": "fn", "lineno": 1, "content_id": fake_id("cd"), } } _print_human({"f.py": tree}, show_hashes=True, tty=False) out = capsys.readouterr().out assert ".." in out def test_summary_line_shows_count(self, capsys: pytest.CaptureFixture[str]) -> None: from muse.cli.commands.symbols import _print_human tree = { "f.py::fn": { "kind": "function", "name": "fn", "qualified_name": "fn", "lineno": 1, "content_id": fake_id("aa"), } } _print_human({"f.py": tree}, show_hashes=False, tty=False) out = capsys.readouterr().out assert "1 symbol" in out or "symbol" in out # ────────────────────────────────────────────────────────────────────────────── # Unit — _emit_json # ────────────────────────────────────────────────────────────────────────────── class TestEmitJson: def _tree(self) -> Mapping[str, object]: return { "billing.py::Invoice": { "kind": "class", "name": "Invoice", "qualified_name": "Invoice", "lineno": 1, "end_lineno": 10, "content_id": fake_id("aa"), "body_hash": fake_id("bb"), "signature_id": fake_id("cc"), } } def test_emits_json_with_schema_version(self, capsys: pytest.CaptureFixture[str]) -> None: from muse.cli.commands.symbols import _emit_json _emit_json({"billing.py": self._tree()}, source_ref="abc123", working_tree=True, elapsed=lambda: 0.0) d = json.loads(capsys.readouterr().out) assert "schema" in d def test_emits_json_with_exit_code(self, capsys: pytest.CaptureFixture[str]) -> None: from muse.cli.commands.symbols import _emit_json _emit_json({"billing.py": self._tree()}, source_ref="abc123", working_tree=True, elapsed=lambda: 0.0) d = json.loads(capsys.readouterr().out) assert d["exit_code"] == 0 def test_emits_json_with_duration_ms(self, capsys: pytest.CaptureFixture[str]) -> None: from muse.cli.commands.symbols import _emit_json _emit_json({"billing.py": self._tree()}, source_ref="abc123", working_tree=True, elapsed=lambda: 0.0) d = json.loads(capsys.readouterr().out) assert "duration_ms" in d assert isinstance(d["duration_ms"], float) def test_total_symbols_correct(self, capsys: pytest.CaptureFixture[str]) -> None: from muse.cli.commands.symbols import _emit_json _emit_json({"billing.py": self._tree()}, source_ref="abc123", working_tree=True, elapsed=lambda: 0.0) d = json.loads(capsys.readouterr().out) assert d["total_symbols"] == 1 def test_result_entry_has_path_field(self, capsys: pytest.CaptureFixture[str]) -> None: from muse.cli.commands.symbols import _emit_json _emit_json({"billing.py": self._tree()}, source_ref="abc123", working_tree=True, elapsed=lambda: 0.0) d = json.loads(capsys.readouterr().out) assert d["results"][0]["path"] == "billing.py" def test_result_entry_fields_complete(self, capsys: pytest.CaptureFixture[str]) -> None: from muse.cli.commands.symbols import _emit_json _emit_json({"billing.py": self._tree()}, source_ref="abc123", working_tree=True, elapsed=lambda: 0.0) d = json.loads(capsys.readouterr().out) entry = d["results"][0] for field in ("address", "kind", "name", "qualified_name", "path", "lineno", "end_lineno", "content_id", "body_hash", "signature_id"): assert field in entry, f"missing field: {field}" def test_working_tree_false_propagated(self, capsys: pytest.CaptureFixture[str]) -> None: from muse.cli.commands.symbols import _emit_json _emit_json({"billing.py": self._tree()}, source_ref="a1b2c3d4", working_tree=False, elapsed=lambda: 0.0) d = json.loads(capsys.readouterr().out) assert d["working_tree"] is False def test_empty_map_emits_zero_results(self, capsys: pytest.CaptureFixture[str]) -> None: from muse.cli.commands.symbols import _emit_json _emit_json({}, source_ref="abc123", working_tree=True, elapsed=lambda: 0.0) d = json.loads(capsys.readouterr().out) assert d["total_symbols"] == 0 assert d["results"] == [] # ────────────────────────────────────────────────────────────────────────────── # Integration — alias, docstrings, envelope # ────────────────────────────────────────────────────────────────────────────── class TestAliasRegistration: def test_j_alias_registered(self) -> None: from muse.cli.commands.symbols import register import argparse p = argparse.ArgumentParser() sub = p.add_subparsers() register(sub) ns = p.parse_args(["symbols", "-j"]) assert ns.json_out is True def test_json_long_form_works(self) -> None: from muse.cli.commands.symbols import register import argparse p = argparse.ArgumentParser() sub = p.add_subparsers() register(sub) ns = p.parse_args(["symbols", "--json"]) assert ns.json_out is True class TestDocstrings: def test_register_mentions_json_alias(self) -> None: from muse.cli.commands.symbols import register doc = register.__doc__ or "" assert "--json" in doc or "-j" in doc def test_run_mentions_exit_code(self) -> None: from muse.cli.commands.symbols import run assert "exit_code" in (run.__doc__ or "") def test_run_mentions_duration_ms(self) -> None: from muse.cli.commands.symbols import run assert "duration_ms" in (run.__doc__ or "") def test_run_mentions_schema_version(self) -> None: from muse.cli.commands.symbols import run assert "schema" in (run.__doc__ or "") class TestJsonEnvelope: def test_schema_version_present(self, sym_repo: pathlib.Path) -> None: r = _syms(sym_repo, "--json") assert r.exit_code == 0 assert "schema" in json.loads(r.output) def test_exit_code_zero(self, sym_repo: pathlib.Path) -> None: r = _syms(sym_repo, "--json") assert r.exit_code == 0 assert json.loads(r.output)["exit_code"] == 0 def test_duration_ms_is_float(self, sym_repo: pathlib.Path) -> None: r = _syms(sym_repo, "--json") assert r.exit_code == 0 d = json.loads(r.output) assert isinstance(d["duration_ms"], float) def test_schema_version_nonempty_string(self, sym_repo: pathlib.Path) -> None: r = _syms(sym_repo, "--json") assert r.exit_code == 0 d = json.loads(r.output) assert isinstance(d["schema"], int) and d["schema"] > 0 class TestJsonAlias: def test_j_parity_with_json(self, sym_repo: pathlib.Path) -> None: r1 = _syms(sym_repo, "--json") r2 = _syms(sym_repo, "-j") assert r1.exit_code == 0 assert r2.exit_code == 0 d1, d2 = json.loads(r1.output), json.loads(r2.output) assert d1["total_symbols"] == d2["total_symbols"] assert d1["results"] == d2["results"] assert d1["schema"] == d2["schema"] assert d1["exit_code"] == d2["exit_code"] # ────────────────────────────────────────────────────────────────────────────── # End-to-end # ────────────────────────────────────────────────────────────────────────────── class TestEndToEnd: def test_basic_exits_zero(self, sym_repo: pathlib.Path) -> None: assert _syms(sym_repo).exit_code == 0 def test_basic_shows_symbols(self, sym_repo: pathlib.Path) -> None: r = _syms(sym_repo) assert "Invoice" in r.output assert "symbols across" in r.output def test_count_flag(self, sym_repo: pathlib.Path) -> None: r = _syms(sym_repo, "--count") assert r.exit_code == 0 assert "symbols" in r.output assert "Python" in r.output assert "Invoice" not in r.output def test_kind_class_filter(self, sym_repo: pathlib.Path) -> None: r = _syms(sym_repo, "--kind", "class") assert r.exit_code == 0 assert "Invoice" in r.output assert "process_order" not in r.output def test_kind_function_filter(self, sym_repo: pathlib.Path) -> None: r = _syms(sym_repo, "--kind", "function") assert r.exit_code == 0 assert "process_order" in r.output assert "Invoice" not in r.output def test_invalid_kind_exits_nonzero(self, sym_repo: pathlib.Path) -> None: r = _syms(sym_repo, "--kind", "potato") assert r.exit_code != 0 def test_file_filter(self, sym_repo: pathlib.Path) -> None: r = _syms(sym_repo, "--file", "billing.py") assert r.exit_code == 0 assert "Invoice" in r.output def test_file_filter_no_match_shows_no_symbols(self, sym_repo: pathlib.Path) -> None: r = _syms(sym_repo, "--file", "nonexistent.py") assert r.exit_code == 0 assert "no semantic symbols found" in r.output def test_language_filter_python(self, sym_repo: pathlib.Path) -> None: r = _syms(sym_repo, "--language", "python") assert r.exit_code == 0 assert "Invoice" in r.output def test_language_filter_case_insensitive(self, sym_repo: pathlib.Path) -> None: for variant in ("python", "Python", "PYTHON"): r = _syms(sym_repo, "--language", variant) assert r.exit_code == 0, f"failed for {variant!r}" assert "Invoice" in r.output def test_language_no_match_shows_no_symbols(self, sym_repo: pathlib.Path) -> None: r = _syms(sym_repo, "--language", "Go") assert r.exit_code == 0 assert "no semantic symbols found" in r.output def test_hashes_flag(self, sym_repo: pathlib.Path) -> None: r = _syms(sym_repo, "--hashes") assert r.exit_code == 0 assert ".." in r.output def test_count_and_json_mutually_exclusive(self, sym_repo: pathlib.Path) -> None: r = _syms(sym_repo, "--count", "--json") assert r.exit_code != 0 def test_commit_head_exits_zero(self, sym_repo: pathlib.Path) -> None: r = _syms(sym_repo, "--commit", "HEAD") assert r.exit_code == 0 assert "Invoice" in r.output def test_json_working_tree_true_when_no_commit(self, sym_repo: pathlib.Path) -> None: r = _syms(sym_repo, "--json") assert r.exit_code == 0 d = json.loads(r.output) assert d["working_tree"] is True assert d["source_ref"] == "working-tree" def test_json_working_tree_false_with_commit(self, sym_repo: pathlib.Path) -> None: r = _syms(sym_repo, "--json", "--commit", "HEAD") assert r.exit_code == 0 d = json.loads(r.output) assert d["working_tree"] is False assert d["source_ref"] != "working-tree" def test_json_result_path_field(self, sym_repo: pathlib.Path) -> None: r = _syms(sym_repo, "--json", "--file", "billing.py") assert r.exit_code == 0 d = json.loads(r.output) assert all(e["path"] == "billing.py" for e in d["results"]) def test_j_alias_works_in_cli(self, sym_repo: pathlib.Path) -> None: r = _syms(sym_repo, "-j") assert r.exit_code == 0 d = json.loads(r.output) assert "total_symbols" in d def test_invalid_commit_ref_exits_nonzero(self, sym_repo: pathlib.Path) -> None: r = _syms(sym_repo, "--commit", "deadbeefdeadbeef") assert r.exit_code != 0 # ────────────────────────────────────────────────────────────────────────────── # Stress # ────────────────────────────────────────────────────────────────────────────── class TestStress: def test_1000_file_matches_calls(self) -> None: from muse.cli.commands.symbols import _file_matches for i in range(1_000): _file_matches(f"src/file{i}.py", "billing.py") def test_10000_normalise_language_calls(self) -> None: from muse.cli.commands.symbols import _normalise_language for _ in range(10_000): result = _normalise_language("python") assert result == "Python" def test_emit_json_500_symbol_map(self, capsys: pytest.CaptureFixture[str]) -> None: from muse.cli.commands.symbols import _emit_json tree = { f"f.py::sym_{i}": { "kind": "function", "name": f"sym_{i}", "qualified_name": f"sym_{i}", "lineno": i + 1, "end_lineno": i + 5, "content_id": fake_id("aa"), "body_hash": fake_id("bb"), "signature_id": fake_id("cc"), } for i in range(500) } _emit_json({"f.py": tree}, source_ref="abc123", working_tree=True, elapsed=lambda: 0.0) d = json.loads(capsys.readouterr().out) assert d["total_symbols"] == 500 def test_concurrent_file_matches(self) -> None: from muse.cli.commands.symbols import _file_matches results: list[bool] = [] lock = threading.Lock() def _run() -> None: v = _file_matches("src/billing.py", "billing.py") with lock: results.append(v) threads = [threading.Thread(target=_run) for _ in range(50)] for t in threads: t.start() for t in threads: t.join() assert all(results) assert len(results) == 50 # ────────────────────────────────────────────────────────────────────────────── # Data integrity # ────────────────────────────────────────────────────────────────────────────── class TestDataIntegrity: def test_total_symbols_matches_results_length(self, sym_repo: pathlib.Path) -> None: r = _syms(sym_repo, "--json") assert r.exit_code == 0 d = json.loads(r.output) assert d["total_symbols"] == len(d["results"]) def test_json_results_ordered_by_lineno(self, sym_repo: pathlib.Path) -> None: r = _syms(sym_repo, "--json", "--file", "billing.py") assert r.exit_code == 0 linenos = [e["lineno"] for e in json.loads(r.output)["results"]] assert linenos == sorted(linenos) def test_all_result_fields_present(self, sym_repo: pathlib.Path) -> None: r = _syms(sym_repo, "--json") assert r.exit_code == 0 for entry in json.loads(r.output)["results"]: for field in ("address", "kind", "name", "qualified_name", "path", "lineno", "end_lineno", "content_id", "body_hash", "signature_id"): assert field in entry, f"missing field: {field}" def test_schema_version_is_string(self, sym_repo: pathlib.Path) -> None: r = _syms(sym_repo, "--json") assert r.exit_code == 0 assert isinstance(json.loads(r.output)["schema"], int) def test_exit_code_is_int(self, sym_repo: pathlib.Path) -> None: r = _syms(sym_repo, "--json") assert r.exit_code == 0 assert isinstance(json.loads(r.output)["exit_code"], int) def test_duration_ms_nonnegative(self, sym_repo: pathlib.Path) -> None: r = _syms(sym_repo, "--json") assert r.exit_code == 0 assert json.loads(r.output)["duration_ms"] >= 0 def test_working_tree_is_bool(self, sym_repo: pathlib.Path) -> None: r = _syms(sym_repo, "--json") assert r.exit_code == 0 assert isinstance(json.loads(r.output)["working_tree"], bool) def test_kind_filter_propagated_to_results(self, sym_repo: pathlib.Path) -> None: r = _syms(sym_repo, "--json", "--kind", "class") assert r.exit_code == 0 d = json.loads(r.output) assert all(e["kind"] == "class" for e in d["results"]) def test_file_filter_propagated_to_results(self, sym_repo: pathlib.Path) -> None: r = _syms(sym_repo, "--json", "--file", "billing.py") assert r.exit_code == 0 d = json.loads(r.output) assert all(e["path"] == "billing.py" for e in d["results"]) def test_lang_counts_total_matches_total_symbols(self, sym_repo: pathlib.Path) -> None: from muse.cli.commands.symbols import _lang_counts tree = { "a.py::f1": {"lineno": 1}, "a.py::f2": {"lineno": 2}, } counts = _lang_counts({"a.py": tree}) assert sum(counts.values()) == 2 # ────────────────────────────────────────────────────────────────────────────── # Security # ────────────────────────────────────────────────────────────────────────────── class TestSecurity: def test_ansi_in_file_filter_does_not_crash(self, sym_repo: pathlib.Path) -> None: r = _syms(sym_repo, "--file", "\x1b[31mbad\x1b[0m.py") assert r.exit_code in (0, 1, 2) def test_very_long_language_does_not_crash(self, sym_repo: pathlib.Path) -> None: r = _syms(sym_repo, "--language", "x" * 10_000) assert r.exit_code in (0, 1, 2) def test_sql_injection_in_file_filter_does_not_crash(self, sym_repo: pathlib.Path) -> None: r = _syms(sym_repo, "--file", "'; DROP TABLE symbols; --") assert r.exit_code in (0, 1, 2) def test_file_matches_with_ansi_in_path(self) -> None: from muse.cli.commands.symbols import _file_matches malicious = "\x1b[31mbilling\x1b[0m.py" # Should return False without raising result = _file_matches("billing.py", malicious) assert isinstance(result, bool) def test_hostile_detail_survives_json_serialisation(self, capsys: pytest.CaptureFixture[str]) -> None: from muse.cli.commands.symbols import _emit_json tree = { "f.py::fn": { "kind": "function", "name": '"; DROP TABLE --', "qualified_name": '"; DROP TABLE --', "lineno": 1, "end_lineno": 5, "content_id": fake_id("aa"), "body_hash": fake_id("bb"), "signature_id": fake_id("cc"), } } _emit_json({"f.py": tree}, source_ref="abc", working_tree=True, elapsed=lambda: 0.0) d = json.loads(capsys.readouterr().out) assert d["results"][0]["name"] == '"; DROP TABLE --' def test_unicode_in_file_filter_does_not_crash(self, sym_repo: pathlib.Path) -> None: r = _syms(sym_repo, "--file", "音符.py") assert r.exit_code in (0, 1, 2) # ────────────────────────────────────────────────────────────────────────────── # Performance # ────────────────────────────────────────────────────────────────────────────── class TestPerformance: def test_10000_file_matches_under_500ms(self) -> None: from muse.cli.commands.symbols import _file_matches start = time.perf_counter() for i in range(10_000): _file_matches(f"src/billing_{i}.py", "billing.py") elapsed = time.perf_counter() - start assert elapsed < 0.5, f"10 000 _file_matches took {elapsed:.2f}s" def test_duration_ms_under_30000ms(self, sym_repo: pathlib.Path) -> None: r = _syms(sym_repo, "--json") assert r.exit_code == 0 d = json.loads(r.output) assert d["duration_ms"] < 30_000 # Flag registration # ────────────────────────────────────────────────────────────────────────────── class TestRegisterFlags: def _parse(self, *args: str) -> "argparse.Namespace": import argparse from muse.cli.commands.symbols import register p = argparse.ArgumentParser() sub = p.add_subparsers() register(sub) return p.parse_args(["symbols", *args]) def test_default_json_out_is_false(self) -> None: ns = self._parse() assert ns.json_out is False def test_json_flag_sets_json_out(self) -> None: ns = self._parse("--json") assert ns.json_out is True def test_j_shorthand_sets_json_out(self) -> None: ns = self._parse("-j") assert ns.json_out is True