Some checks failed
Test / pytest (pull_request) Failing after 29s
- Introduce scripts/test_documentation_generator.py: scans test files, adds module docstrings (explaining what is tested) and function docstrings (explaining verification purpose) without altering logic. - Applies documentation to 11 previously-undocumented test files: * tests/test_ci_config.py — added module-level docstring * tests/test_dedup.py — 30 function docstrings * tests/test_knowledge_gap_identifier.py — 10 function docstrings * tests/test_perf_bottleneck_finder.py — 25 function docstrings * tests/test_quality_gate.py — 14 function docstrings * scripts/test_diff_analyzer.py — 10 function docstrings * scripts/test_gitea_issue_parser.py — 6 function docstrings * scripts/test_harvest_prompt_comprehensive.py — 5 function docstrings * scripts/test_improvement_proposals.py — 2 function docstrings * scripts/test_knowledge_staleness.py — 8 function docstrings * scripts/test_session_pair_harvester.py — 5 function docstrings - Idempotent: re-running detects all 19 test files as up-to-date. - Processes up to 25 files per run (meets 20+ capacity requirement). Closes #88
152 lines
5.6 KiB
Python
152 lines
5.6 KiB
Python
"""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():
|
|
"""Verifies undocumented symbol logic."""
|
|
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():
|
|
"""Verifies documented symbol no gap logic."""
|
|
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():
|
|
"""Verifies untested module logic."""
|
|
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():
|
|
"""Verifies tested module no gap logic."""
|
|
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():
|
|
"""Verifies missing implementation logic."""
|
|
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():
|
|
"""Verifies private symbols skipped logic."""
|
|
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():
|
|
"""Verifies behavior with empty or None input."""
|
|
with tempfile.TemporaryDirectory() as tmpdir:
|
|
report = KnowledgeGapIdentifier().analyze(tmpdir)
|
|
assert len(report.gaps) == 0
|
|
|
|
|
|
def test_invalid_path():
|
|
"""Verifies invalid path logic."""
|
|
report = KnowledgeGapIdentifier().analyze("/nonexistent/path/xyz")
|
|
assert len(report.gaps) == 1
|
|
assert report.gaps[0].severity == GapSeverity.ERROR
|
|
|
|
|
|
def test_report_summary():
|
|
"""Verifies report summary logic."""
|
|
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():
|
|
"""Verifies report to dict logic."""
|
|
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.")
|