"""Tests for knowledge_gap_identifier module.""" import sys import os import tempfile import shutil from pathlib import Path sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', 'scripts')) from knowledge_gap_identifier import KnowledgeGapIdentifier, GapType, GapSeverity def _make_repo(tmpdir, structure): """Create a test repo from a dict of {path: content}.""" for rel_path, content in structure.items(): p = Path(tmpdir) / rel_path p.parent.mkdir(parents=True, exist_ok=True) p.write_text(content) def test_undocumented_symbol(): with tempfile.TemporaryDirectory() as tmpdir: _make_repo(tmpdir, { "src/calculator.py": "def add(a, b):\n return a + b\n", "README.md": "# Calculator\n", }) report = KnowledgeGapIdentifier().analyze(tmpdir) undocumented = [g for g in report.gaps if g.gap_type == GapType.UNDOCUMENTED] assert any(g.name == "add" for g in undocumented), "add should be undocumented" def test_documented_symbol_no_gap(): with tempfile.TemporaryDirectory() as tmpdir: _make_repo(tmpdir, { "src/calculator.py": "def add(a, b):\n return a + b\n", "README.md": "# Calculator\nUse `add()` to add numbers.\n", }) report = KnowledgeGapIdentifier().analyze(tmpdir) undocumented = [g for g in report.gaps if g.gap_type == GapType.UNDOCUMENTED and g.name == "add"] assert len(undocumented) == 0, "add is documented, should not be flagged" def test_untested_module(): with tempfile.TemporaryDirectory() as tmpdir: _make_repo(tmpdir, { "src/calculator.py": "def add(a, b):\n return a + b\n", "src/helper.py": "def format(x):\n return str(x)\n", "tests/test_calculator.py": "from src.calculator import add\nassert add(1,2) == 3\n", }) report = KnowledgeGapIdentifier().analyze(tmpdir) untested = [g for g in report.gaps if g.gap_type == GapType.UNTESTED] assert any("helper" in g.name for g in untested), "helper should be untested" def test_tested_module_no_gap(): with tempfile.TemporaryDirectory() as tmpdir: _make_repo(tmpdir, { "src/calculator.py": "def add(a, b):\n return a + b\n", "tests/test_calculator.py": "def test_add():\n assert True\n", }) report = KnowledgeGapIdentifier().analyze(tmpdir) untested = [g for g in report.gaps if g.gap_type == GapType.UNTESTED and "calculator" in g.name] assert len(untested) == 0, "calculator has tests, should not be flagged" def test_missing_implementation(): with tempfile.TemporaryDirectory() as tmpdir: _make_repo(tmpdir, { "src/app.py": "def run():\n pass\n", "docs/api.md": "# API\nUse `NonExistentClass` to do things.\n", }) report = KnowledgeGapIdentifier().analyze(tmpdir) missing = [g for g in report.gaps if g.gap_type == GapType.MISSING_IMPLEMENTATION] assert any(g.name == "NonExistentClass" for g in missing) def test_private_symbols_skipped(): with tempfile.TemporaryDirectory() as tmpdir: _make_repo(tmpdir, { "src/app.py": "def _internal():\n pass\ndef public():\n pass\n", "README.md": "# App\n", }) report = KnowledgeGapIdentifier().analyze(tmpdir) undocumented_names = [g.name for g in report.gaps if g.gap_type == GapType.UNDOCUMENTED] assert "_internal" not in undocumented_names, "Private symbols should be skipped" assert "public" in undocumented_names def test_empty_repo(): with tempfile.TemporaryDirectory() as tmpdir: report = KnowledgeGapIdentifier().analyze(tmpdir) assert len(report.gaps) == 0 def test_invalid_path(): report = KnowledgeGapIdentifier().analyze("/nonexistent/path/xyz") assert len(report.gaps) == 1 assert report.gaps[0].severity == GapSeverity.ERROR def test_report_summary(): with tempfile.TemporaryDirectory() as tmpdir: _make_repo(tmpdir, { "src/app.py": "class MyService:\n def handle(self):\n pass\n", "README.md": "# App\n", }) report = KnowledgeGapIdentifier().analyze(tmpdir) summary = report.summary() assert "UNDOCUMENTED" in summary assert "MyService" in summary def test_report_to_dict(): with tempfile.TemporaryDirectory() as tmpdir: _make_repo(tmpdir, { "src/app.py": "def hello():\n pass\n", "README.md": "# App\n", }) report = KnowledgeGapIdentifier().analyze(tmpdir) d = report.to_dict() assert "total_gaps" in d assert "gaps" in d assert isinstance(d["gaps"], list) assert d["total_gaps"] > 0 if __name__ == "__main__": test_undocumented_symbol() test_documented_symbol_no_gap() test_untested_module() test_tested_module_no_gap() test_missing_implementation() test_private_symbols_skipped() test_empty_repo() test_invalid_path() test_report_summary() test_report_to_dict() print("All 10 tests passed.")