"""Integration tests for the complete MkDocs MCP setup."""
import subprocess
import sys
from pathlib import Path
from typing import Generator
import pytest
class TestProjectIntegration:
"""Integration tests for the entire project setup."""
@pytest.fixture
def project_root(self) -> Path:
"""Get the project root directory."""
return Path(__file__).parent.parent
def test_project_structure(self, project_root: Path):
"""Test that all expected project files exist."""
expected_files = [
"pyproject.toml",
"mkdocs-site/mkdocs.yml",
"mkdocs-site/pyproject.toml",
"mcp-server/pyproject.toml",
"mcp-server/src/mkdocs_mcp/__init__.py",
"mcp-server/src/mkdocs_mcp/server.py",
"mcp-server/src/mkdocs_mcp/resources.py",
"mcp-server/src/mkdocs_mcp/tools.py",
"docs/index.md",
".devcontainer/devcontainer.json",
".devcontainer/Dockerfile",
".vscode/settings.json",
]
for expected_file in expected_files:
file_path = project_root / expected_file
assert file_path.exists(), f"Missing expected file: {expected_file}"
def test_mkdocs_config_valid(self, project_root: Path):
"""Test that MkDocs configuration is valid."""
mkdocs_config = project_root / "mkdocs-site" / "mkdocs.yml"
assert mkdocs_config.exists()
# Try to parse the YAML (basic validation)
import yaml
with open(mkdocs_config, 'r', encoding='utf-8') as f:
config = yaml.safe_load(f)
# Check required fields
assert config.get('site_name')
assert config.get('theme')
assert config.get('theme', {}).get('name') == 'material'
assert config.get('nav')
def test_documentation_files_exist(self, project_root: Path):
"""Test that documentation files exist and are readable."""
docs_dir = project_root / "docs"
assert docs_dir.exists()
assert docs_dir.is_dir()
# Check for key documentation files
key_files = [
"index.md",
"getting-started/installation.md",
"getting-started/quick-start.md",
]
for file_path in key_files:
doc_file = docs_dir / file_path
assert doc_file.exists(), f"Missing documentation file: {file_path}"
# Check that file is readable and not empty
content = doc_file.read_text(encoding='utf-8')
assert len(content.strip()) > 0, f"Documentation file is empty: {file_path}"
def test_python_imports(self, project_root: Path):
"""Test that Python modules can be imported."""
# Add the mcp-server src to path for testing
mcp_src = project_root / "mcp-server" / "src"
if str(mcp_src) not in sys.path:
sys.path.insert(0, str(mcp_src))
try:
# Test importing main modules
import mkdocs_mcp
from mkdocs_mcp import server, resources, tools
from mkdocs_mcp.server import MkDocsMCPServer
# Test creating server instance
docs_path = project_root / "docs"
server_instance = MkDocsMCPServer(docs_path)
assert server_instance.docs_path == docs_path
except ImportError as e:
pytest.fail(f"Failed to import MCP modules: {e}")
finally:
# Clean up sys.path
if str(mcp_src) in sys.path:
sys.path.remove(str(mcp_src))
@pytest.mark.slow
def test_uv_dependencies_installable(self, project_root: Path):
"""Test that uv can resolve and install dependencies."""
# This test checks if the dependency specification is valid
# Note: This is a slow test as it actually runs uv
result = subprocess.run(
["uv", "pip", "compile", "pyproject.toml", "--dry-run"],
cwd=project_root,
capture_output=True,
text=True
)
# If uv is available and dependencies are valid, this should succeed
if result.returncode != 0 and "command not found" not in result.stderr:
pytest.fail(f"uv dependency resolution failed: {result.stderr}")
@pytest.mark.slow
def test_mkdocs_build(self, project_root: Path):
"""Test that MkDocs can build the documentation."""
mkdocs_dir = project_root / "mkdocs-site"
try:
result = subprocess.run(
["python", "-m", "mkdocs", "build", "--strict"],
cwd=mkdocs_dir,
capture_output=True,
text=True,
timeout=60
)
if result.returncode != 0:
pytest.fail(f"MkDocs build failed: {result.stderr}")
# Check that site was built
site_dir = mkdocs_dir / "site"
assert site_dir.exists(), "MkDocs site directory not created"
# Check for key generated files
index_html = site_dir / "index.html"
assert index_html.exists(), "index.html not generated"
except subprocess.TimeoutExpired:
pytest.fail("MkDocs build timed out")
except FileNotFoundError:
pytest.skip("MkDocs not available for testing")
def test_devcontainer_config_valid(self, project_root: Path):
"""Test that devcontainer configuration is valid JSON."""
devcontainer_json = project_root / ".devcontainer" / "devcontainer.json"
assert devcontainer_json.exists()
import json
with open(devcontainer_json, 'r', encoding='utf-8') as f:
config = json.load(f)
# Check required fields
assert config.get('name')
assert config.get('build')
assert config.get('build', {}).get('dockerfile')
def test_vscode_config_valid(self, project_root: Path):
"""Test that VSCode configuration files are valid JSON."""
vscode_dir = project_root / ".vscode"
json_files = [
"settings.json",
"extensions.json",
"tasks.json",
"launch.json"
]
import json
for json_file in json_files:
file_path = vscode_dir / json_file
if file_path.exists(): # Some files might be optional
with open(file_path, 'r', encoding='utf-8') as f:
config = json.load(f)
# Just loading without error is sufficient validation
assert isinstance(config, dict)
def test_workspace_structure(self, project_root: Path):
"""Test that the uv workspace is properly configured."""
root_toml = project_root / "pyproject.toml"
assert root_toml.exists()
# Parse TOML to check workspace configuration
try:
import tomllib
except ImportError:
import tomli as tomllib
with open(root_toml, 'rb') as f:
config = tomllib.load(f)
# Check workspace configuration
workspace = config.get('tool', {}).get('uv', {}).get('workspace')
assert workspace is not None
assert 'members' in workspace
members = workspace['members']
assert 'mcp-server' in members
assert 'mkdocs-site' in members
# Verify workspace members exist
for member in members:
member_path = project_root / member
assert member_path.exists(), f"Workspace member not found: {member}"
assert (member_path / "pyproject.toml").exists(), f"Member missing pyproject.toml: {member}"