This commit is contained in:
Pablo Estevez
2026-01-17 17:29:21 +00:00
parent c89f059712
commit 5ed767ff9a
144 changed files with 14142 additions and 16488 deletions

View File

@@ -10,15 +10,15 @@ Test Coverage:
- Integration with code analysis results
"""
import unittest
import tempfile
import shutil
from pathlib import Path
import sys
import os
import shutil
import sys
import tempfile
import unittest
from pathlib import Path
# Add src to path
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', 'src'))
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "src"))
from skill_seekers.cli.api_reference_builder import APIReferenceBuilder
@@ -38,28 +38,34 @@ class TestAPIReferenceBuilder(unittest.TestCase):
def test_class_formatting(self):
"""Test markdown formatting for class signatures."""
code_analysis = {
'files': [{
'file': 'test.py',
'language': 'Python',
'classes': [{
'name': 'Calculator',
'docstring': 'A simple calculator class.',
'base_classes': ['object'],
'methods': [{
'name': 'add',
'parameters': [
{'name': 'a', 'type_hint': 'int', 'default': None},
{'name': 'b', 'type_hint': 'int', 'default': None}
],
'return_type': 'int',
'docstring': 'Add two numbers.',
'is_async': False,
'is_method': True,
'decorators': []
}]
}],
'functions': []
}]
"files": [
{
"file": "test.py",
"language": "Python",
"classes": [
{
"name": "Calculator",
"docstring": "A simple calculator class.",
"base_classes": ["object"],
"methods": [
{
"name": "add",
"parameters": [
{"name": "a", "type_hint": "int", "default": None},
{"name": "b", "type_hint": "int", "default": None},
],
"return_type": "int",
"docstring": "Add two numbers.",
"is_async": False,
"is_method": True,
"decorators": [],
}
],
}
],
"functions": [],
}
]
}
builder = APIReferenceBuilder(code_analysis)
@@ -72,31 +78,33 @@ class TestAPIReferenceBuilder(unittest.TestCase):
# Verify content
content = output_file.read_text()
self.assertIn('### Calculator', content)
self.assertIn('A simple calculator class', content)
self.assertIn('**Inherits from**: object', content)
self.assertIn('##### add', content)
self.assertIn('Add two numbers', content)
self.assertIn("### Calculator", content)
self.assertIn("A simple calculator class", content)
self.assertIn("**Inherits from**: object", content)
self.assertIn("##### add", content)
self.assertIn("Add two numbers", content)
def test_function_formatting(self):
"""Test markdown formatting for function signatures."""
code_analysis = {
'files': [{
'file': 'utils.py',
'language': 'Python',
'classes': [],
'functions': [{
'name': 'calculate_sum',
'parameters': [
{'name': 'numbers', 'type_hint': 'list', 'default': None}
"files": [
{
"file": "utils.py",
"language": "Python",
"classes": [],
"functions": [
{
"name": "calculate_sum",
"parameters": [{"name": "numbers", "type_hint": "list", "default": None}],
"return_type": "int",
"docstring": "Calculate sum of numbers.",
"is_async": False,
"is_method": False,
"decorators": [],
}
],
'return_type': 'int',
'docstring': 'Calculate sum of numbers.',
'is_async': False,
'is_method': False,
'decorators': []
}]
}]
}
]
}
builder = APIReferenceBuilder(code_analysis)
@@ -106,32 +114,36 @@ class TestAPIReferenceBuilder(unittest.TestCase):
output_file = list(generated.values())[0]
content = output_file.read_text()
self.assertIn('## Functions', content)
self.assertIn('### calculate_sum', content)
self.assertIn('Calculate sum of numbers', content)
self.assertIn('**Returns**: `int`', content)
self.assertIn("## Functions", content)
self.assertIn("### calculate_sum", content)
self.assertIn("Calculate sum of numbers", content)
self.assertIn("**Returns**: `int`", content)
def test_parameter_table_generation(self):
"""Test parameter table formatting."""
code_analysis = {
'files': [{
'file': 'test.py',
'language': 'Python',
'classes': [],
'functions': [{
'name': 'create_user',
'parameters': [
{'name': 'name', 'type_hint': 'str', 'default': None},
{'name': 'age', 'type_hint': 'int', 'default': '18'},
{'name': 'active', 'type_hint': 'bool', 'default': 'True'}
"files": [
{
"file": "test.py",
"language": "Python",
"classes": [],
"functions": [
{
"name": "create_user",
"parameters": [
{"name": "name", "type_hint": "str", "default": None},
{"name": "age", "type_hint": "int", "default": "18"},
{"name": "active", "type_hint": "bool", "default": "True"},
],
"return_type": "dict",
"docstring": "Create a user object.",
"is_async": False,
"is_method": False,
"decorators": [],
}
],
'return_type': 'dict',
'docstring': 'Create a user object.',
'is_async': False,
'is_method': False,
'decorators': []
}]
}]
}
]
}
builder = APIReferenceBuilder(code_analysis)
@@ -141,34 +153,33 @@ class TestAPIReferenceBuilder(unittest.TestCase):
output_file = list(generated.values())[0]
content = output_file.read_text()
self.assertIn('**Parameters**:', content)
self.assertIn('| Name | Type | Default | Description |', content)
self.assertIn('| name | str | - |', content) # Parameters with no default show "-"
self.assertIn('| age | int | 18 |', content)
self.assertIn('| active | bool | True |', content)
self.assertIn("**Parameters**:", content)
self.assertIn("| Name | Type | Default | Description |", content)
self.assertIn("| name | str | - |", content) # Parameters with no default show "-"
self.assertIn("| age | int | 18 |", content)
self.assertIn("| active | bool | True |", content)
def test_markdown_output_structure(self):
"""Test overall markdown document structure."""
code_analysis = {
'files': [{
'file': 'module.py',
'language': 'Python',
'classes': [{
'name': 'TestClass',
'docstring': 'Test class.',
'base_classes': [],
'methods': []
}],
'functions': [{
'name': 'test_func',
'parameters': [],
'return_type': None,
'docstring': 'Test function.',
'is_async': False,
'is_method': False,
'decorators': []
}]
}]
"files": [
{
"file": "module.py",
"language": "Python",
"classes": [{"name": "TestClass", "docstring": "Test class.", "base_classes": [], "methods": []}],
"functions": [
{
"name": "test_func",
"parameters": [],
"return_type": None,
"docstring": "Test function.",
"is_async": False,
"is_method": False,
"decorators": [],
}
],
}
]
}
builder = APIReferenceBuilder(code_analysis)
@@ -179,13 +190,13 @@ class TestAPIReferenceBuilder(unittest.TestCase):
content = output_file.read_text()
# Check header
self.assertIn('# API Reference: module.py', content)
self.assertIn('**Language**: Python', content)
self.assertIn('**Source**: `module.py`', content)
self.assertIn("# API Reference: module.py", content)
self.assertIn("**Language**: Python", content)
self.assertIn("**Source**: `module.py`", content)
# Check sections in order
classes_pos = content.find('## Classes')
functions_pos = content.find('## Functions')
classes_pos = content.find("## Classes")
functions_pos = content.find("## Functions")
self.assertNotEqual(classes_pos, -1)
self.assertNotEqual(functions_pos, -1)
@@ -195,48 +206,50 @@ class TestAPIReferenceBuilder(unittest.TestCase):
"""Test integration with actual code analyzer output format."""
# Simulate real code analyzer output
code_analysis = {
'files': [
"files": [
{
'file': 'calculator.py',
'language': 'Python',
'classes': [{
'name': 'Calculator',
'base_classes': [],
'methods': [
{
'name': 'add',
'parameters': [
{'name': 'a', 'type_hint': 'float', 'default': None},
{'name': 'b', 'type_hint': 'float', 'default': None}
],
'return_type': 'float',
'docstring': 'Add two numbers.',
'decorators': [],
'is_async': False,
'is_method': True
}
],
'docstring': 'Calculator class.',
'line_number': 1
}],
'functions': []
"file": "calculator.py",
"language": "Python",
"classes": [
{
"name": "Calculator",
"base_classes": [],
"methods": [
{
"name": "add",
"parameters": [
{"name": "a", "type_hint": "float", "default": None},
{"name": "b", "type_hint": "float", "default": None},
],
"return_type": "float",
"docstring": "Add two numbers.",
"decorators": [],
"is_async": False,
"is_method": True,
}
],
"docstring": "Calculator class.",
"line_number": 1,
}
],
"functions": [],
},
{
'file': 'utils.js',
'language': 'JavaScript',
'classes': [],
'functions': [{
'name': 'formatDate',
'parameters': [
{'name': 'date', 'type_hint': None, 'default': None}
],
'return_type': None,
'docstring': None,
'is_async': False,
'is_method': False,
'decorators': []
}]
}
"file": "utils.js",
"language": "JavaScript",
"classes": [],
"functions": [
{
"name": "formatDate",
"parameters": [{"name": "date", "type_hint": None, "default": None}],
"return_type": None,
"docstring": None,
"is_async": False,
"is_method": False,
"decorators": [],
}
],
},
]
}
@@ -248,40 +261,42 @@ class TestAPIReferenceBuilder(unittest.TestCase):
# Verify filenames
filenames = [f.name for f in generated.values()]
self.assertIn('calculator.md', filenames)
self.assertIn('utils.md', filenames)
self.assertIn("calculator.md", filenames)
self.assertIn("utils.md", filenames)
# Verify Python file content
py_file = next(f for f in generated.values() if f.name == 'calculator.md')
py_file = next(f for f in generated.values() if f.name == "calculator.md")
py_content = py_file.read_text()
self.assertIn('Calculator class', py_content)
self.assertIn('add(a: float, b: float) → float', py_content)
self.assertIn("Calculator class", py_content)
self.assertIn("add(a: float, b: float) → float", py_content)
# Verify JavaScript file content
js_file = next(f for f in generated.values() if f.name == 'utils.md')
js_file = next(f for f in generated.values() if f.name == "utils.md")
js_content = js_file.read_text()
self.assertIn('formatDate', js_content)
self.assertIn('**Language**: JavaScript', js_content)
self.assertIn("formatDate", js_content)
self.assertIn("**Language**: JavaScript", js_content)
def test_async_function_indicator(self):
"""Test that async functions are marked in output."""
code_analysis = {
'files': [{
'file': 'async_utils.py',
'language': 'Python',
'classes': [],
'functions': [{
'name': 'fetch_data',
'parameters': [
{'name': 'url', 'type_hint': 'str', 'default': None}
"files": [
{
"file": "async_utils.py",
"language": "Python",
"classes": [],
"functions": [
{
"name": "fetch_data",
"parameters": [{"name": "url", "type_hint": "str", "default": None}],
"return_type": "dict",
"docstring": "Fetch data from URL.",
"is_async": True,
"is_method": False,
"decorators": [],
}
],
'return_type': 'dict',
'docstring': 'Fetch data from URL.',
'is_async': True,
'is_method': False,
'decorators': []
}]
}]
}
]
}
builder = APIReferenceBuilder(code_analysis)
@@ -291,33 +306,30 @@ class TestAPIReferenceBuilder(unittest.TestCase):
output_file = list(generated.values())[0]
content = output_file.read_text()
self.assertIn('**Async function**', content)
self.assertIn('fetch_data', content)
self.assertIn("**Async function**", content)
self.assertIn("fetch_data", content)
def test_empty_analysis_skipped(self):
"""Test that files with no analysis are skipped."""
code_analysis = {
'files': [
"files": [
{"file": "empty.py", "language": "Python", "classes": [], "functions": []},
{
'file': 'empty.py',
'language': 'Python',
'classes': [],
'functions': []
"file": "valid.py",
"language": "Python",
"classes": [],
"functions": [
{
"name": "test",
"parameters": [],
"return_type": None,
"docstring": None,
"is_async": False,
"is_method": False,
"decorators": [],
}
],
},
{
'file': 'valid.py',
'language': 'Python',
'classes': [],
'functions': [{
'name': 'test',
'parameters': [],
'return_type': None,
'docstring': None,
'is_async': False,
'is_method': False,
'decorators': []
}]
}
]
}
@@ -326,9 +338,9 @@ class TestAPIReferenceBuilder(unittest.TestCase):
# Only valid.py should be generated
self.assertEqual(len(generated), 1)
self.assertIn('valid.py', list(generated.keys())[0])
self.assertIn("valid.py", list(generated.keys())[0])
if __name__ == '__main__':
if __name__ == "__main__":
# Run tests with verbose output
unittest.main(verbosity=2)