feat(lib): bundle MCP servers from .common/lib into factory/lib/
Copies source (no node_modules, dist, .tasks, .wiki, __pycache__) for: - projects-meta-mcp v2.25.0 (TypeScript/Node) - wiki-graph v0.3.1 (TypeScript/Node) - interns-mcp v0.3.3 (Python/FastMCP) .gitignore: exclude lib build artefacts (node_modules, dist, .venv, __pycache__, *.pyc) bootstrap.ps1: add MCP build step — npm install+build for TS servers, venv+pip for Python Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
237
lib/interns-mcp/tests/test_grep_audit.py
Normal file
237
lib/interns-mcp/tests/test_grep_audit.py
Normal file
@@ -0,0 +1,237 @@
|
||||
"""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
|
||||
Reference in New Issue
Block a user