"""TDD tests for muse#49 Phase 4 — ``get_cache_status`` / ``--cache-status``. Covers CT_11–CT_14: an honest, content-addressed-aware status schema (not the ticket's originally-imagined snapshot-ID-keyed one — see ``docs/code-test-internals.md`` "Decision" section for why), graceful behavior on a fresh repo with no commits and no cache files, and the stale/cache_hit flags tracking real per-file cache warmth. """ from __future__ import annotations import pathlib import pytest from muse.core.callgraph_cache import CallGraphCache from muse.core.commits import get_head_snapshot_id from muse.core.snapshots import read_snapshot from muse.core.symbol_cache import SymbolCache from muse.core.test_selection import ChangedSymbol, get_cache_status, select_tests from tests.cli_test_helper import CliRunner @pytest.fixture def repo(tmp_path: pathlib.Path, monkeypatch: pytest.MonkeyPatch) -> pathlib.Path: monkeypatch.chdir(tmp_path) monkeypatch.setenv("MUSE_REPO_ROOT", str(tmp_path)) runner = CliRunner() result = runner.invoke(None, ["init", "--domain", "code"]) assert result.exit_code == 0, result.output return tmp_path class TestFreshRepoGraceful: def test_no_commits_no_exception_zero_counts(self, repo: pathlib.Path) -> None: """CT_14: a freshly-initialised repo with no commits and no cache files must never raise — just report trivially empty status.""" status = get_cache_status(repo) assert status["head_snapshot_id"] is None assert status["semantic_files_total"] == 0 assert status["files_warm"] == 0 assert status["files_cold"] == 0 assert status["cache_hit"] is True assert status["stale"] is False assert status["symbol_cache_entries"] == 0 assert status["callgraph_cache_entries"] == 0 assert status["symbol_cache_age_seconds"] is None assert status["callgraph_cache_age_seconds"] is None def test_no_muse_repo_at_all_no_exception(self, tmp_path: pathlib.Path) -> None: """Not even a ``.muse/`` directory — must still degrade gracefully.""" status = get_cache_status(tmp_path) assert status["cache_hit"] is True assert status["head_snapshot_id"] is None class TestColdAfterCommitBeforeAnyRun: def test_committed_file_reported_cold(self, repo: pathlib.Path) -> None: """CT_11/CT_12: after a real commit, before any cache-populating ``select_tests`` call, the file must be reported stale/cold.""" (repo / "prod.py").write_text("def compute(x: int) -> int:\n return x * 2\n") runner = CliRunner() assert runner.invoke(None, ["code", "add", "."]).exit_code == 0 assert runner.invoke(None, ["commit", "-m", "v1"]).exit_code == 0 status = get_cache_status(repo) assert status["head_snapshot_id"] is not None assert status["semantic_files_total"] == 1 assert status["files_warm"] == 0 assert status["files_cold"] == 1 assert status["cache_hit"] is False assert status["stale"] is True class TestWarmAfterRealRun: def test_cache_status_reflects_warm_run(self, repo: pathlib.Path) -> None: """CT_13: after a real select_tests run warms both caches, status must flip to cache_hit=True/stale=False and report nonzero entries and a defined (non-negative) age — proving the status function reads the *actual* on-disk cache state, not a hardcoded value.""" (repo / "prod.py").write_text("def compute(x: int) -> int:\n return x * 2\n") runner = CliRunner() assert runner.invoke(None, ["code", "add", "."]).exit_code == 0 assert runner.invoke(None, ["commit", "-m", "v1"]).exit_code == 0 sid = get_head_snapshot_id(repo, "main") snapshot = read_snapshot(repo, sid) assert snapshot is not None sym_cache = SymbolCache.from_root(repo) cg_cache = CallGraphCache.from_root(repo) changed = [ChangedSymbol(address="prod.py::compute", change_kind="modified")] select_tests(repo, changed, snapshot.manifest, cache=sym_cache, callgraph_cache=cg_cache) sym_cache.save() cg_cache.save() status = get_cache_status(repo) assert status["cache_hit"] is True assert status["stale"] is False assert status["files_cold"] == 0 assert status["symbol_cache_entries"] > 0 assert status["callgraph_cache_entries"] > 0 assert status["symbol_cache_age_seconds"] is not None assert status["symbol_cache_age_seconds"] >= 0.0 assert status["callgraph_cache_age_seconds"] is not None assert status["callgraph_cache_age_seconds"] >= 0.0 def test_status_never_triggers_a_parse( self, repo: pathlib.Path, monkeypatch: pytest.MonkeyPatch ) -> None: """get_cache_status must be pure dict-lookup + os.stat — it must never call read_object or parse_symbols, even when warm.""" from unittest.mock import patch (repo / "prod.py").write_text("def compute(x: int) -> int:\n return x * 2\n") runner = CliRunner() assert runner.invoke(None, ["code", "add", "."]).exit_code == 0 assert runner.invoke(None, ["commit", "-m", "v1"]).exit_code == 0 with ( patch("muse.plugins.code._query.read_object") as mock_query_read, patch("muse.plugins.code._callgraph.read_object") as mock_cg_read, patch("muse.plugins.code.ast_parser.parse_symbols") as mock_parse, ): get_cache_status(repo) mock_query_read.assert_not_called() mock_cg_read.assert_not_called() mock_parse.assert_not_called()