"""Tests for grep_audit intern — deterministic, LLM-free.""" import json from interns_mcp.interns.base import Intern, InternResponse from interns_mcp.interns.grep_audit import GrepAudit def test_substring_case_sensitive(tmp_path): f = tmp_path / "claude.md" f.write_text("follow project discipline\nuse superpowers\n", encoding="utf-8") intern = GrepAudit() result = intern.run( paths=[str(f)], patterns=["follow project discipline", "Follow Project Discipline"], output="json", ) assert isinstance(result, InternResponse) data = json.loads(result.text) matches = data["rows"][0]["matches"] assert matches["follow project discipline"] is True assert matches["Follow Project Discipline"] is False def test_substring_case_insensitive(tmp_path): f = tmp_path / "claude.md" f.write_text("follow project discipline\n", encoding="utf-8") intern = GrepAudit() result = intern.run( paths=[str(f)], patterns=["FOLLOW PROJECT DISCIPLINE"], output="json", case_sensitive=False, ) data = json.loads(result.text) assert data["rows"][0]["matches"]["FOLLOW PROJECT DISCIPLINE"] is True def test_regex_named(tmp_path): f = tmp_path / "pyproject.toml" f.write_text('version = "0.3.0"\n', encoding="utf-8") intern = GrepAudit() result = intern.run( paths=[str(f)], patterns=[{"pattern": r'version\s*=\s*"\d+\.\d+\.\d+"', "name": "version-line", "regex": True}], output="json", ) data = json.loads(result.text) assert data["rows"][0]["matches"]["version-line"] is True def test_dict_substring(tmp_path): f = tmp_path / "skill.md" f.write_text("trigger string here\n", encoding="utf-8") intern = GrepAudit() result = intern.run( paths=[str(f)], patterns=[{"pattern": "trigger string", "name": "trigger"}], output="json", ) data = json.loads(result.text) assert data["rows"][0]["matches"]["trigger"] is True def test_output_table(tmp_path): f1 = tmp_path / "a.md" f1.write_text("alpha beta\n", encoding="utf-8") f2 = tmp_path / "b.md" f2.write_text("gamma\n", encoding="utf-8") intern = GrepAudit() result = intern.run( paths=[str(f1), str(f2)], patterns=["alpha", "gamma"], output="table", ) text = result.text assert text.splitlines()[0].startswith("| Path |") assert "alpha" in text.splitlines()[0] assert "gamma" in text.splitlines()[0] # f1 has alpha but not gamma f1_line = [l for l in text.splitlines() if str(f1) in l][0] assert "✅" in f1_line assert "❌" in f1_line # f2 has gamma but not alpha f2_line = [l for l in text.splitlines() if str(f2) in l][0] assert "✅" in f2_line assert "❌" in f2_line def test_output_json(tmp_path): f = tmp_path / "a.md" f.write_text("alpha\n", encoding="utf-8") intern = GrepAudit() result = intern.run( paths=[str(f)], patterns=["alpha", "missing"], output="json", ) data = json.loads(result.text) assert "rows" in data assert len(data["rows"]) == 1 assert data["rows"][0]["path"] == str(f) assert data["rows"][0]["matches"]["alpha"] is True assert data["rows"][0]["matches"]["missing"] is False def test_file_not_found_partial_result(tmp_path): f_real = tmp_path / "exists.md" f_real.write_text("hello\n", encoding="utf-8") f_ghost = tmp_path / "ghost.md" # never created intern = GrepAudit() result = intern.run( paths=[str(f_real), str(f_ghost)], patterns=["hello"], output="json", ) data = json.loads(result.text) assert len(data["rows"]) == 2 # partial result, no abort rows_by_path = {r["path"]: r for r in data["rows"]} assert rows_by_path[str(f_real)]["matches"]["hello"] is True # Ghost row: match is None (unreadable cell), error field present assert rows_by_path[str(f_ghost)]["matches"]["hello"] is None assert "error" in rows_by_path[str(f_ghost)] def test_file_not_found_table_renders_warning(tmp_path): f_ghost = tmp_path / "ghost.md" # never created intern = GrepAudit() result = intern.run( paths=[str(f_ghost)], patterns=["x"], output="table", ) assert "⚠️" in result.text def test_empty_paths(): intern = GrepAudit() result = intern.run(paths=[], patterns=["x"], output="json") data = json.loads(result.text) assert data["rows"] == [] assert result.usage["files_scanned"] == 0 assert result.usage["matches_total"] == 0 def test_empty_patterns(tmp_path): f = tmp_path / "a.md" f.write_text("alpha\n", encoding="utf-8") intern = GrepAudit() result = intern.run(paths=[str(f)], patterns=[], output="json") data = json.loads(result.text) assert len(data["rows"]) == 1 assert data["rows"][0]["matches"] == {} assert result.usage["patterns_evaluated"] == 0 def test_unicode_content_utf8(tmp_path): f = tmp_path / "ru.md" f.write_text("русский текст\n", encoding="utf-8") intern = GrepAudit() result = intern.run( paths=[str(f)], patterns=["русский"], output="json", ) data = json.loads(result.text) assert data["rows"][0]["matches"]["русский"] is True def test_unicode_content_binary_garbage_does_not_crash(tmp_path): f = tmp_path / "binary.bin" f.write_bytes(b"\xff\xfe\x00\x01\x02 hello \xff") intern = GrepAudit() # errors="replace" should let us still grep for "hello" result = intern.run( paths=[str(f)], patterns=["hello"], output="json", ) data = json.loads(result.text) assert data["rows"][0]["matches"]["hello"] is True def test_usage_counts(tmp_path): f1 = tmp_path / "a.md" f1.write_text("alpha\n", encoding="utf-8") f2 = tmp_path / "b.md" f2.write_text("alpha beta\n", encoding="utf-8") intern = GrepAudit() result = intern.run( paths=[str(f1), str(f2)], patterns=["alpha", "beta"], output="json", ) assert result.usage["files_scanned"] == 2 assert result.usage["patterns_evaluated"] == 2 # f1: alpha ✓, beta ✗ → 1 # f2: alpha ✓, beta ✓ → 2 assert result.usage["matches_total"] == 3 def test_endpoint_null_no_client_no_crash(): """LLM-free intern must not require client wiring.""" intern = GrepAudit(config={"endpoint": None}) assert intern.client is None assert intern.endpoint is None def test_base_intern_accepts_endpoint_null_config(): """Base class survives config with explicit endpoint=null (no LLM init triggered).""" intern = Intern(config={"endpoint": None, "model": None}) assert intern.endpoint is None assert intern.model is None assert intern.client is None