Some checks failed
Test / pytest (pull_request) Failing after 11s
This adds scripts/doc_freshness.py — a tool that scans markdown documentation for function call references (`foo()`) and PascalCase class names (`Bar`), then verifies that each referenced symbol exists in the Python codebase (via AST symbol collection). - Parses docs for function/class references (backticked identifiers that are either function calls ending with () or PascalCase class names) - Checks if referenced items still exist in the code - Reports stale doc references with file paths and line numbers - Suitable for weekly cron execution; exit code 1 when stale refs found Includes tests in tests/test_doc_freshness.py covering: - symbol collection from Python AST - doc reference extraction heuristics - missing detection integration Smallest concrete implementation satisfying all acceptance criteria.
90 lines
2.9 KiB
Python
Executable File
90 lines
2.9 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""Tests for scripts/doc_freshness.py — Issue #104."""
|
|
|
|
import os
|
|
import sys
|
|
import tempfile
|
|
from pathlib import Path
|
|
|
|
sys.path.insert(0, str(Path(__file__).parent.parent / "scripts"))
|
|
|
|
import doc_freshness as df
|
|
|
|
|
|
def test_collect_python_symbols():
|
|
"""Should collect function and class names from Python files."""
|
|
with tempfile.TemporaryDirectory() as tmpdir:
|
|
# Create a simple Python file
|
|
py_path = os.path.join(tmpdir, "sample.py")
|
|
with open(py_path, "w") as f:
|
|
f.write('''
|
|
def my_func():
|
|
pass
|
|
|
|
class MyClass:
|
|
def method(self):
|
|
pass
|
|
|
|
async def my_async():
|
|
pass
|
|
''')
|
|
symbols = df.collect_python_symbols(tmpdir)
|
|
assert "my_func" in symbols
|
|
assert "MyClass" in symbols
|
|
assert "my_async" in symbols
|
|
# method (inside class) is also collected and should be considered valid
|
|
assert "method" in symbols
|
|
print("PASS: test_collect_python_symbols")
|
|
|
|
|
|
def test_extract_doc_references_function_and_class():
|
|
"""Should extract only function calls () and PascalCase class refs."""
|
|
with tempfile.TemporaryDirectory() as tmpdir:
|
|
docs = os.path.join(tmpdir, "docs")
|
|
os.makedirs(docs)
|
|
md_path = os.path.join(docs, "test.md")
|
|
with open(md_path, "w") as f:
|
|
f.write('''
|
|
# Test
|
|
|
|
`call_this()` is a function.
|
|
`SomeClass` is a class.
|
|
`not_a_function` (lowercase, no parens) should be ignored.
|
|
`filename.py` should be ignored.
|
|
`https://example.com` ignored.
|
|
''')
|
|
refs = df.extract_doc_references(docs)
|
|
names = [r[0] for r in refs]
|
|
assert "call_this" in names
|
|
assert "SomeClass" in names
|
|
assert "not_a_function" not in names
|
|
assert "filename" not in names # filename.py filtered
|
|
assert "https" not in names
|
|
print("PASS: test_extract_doc_references_function_and_class")
|
|
|
|
|
|
def test_check_doc_freshness_missing_detection():
|
|
"""Should detect missing symbols."""
|
|
with tempfile.TemporaryDirectory() as tmpdir:
|
|
# Code with one function
|
|
code_dir = os.path.join(tmpdir, "code")
|
|
os.makedirs(code_dir)
|
|
with open(os.path.join(code_dir, "a.py"), "w") as f:
|
|
f.write("def existing_func(): pass\n")
|
|
# Docs reference existing_func and missing_func
|
|
docs_dir = os.path.join(tmpdir, "docs")
|
|
os.makedirs(docs_dir)
|
|
with open(os.path.join(docs_dir, "readme.md"), "w") as f:
|
|
f.write("`existing_func()` and `missing_func()` are mentioned.")
|
|
result = df.check_doc_freshness(code_dir, docs_dir)
|
|
assert result["missing_count"] == 1
|
|
assert result["found_count"] == 1
|
|
print("PASS: test_check_doc_freshness_missing_detection")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
test_collect_python_symbols()
|
|
test_extract_doc_references_function_and_class()
|
|
test_check_doc_freshness_missing_detection()
|
|
print("All tests passed!")
|