"""Integration tests for the MCP server."""
import pytest
from src.mcp_impl.server import analyze_code_structure
class TestMCPServerIntegration:
"""Integration tests for MCP server."""
@pytest.mark.asyncio
async def test_analyze_python_file(self, tmp_path) -> None:
"""Test analyzing a Python file."""
# Create a test Python file
test_file = tmp_path / "test.py"
test_file.write_text(
'''"""Test module."""
def hello(name: str) -> str:
"""Say hello."""
return f"Hello, {name}!"
class Greeter:
"""A greeter class."""
def __init__(self, greeting: str = "Hello"):
"""Initialize the greeter."""
self.greeting = greeting
def greet(self, name: str) -> str:
"""Greet someone."""
return f"{self.greeting}, {name}!"
'''
)
result = await analyze_code_structure(str(test_file))
assert len(result) == 1
markdown = result[0].text
assert "# Code Structure:" in markdown
assert "**Language**: Python" in markdown
assert "**Total Classes**: 1" in markdown
assert "**Total Functions**: 3" in markdown
assert "### `hello`" in markdown
assert "### `Greeter`" in markdown
assert "**Parameters**: `name: str`" in markdown
assert "**Return Type**: `str`" in markdown
@pytest.mark.asyncio
async def test_analyze_with_docstrings(self, tmp_path) -> None:
"""Test analyzing with docstrings enabled."""
test_file = tmp_path / "test.py"
test_file.write_text(
'''"""Test module."""
def hello(name: str) -> str:
"""Say hello."""
return f"Hello, {name}!"
'''
)
result = await analyze_code_structure(str(test_file), include_docstrings=True)
markdown = result[0].text
assert "**Docstring**: Say hello." in markdown
@pytest.mark.asyncio
async def test_analyze_nonexistent_file(self, tmp_path) -> None:
"""Test analyzing a non-existent file."""
result = await analyze_code_structure(str(tmp_path / "nonexistent.py"))
markdown = result[0].text
assert "# Error" in markdown
assert "File not found" in markdown
@pytest.mark.asyncio
async def test_analyze_unsupported_file(self, tmp_path) -> None:
"""Test analyzing an unsupported file type."""
test_file = tmp_path / "test.xyz"
test_file.write_text("some content")
result = await analyze_code_structure(str(test_file))
markdown = result[0].text
assert "# Error" in markdown
assert "Unsupported file type" in markdown
@pytest.mark.asyncio
async def test_analyze_python_with_syntax_error(self, tmp_path) -> None:
"""Test analyzing a Python file with syntax errors."""
test_file = tmp_path / "test.py"
test_file.write_text(
'''"""Test module."""
def hello(name: str) -> str:
"""Say hello."""
return f"Hello, {name}!
class Greeter:
"""A greeter class."""
'''
)
result = await analyze_code_structure(str(test_file))
markdown = result[0].text
assert "# Code Structure:" in markdown
# Should still parse what it can
assert "## Parse Errors" in markdown
@pytest.mark.asyncio
async def test_analyze_nested_functions(self, tmp_path) -> None:
"""Test analyzing nested functions."""
test_file = tmp_path / "test.py"
test_file.write_text(
'''"""Test module."""
def outer():
"""Outer function."""
def inner():
"""Inner function."""
return "inner"
return inner()
'''
)
result = await analyze_code_structure(str(test_file))
markdown = result[0].text
assert "### `outer`" in markdown
assert "### `inner`" in markdown
assert "**Nested Functions**:" in markdown
assert "Parent: `outer`" in markdown
class TestMultiFileSupport:
"""Tests for multi-file analysis support."""
@pytest.mark.asyncio
async def test_analyze_multiple_files(self, tmp_path) -> None:
"""Test analyzing multiple files."""
# Create first test file
test_file1 = tmp_path / "test1.py"
test_file1.write_text(
'''"""Test module 1."""
def hello(name: str) -> str:
"""Say hello."""
return f"Hello, {name}!"
'''
)
# Create second test file
test_file2 = tmp_path / "test2.py"
test_file2.write_text(
'''"""Test module 2."""
class Greeter:
"""A greeter class."""
def greet(self, name: str) -> str:
"""Greet someone."""
return f"Hello, {name}!"
'''
)
result = await analyze_code_structure([str(test_file1), str(test_file2)])
assert len(result) == 1
markdown = result[0].text
assert "# Code Structure Analysis" in markdown
assert "**Files Analyzed**: 2" in markdown
assert "**Successful**: 2" in markdown
assert "**Failed**: 0" in markdown
assert "---" in markdown # Separator between files
assert "### `hello`" in markdown
assert "### `Greeter`" in markdown
@pytest.mark.asyncio
async def test_analyze_mixed_success_failure(self, tmp_path) -> None:
"""Test analyzing files with mixed success/failure."""
# Create valid file
valid_file = tmp_path / "valid.py"
valid_file.write_text(
'''"""Valid module."""
def test_func():
"""Test function."""
pass
'''
)
# Create non-existent file path
nonexistent_file = tmp_path / "nonexistent.py"
result = await analyze_code_structure([str(valid_file), str(nonexistent_file)])
assert len(result) == 1
markdown = result[0].text
assert "# Code Structure Analysis" in markdown
assert "**Files Analyzed**: 2" in markdown
assert "**Successful**: 1" in markdown
assert "**Failed**: 1" in markdown
assert "### `test_func`" in markdown
assert "File not found" in markdown
@pytest.mark.asyncio
async def test_analyze_single_file_backward_compat(self, tmp_path) -> None:
"""Test backward compatibility with single file string input."""
test_file = tmp_path / "test.py"
test_file.write_text(
'''"""Test module."""
def hello(name: str) -> str:
"""Say hello."""
return f"Hello, {name}!"
'''
)
result = await analyze_code_structure(str(test_file))
# Should behave exactly as before
assert len(result) == 1
markdown = result[0].text
assert "# Code Structure:" in markdown # Single file format
assert "**Language**: Python" in markdown
assert "### `hello`" in markdown
# Should NOT have multi-file summary header
assert "# Code Structure Analysis" not in markdown
@pytest.mark.asyncio
async def test_analyze_multiple_files_with_docstrings(self, tmp_path) -> None:
"""Test analyzing multiple files with docstrings enabled."""
test_file1 = tmp_path / "test1.py"
test_file1.write_text(
'''"""Test module 1."""
def hello(name: str) -> str:
"""Say hello."""
return f"Hello, {name}!"
'''
)
test_file2 = tmp_path / "test2.py"
test_file2.write_text(
'''"""Test module 2."""
def goodbye(name: str) -> str:
"""Say goodbye."""
return f"Goodbye, {name}!"
'''
)
result = await analyze_code_structure(
[str(test_file1), str(test_file2)], include_docstrings=True
)
markdown = result[0].text
assert "**Docstring**: Say hello." in markdown
assert "**Docstring**: Say goodbye." in markdown