Files
lmiranda d580464f4a test(contract-validator): add comprehensive tests (#193)
Add 34 tests across 3 test modules:
- test_parse_tools.py: 11 tests for README/CLAUDE.md parsing
- test_validation_tools.py: 11 tests for compatibility and agent validation
- test_report_tools.py: 12 tests for report generation and filtering

Coverage: parse_tools 79%, validation_tools 96%, report_tools 89%

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-26 15:04:26 -05:00

194 lines
5.4 KiB
Python

"""
Unit tests for parse tools.
"""
import pytest
from pathlib import Path
@pytest.fixture
def parse_tools():
"""Create ParseTools instance"""
from mcp_server.parse_tools import ParseTools
return ParseTools()
@pytest.fixture
def sample_readme(tmp_path):
"""Create a sample README.md for testing"""
readme = tmp_path / "README.md"
readme.write_text("""# Test Plugin
A test plugin for validation.
## Features
- **Feature One**: Does something
- **Feature Two**: Does something else
## Commands
| Command | Description |
|---------|-------------|
| `/test-cmd` | Test command |
| `/another-cmd` | Another test command |
## Agents
| Agent | Description |
|-------|-------------|
| `test-agent` | A test agent |
## Tools Summary
### Category A (3 tools)
`tool_a`, `tool_b`, `tool_c`
### Category B (2 tools)
`tool_d`, `tool_e`
""")
return str(tmp_path)
@pytest.fixture
def sample_claude_md(tmp_path):
"""Create a sample CLAUDE.md for testing"""
claude_md = tmp_path / "CLAUDE.md"
claude_md.write_text("""# CLAUDE.md
## Project Overview
### Four-Agent Model (test)
| Agent | Personality | Responsibilities |
|-------|-------------|------------------|
| **Planner** | Thoughtful | Planning via `create_issue`, `search_lessons` |
| **Executor** | Focused | Implementation via `write`, `edit` |
## Workflow
1. Planner creates issues
2. Executor implements code
""")
return str(claude_md)
@pytest.mark.asyncio
async def test_parse_plugin_interface_basic(parse_tools, sample_readme):
"""Test basic plugin interface parsing"""
result = await parse_tools.parse_plugin_interface(sample_readme)
assert "error" not in result
# Plugin name extraction strips "Plugin" suffix
assert result["plugin_name"] == "Test"
assert "A test plugin" in result["description"]
@pytest.mark.asyncio
async def test_parse_plugin_interface_commands(parse_tools, sample_readme):
"""Test command extraction from README"""
result = await parse_tools.parse_plugin_interface(sample_readme)
commands = result["commands"]
assert len(commands) == 2
assert commands[0]["name"] == "/test-cmd"
assert commands[1]["name"] == "/another-cmd"
@pytest.mark.asyncio
async def test_parse_plugin_interface_agents(parse_tools, sample_readme):
"""Test agent extraction from README"""
result = await parse_tools.parse_plugin_interface(sample_readme)
agents = result["agents"]
assert len(agents) == 1
assert agents[0]["name"] == "test-agent"
@pytest.mark.asyncio
async def test_parse_plugin_interface_tools(parse_tools, sample_readme):
"""Test tool extraction from README"""
result = await parse_tools.parse_plugin_interface(sample_readme)
tools = result["tools"]
tool_names = [t["name"] for t in tools]
assert "tool_a" in tool_names
assert "tool_b" in tool_names
assert "tool_e" in tool_names
assert len(tools) >= 5
@pytest.mark.asyncio
async def test_parse_plugin_interface_categories(parse_tools, sample_readme):
"""Test tool category extraction"""
result = await parse_tools.parse_plugin_interface(sample_readme)
categories = result["tool_categories"]
assert "Category A" in categories
assert "Category B" in categories
assert "tool_a" in categories["Category A"]
@pytest.mark.asyncio
async def test_parse_plugin_interface_features(parse_tools, sample_readme):
"""Test feature extraction"""
result = await parse_tools.parse_plugin_interface(sample_readme)
features = result["features"]
assert "Feature One" in features
assert "Feature Two" in features
@pytest.mark.asyncio
async def test_parse_plugin_interface_not_found(parse_tools, tmp_path):
"""Test error when README not found"""
result = await parse_tools.parse_plugin_interface(str(tmp_path / "nonexistent"))
assert "error" in result
assert "not found" in result["error"].lower()
@pytest.mark.asyncio
async def test_parse_claude_md_agents(parse_tools, sample_claude_md):
"""Test agent extraction from CLAUDE.md"""
result = await parse_tools.parse_claude_md_agents(sample_claude_md)
assert "error" not in result
assert result["agent_count"] == 2
agents = result["agents"]
agent_names = [a["name"] for a in agents]
assert "Planner" in agent_names
assert "Executor" in agent_names
@pytest.mark.asyncio
async def test_parse_claude_md_tool_refs(parse_tools, sample_claude_md):
"""Test tool reference extraction from agents"""
result = await parse_tools.parse_claude_md_agents(sample_claude_md)
agents = {a["name"]: a for a in result["agents"]}
planner = agents["Planner"]
assert "create_issue" in planner["tool_refs"]
assert "search_lessons" in planner["tool_refs"]
@pytest.mark.asyncio
async def test_parse_claude_md_not_found(parse_tools, tmp_path):
"""Test error when CLAUDE.md not found"""
result = await parse_tools.parse_claude_md_agents(str(tmp_path / "CLAUDE.md"))
assert "error" in result
assert "not found" in result["error"].lower()
@pytest.mark.asyncio
async def test_parse_plugin_with_direct_file(parse_tools, sample_readme):
"""Test parsing with direct file path instead of directory"""
readme_path = Path(sample_readme) / "README.md"
result = await parse_tools.parse_plugin_interface(str(readme_path))
assert "error" not in result
# Plugin name extraction strips "Plugin" suffix
assert result["plugin_name"] == "Test"