diff --git a/analyze_test_issues.py b/analyze_test_issues.py deleted file mode 100644 index 897dfca..0000000 --- a/analyze_test_issues.py +++ /dev/null @@ -1,695 +0,0 @@ -#!/usr/bin/env python3 -""" -Script to analyze and identify potential test issues in the vultr-dns-mcp repository. -""" - -import sys -import subprocess -import os -from pathlib import Path - -def analyze_test_structure(): - """Analyze the test structure and identify potential issues.""" - - print("=== Vultr DNS MCP Test Analysis ===\n") - - issues_found = [] - fixes_needed = [] - - # Common issues in MCP test suites - print("๐Ÿ” Analyzing potential test issues...\n") - - # Issue 1: Import path problems - print("1. Import Path Issues:") - print(" - Tests may have incorrect import paths for the vultr_dns_mcp module") - print(" - Solution: Fix import statements to use correct package structure") - issues_found.append("Import path issues") - fixes_needed.append("Fix import statements in test files") - - # Issue 2: Async/await patterns - print("\n2. Async/Await Pattern Issues:") - print(" - Tests use @pytest.mark.asyncio but may have incorrect async patterns") - print(" - FastMCP Client context manager usage might be incorrect") - print(" - Solution: Ensure proper async/await patterns and context management") - issues_found.append("Async/await pattern issues") - fixes_needed.append("Fix async patterns and FastMCP Client usage") - - # Issue 3: Mock configuration - print("\n3. Mock Configuration Issues:") - print(" - Mock setup in conftest.py may not match actual API structure") - print(" - Patch decorators might target wrong import paths") - print(" - Solution: Update mock configurations to match current API") - issues_found.append("Mock configuration issues") - fixes_needed.append("Update mock configurations") - - # Issue 4: Dependency versions - print("\n4. Dependency Version Issues:") - print(" - FastMCP version compatibility issues") - print(" - Pytest-asyncio version compatibility") - print(" - Solution: Update dependency versions in pyproject.toml") - issues_found.append("Dependency version issues") - fixes_needed.append("Update dependency versions") - - # Issue 5: Test data structure - print("\n5. Test Data Structure Issues:") - print(" - Sample data in fixtures may not match current API response format") - print(" - Solution: Update test data to match current Vultr API structure") - issues_found.append("Test data structure issues") - fixes_needed.append("Update test data structures") - - return issues_found, fixes_needed - -def create_fix_script(): - """Create a comprehensive fix script for the test issues.""" - - fix_script = '''#!/usr/bin/env python3 -""" -Comprehensive test fix script for vultr-dns-mcp repository. -This script addresses common test failures and updates the test suite. -""" - -import os -import re -from pathlib import Path - -def fix_import_statements(): - """Fix import statements in test files.""" - print("๐Ÿ”ง Fixing import statements...") - - # Common import fixes needed - import_fixes = { - # Fix relative imports - r"from vultr_dns_mcp\.server import": "from vultr_dns_mcp.server import", - r"from vultr_dns_mcp\.client import": "from vultr_dns_mcp.client import", - # Fix FastMCP imports - r"from fastmcp import Client": "from fastmcp import Client", - # Add missing imports - r"import pytest": "import pytest\\nimport asyncio", - } - - return import_fixes - -def fix_async_patterns(): - """Fix async/await patterns in tests.""" - print("๐Ÿ”ง Fixing async patterns...") - - async_fixes = { - # Fix FastMCP client usage - r"async with Client\(([^)]+)\) as client:": r"async with Client(\\1) as client:", - # Fix pytest.mark.asyncio usage - r"@pytest\.mark\.asyncio": "@pytest.mark.asyncio\\nasync def", - } - - return async_fixes - -def create_updated_conftest(): - """Create an updated conftest.py file.""" - - conftest_content = '''"""Configuration for pytest tests.""" - -import os -import pytest -from unittest.mock import AsyncMock, MagicMock, patch -from vultr_dns_mcp.server import create_mcp_server - - -@pytest.fixture -def mock_api_key(): - """Provide a mock API key for testing.""" - return "test-api-key-123456789" - - -@pytest.fixture -def mcp_server(mock_api_key): - """Create a FastMCP server instance for testing.""" - return create_mcp_server(mock_api_key) - - -@pytest.fixture -def mock_vultr_client(): - """Create a mock VultrDNSServer for testing API interactions.""" - from vultr_dns_mcp.server import VultrDNSServer - - mock_client = AsyncMock(spec=VultrDNSServer) - - # Configure common mock responses - mock_client.list_domains.return_value = [ - { - "domain": "example.com", - "date_created": "2024-01-01T00:00:00Z", - "dns_sec": "disabled" - }, - { - "domain": "test.com", - "date_created": "2024-01-02T00:00:00Z", - "dns_sec": "enabled" - } - ] - - mock_client.get_domain.return_value = { - "domain": "example.com", - "date_created": "2024-01-01T00:00:00Z", - "dns_sec": "disabled" - } - - mock_client.list_records.return_value = [ - { - "id": "record-123", - "type": "A", - "name": "@", - "data": "192.168.1.100", - "ttl": 300, - "priority": None - }, - { - "id": "record-456", - "type": "MX", - "name": "@", - "data": "mail.example.com", - "ttl": 300, - "priority": 10 - } - ] - - mock_client.create_record.return_value = { - "id": "new-record-789", - "type": "A", - "name": "www", - "data": "192.168.1.100", - "ttl": 300 - } - - mock_client.create_domain.return_value = { - "domain": "newdomain.com", - "date_created": "2024-12-20T00:00:00Z" - } - - return mock_client - - -@pytest.fixture(autouse=True) -def mock_env_api_key(monkeypatch, mock_api_key): - """Automatically set the API key environment variable for all tests.""" - monkeypatch.setenv("VULTR_API_KEY", mock_api_key) - - -@pytest.fixture -def sample_domain_data(): - """Sample domain data for testing.""" - return { - "domain": "example.com", - "date_created": "2024-01-01T00:00:00Z", - "dns_sec": "disabled" - } - - -@pytest.fixture -def sample_record_data(): - """Sample DNS record data for testing.""" - return { - "id": "record-123", - "type": "A", - "name": "www", - "data": "192.168.1.100", - "ttl": 300, - "priority": None - } - - -@pytest.fixture -def sample_records(): - """Sample list of DNS records for testing.""" - return [ - { - "id": "record-123", - "type": "A", - "name": "@", - "data": "192.168.1.100", - "ttl": 300 - }, - { - "id": "record-456", - "type": "A", - "name": "www", - "data": "192.168.1.100", - "ttl": 300 - }, - { - "id": "record-789", - "type": "MX", - "name": "@", - "data": "mail.example.com", - "ttl": 300, - "priority": 10 - }, - { - "id": "record-999", - "type": "TXT", - "name": "@", - "data": "v=spf1 include:_spf.google.com ~all", - "ttl": 300 - } - ] - - -# Configure pytest markers -def pytest_configure(config): - """Configure custom pytest markers.""" - config.addinivalue_line( - "markers", "unit: mark test as a unit test" - ) - config.addinivalue_line( - "markers", "integration: mark test as an integration test" - ) - config.addinivalue_line( - "markers", "slow: mark test as slow running" - ) - config.addinivalue_line( - "markers", "mcp: mark test as MCP-specific" - ) -''' - - return conftest_content - -def create_updated_test_mcp_server(): - """Create an updated test_mcp_server.py file.""" - - test_content = '''"""Tests for MCP server functionality using FastMCP testing patterns.""" - -import pytest -from unittest.mock import patch, AsyncMock -from fastmcp import Client -from vultr_dns_mcp.server import VultrDNSServer, create_mcp_server - - -class TestMCPServerBasics: - """Test basic MCP server functionality.""" - - def test_server_creation(self, mock_api_key): - """Test that MCP server can be created successfully.""" - server = create_mcp_server(mock_api_key) - assert server is not None - assert hasattr(server, '_tools') - assert hasattr(server, '_resources') - - def test_server_creation_without_api_key(self): - """Test that server creation fails without API key.""" - with pytest.raises(ValueError, match="VULTR_API_KEY must be provided"): - create_mcp_server(None) - - @patch.dict('os.environ', {'VULTR_API_KEY': 'env-test-key'}) - def test_server_creation_from_env(self): - """Test server creation using environment variable.""" - server = create_mcp_server() - assert server is not None - - -@pytest.mark.mcp -class TestMCPTools: - """Test MCP tools through in-memory client connection.""" - - @pytest.mark.asyncio - async def test_list_dns_domains_tool(self, mcp_server, mock_vultr_client): - """Test the list_dns_domains MCP tool.""" - with patch('vultr_dns_mcp.server.VultrDNSServer', return_value=mock_vultr_client): - server = create_mcp_server("test-api-key") - - async with Client(server) as client: - result = await client.call_tool("list_dns_domains", {}) - - assert result is not None - assert isinstance(result, list) - # The result should contain the mock data - if len(result) > 0: - # Check if we got the mock data - mock_vultr_client.list_domains.assert_called_once() - - @pytest.mark.asyncio - async def test_get_dns_domain_tool(self, mcp_server, mock_vultr_client): - """Test the get_dns_domain MCP tool.""" - with patch('vultr_dns_mcp.server.VultrDNSServer', return_value=mock_vultr_client): - server = create_mcp_server("test-api-key") - - async with Client(server) as client: - result = await client.call_tool("get_dns_domain", {"domain": "example.com"}) - - assert result is not None - mock_vultr_client.get_domain.assert_called_once_with("example.com") - - @pytest.mark.asyncio - async def test_create_dns_domain_tool(self, mcp_server, mock_vultr_client): - """Test the create_dns_domain MCP tool.""" - with patch('vultr_dns_mcp.server.VultrDNSServer', return_value=mock_vultr_client): - server = create_mcp_server("test-api-key") - - async with Client(server) as client: - result = await client.call_tool("create_dns_domain", { - "domain": "newdomain.com", - "ip": "192.168.1.100" - }) - - assert result is not None - mock_vultr_client.create_domain.assert_called_once_with("newdomain.com", "192.168.1.100") - - @pytest.mark.asyncio - async def test_delete_dns_domain_tool(self, mcp_server, mock_vultr_client): - """Test the delete_dns_domain MCP tool.""" - with patch('vultr_dns_mcp.server.VultrDNSServer', return_value=mock_vultr_client): - server = create_mcp_server("test-api-key") - - async with Client(server) as client: - result = await client.call_tool("delete_dns_domain", {"domain": "example.com"}) - - assert result is not None - mock_vultr_client.delete_domain.assert_called_once_with("example.com") - - @pytest.mark.asyncio - async def test_list_dns_records_tool(self, mcp_server, mock_vultr_client): - """Test the list_dns_records MCP tool.""" - with patch('vultr_dns_mcp.server.VultrDNSServer', return_value=mock_vultr_client): - server = create_mcp_server("test-api-key") - - async with Client(server) as client: - result = await client.call_tool("list_dns_records", {"domain": "example.com"}) - - assert result is not None - mock_vultr_client.list_records.assert_called_once_with("example.com") - - @pytest.mark.asyncio - async def test_create_dns_record_tool(self, mcp_server, mock_vultr_client): - """Test the create_dns_record MCP tool.""" - with patch('vultr_dns_mcp.server.VultrDNSServer', return_value=mock_vultr_client): - server = create_mcp_server("test-api-key") - - async with Client(server) as client: - result = await client.call_tool("create_dns_record", { - "domain": "example.com", - "record_type": "A", - "name": "www", - "data": "192.168.1.100", - "ttl": 300 - }) - - assert result is not None - mock_vultr_client.create_record.assert_called_once_with( - "example.com", "A", "www", "192.168.1.100", 300, None - ) - - @pytest.mark.asyncio - async def test_validate_dns_record_tool(self, mcp_server): - """Test the validate_dns_record MCP tool.""" - async with Client(mcp_server) as client: - # Test valid A record - result = await client.call_tool("validate_dns_record", { - "record_type": "A", - "name": "www", - "data": "192.168.1.100", - "ttl": 300 - }) - - assert result is not None - # The validation should pass for a valid A record - - @pytest.mark.asyncio - async def test_validate_dns_record_invalid(self, mcp_server): - """Test the validate_dns_record tool with invalid data.""" - async with Client(mcp_server) as client: - # Test invalid A record (bad IP) - result = await client.call_tool("validate_dns_record", { - "record_type": "A", - "name": "www", - "data": "invalid-ip-address" - }) - - assert result is not None - # Should detect the invalid IP address - - @pytest.mark.asyncio - async def test_analyze_dns_records_tool(self, mcp_server, mock_vultr_client): - """Test the analyze_dns_records MCP tool.""" - with patch('vultr_dns_mcp.server.VultrDNSServer', return_value=mock_vultr_client): - server = create_mcp_server("test-api-key") - - async with Client(server) as client: - result = await client.call_tool("analyze_dns_records", {"domain": "example.com"}) - - assert result is not None - mock_vultr_client.list_records.assert_called_once_with("example.com") - - -@pytest.mark.mcp -class TestMCPResources: - """Test MCP resources through in-memory client connection.""" - - @pytest.mark.asyncio - async def test_domains_resource(self, mcp_server, mock_vultr_client): - """Test the vultr://domains resource.""" - with patch('vultr_dns_mcp.server.VultrDNSServer', return_value=mock_vultr_client): - server = create_mcp_server("test-api-key") - - async with Client(server) as client: - # Get available resources - resources = await client.list_resources() - - # Check that domains resource is available - resource_uris = [r.uri for r in resources] - assert "vultr://domains" in resource_uris - - @pytest.mark.asyncio - async def test_capabilities_resource(self, mcp_server): - """Test the vultr://capabilities resource.""" - async with Client(mcp_server) as client: - resources = await client.list_resources() - resource_uris = [r.uri for r in resources] - assert "vultr://capabilities" in resource_uris - - @pytest.mark.asyncio - async def test_read_domains_resource(self, mcp_server, mock_vultr_client): - """Test reading the domains resource content.""" - with patch('vultr_dns_mcp.server.VultrDNSServer', return_value=mock_vultr_client): - server = create_mcp_server("test-api-key") - - async with Client(server) as client: - try: - result = await client.read_resource("vultr://domains") - assert result is not None - mock_vultr_client.list_domains.assert_called_once() - except Exception: - # Resource reading might not be available in all FastMCP versions - pass - - -@pytest.mark.mcp -class TestMCPToolErrors: - """Test MCP tool error handling.""" - - @pytest.mark.asyncio - async def test_tool_with_api_error(self, mcp_server): - """Test tool behavior when API returns an error.""" - mock_client = AsyncMock() - mock_client.list_domains.side_effect = Exception("API Error") - - with patch('vultr_dns_mcp.server.VultrDNSServer', return_value=mock_client): - server = create_mcp_server("test-api-key") - - async with Client(server) as client: - result = await client.call_tool("list_dns_domains", {}) - - # Should handle the error gracefully - assert result is not None - - @pytest.mark.asyncio - async def test_missing_required_parameters(self, mcp_server): - """Test tool behavior with missing required parameters.""" - async with Client(mcp_server) as client: - with pytest.raises(Exception): - # This should fail due to missing required 'domain' parameter - await client.call_tool("get_dns_domain", {}) - - -@pytest.mark.integration -class TestMCPIntegration: - """Integration tests for the complete MCP workflow.""" - - @pytest.mark.asyncio - async def test_complete_domain_workflow(self, mcp_server, mock_vultr_client): - """Test a complete domain management workflow.""" - with patch('vultr_dns_mcp.server.VultrDNSServer', return_value=mock_vultr_client): - server = create_mcp_server("test-api-key") - - async with Client(server) as client: - # 1. List domains - domains = await client.call_tool("list_dns_domains", {}) - assert domains is not None - - # 2. Get domain details - domain_info = await client.call_tool("get_dns_domain", {"domain": "example.com"}) - assert domain_info is not None - - # 3. List records - records = await client.call_tool("list_dns_records", {"domain": "example.com"}) - assert records is not None - - # 4. Analyze configuration - analysis = await client.call_tool("analyze_dns_records", {"domain": "example.com"}) - assert analysis is not None - - # Verify all expected API calls were made - mock_vultr_client.list_domains.assert_called() - mock_vultr_client.get_domain.assert_called_with("example.com") - mock_vultr_client.list_records.assert_called_with("example.com") - - @pytest.mark.asyncio - async def test_record_management_workflow(self, mcp_server, mock_vultr_client): - """Test record creation and management workflow.""" - with patch('vultr_dns_mcp.server.VultrDNSServer', return_value=mock_vultr_client): - server = create_mcp_server("test-api-key") - - async with Client(server) as client: - # 1. Validate record before creation - validation = await client.call_tool("validate_dns_record", { - "record_type": "A", - "name": "www", - "data": "192.168.1.100" - }) - assert validation is not None - - # 2. Create the record - create_result = await client.call_tool("create_dns_record", { - "domain": "example.com", - "record_type": "A", - "name": "www", - "data": "192.168.1.100", - "ttl": 300 - }) - assert create_result is not None - - # 3. Verify the record was created - mock_vultr_client.create_record.assert_called_with( - "example.com", "A", "www", "192.168.1.100", 300, None - ) - - -@pytest.mark.unit -class TestValidationLogic: - """Test DNS record validation logic in isolation.""" - - @pytest.mark.asyncio - async def test_a_record_validation(self, mcp_server): - """Test A record validation logic.""" - async with Client(mcp_server) as client: - # Valid IPv4 - result = await client.call_tool("validate_dns_record", { - "record_type": "A", - "name": "www", - "data": "192.168.1.1" - }) - assert result is not None - - # Invalid IPv4 - result = await client.call_tool("validate_dns_record", { - "record_type": "A", - "name": "www", - "data": "999.999.999.999" - }) - assert result is not None - - @pytest.mark.asyncio - async def test_cname_validation(self, mcp_server): - """Test CNAME record validation logic.""" - async with Client(mcp_server) as client: - # Invalid: CNAME on root domain - result = await client.call_tool("validate_dns_record", { - "record_type": "CNAME", - "name": "@", - "data": "example.com" - }) - assert result is not None - - # Valid: CNAME on subdomain - result = await client.call_tool("validate_dns_record", { - "record_type": "CNAME", - "name": "www", - "data": "example.com" - }) - assert result is not None - - @pytest.mark.asyncio - async def test_mx_validation(self, mcp_server): - """Test MX record validation logic.""" - async with Client(mcp_server) as client: - # Invalid: Missing priority - result = await client.call_tool("validate_dns_record", { - "record_type": "MX", - "name": "@", - "data": "mail.example.com" - }) - assert result is not None - - # Valid: With priority - result = await client.call_tool("validate_dns_record", { - "record_type": "MX", - "name": "@", - "data": "mail.example.com", - "priority": 10 - }) - assert result is not None - - -if __name__ == "__main__": - pytest.main([__file__]) -''' - - return test_content - -def apply_all_fixes(): - """Apply all the fixes to the test suite.""" - print("๐Ÿš€ Starting comprehensive test fix process...") - - # Create updated conftest.py - print("๐Ÿ“ Creating updated conftest.py...") - conftest_content = create_updated_conftest() - with open("tests/conftest.py", "w") as f: - f.write(conftest_content) - - # Create updated test_mcp_server.py - print("๐Ÿ“ Creating updated test_mcp_server.py...") - test_content = create_updated_test_mcp_server() - with open("tests/test_mcp_server.py", "w") as f: - f.write(test_content) - - print("โœ… Test fixes applied successfully!") - print("\\n๐Ÿงช To run the tests:") - print(" pytest tests/ -v") - print(" pytest tests/ -m mcp") - print(" python run_tests.py --type mcp") - -if __name__ == "__main__": - apply_all_fixes() -''' - - return fix_script - -if __name__ == "__main__": - issues, fixes = analyze_test_structure() - - print(f"\n๐Ÿ“Š Summary:") - print(f" Issues found: {len(issues)}") - print(f" Fixes needed: {len(fixes)}") - - print(f"\n๐Ÿ› ๏ธ Creating comprehensive fix script...") - fix_script = create_fix_script() - - # Write the fix script - with open("/home/rpm/claude/vultr-dns-mcp-fix/comprehensive_test_fix.py", "w") as f: - f.write(fix_script) - - print(f"โœ… Fix script created: comprehensive_test_fix.py") - print(f"\n๐ŸŽฏ Key fixes to apply:") - for i, fix in enumerate(fixes, 1): - print(f" {i}. {fix}") - - print(f"\n๐Ÿš€ Next steps:") - print(f" 1. Clone the repository") - print(f" 2. Run the comprehensive fix script") - print(f" 3. Test the fixes with pytest")