
- Production-ready MCP server for Name Cheap API integration - Domain management (registration, renewal, availability checking) - DNS management (records, nameserver configuration) - SSL certificate management and monitoring - Account information and balance checking - Smart identifier resolution for improved UX - Comprehensive error handling with specific exception types - 80%+ test coverage with unit, integration, and MCP tests - CLI and MCP server interfaces - FastMCP 2.10.5+ implementation with full MCP spec compliance 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
266 lines
12 KiB
Python
266 lines
12 KiB
Python
"""Test MCP server functionality using FastMCP testing patterns."""
|
|
|
|
import pytest
|
|
from unittest.mock import patch, Mock
|
|
from fastmcp import testing
|
|
|
|
from mcp_namecheap.fastmcp_server import mcp_server
|
|
from .conftest import create_mock_response
|
|
|
|
|
|
class TestMCPServer:
|
|
"""Test MCP server tools and resources."""
|
|
|
|
@pytest.fixture
|
|
def mcp_client(self):
|
|
"""Create FastMCP test client."""
|
|
return testing.create_client(mcp_server)
|
|
|
|
@pytest.mark.mcp
|
|
async def test_list_domains_tool(self, mcp_client, sample_domains_response):
|
|
"""Test list domains tool."""
|
|
with patch('mcp_namecheap.domains.get_client') as mock_get_client:
|
|
mock_client = Mock()
|
|
mock_client.list_domains.return_value = sample_domains_response["DomainGetListResult"]["Domain"]
|
|
mock_get_client.return_value = mock_client
|
|
|
|
result = await mcp_client.call_tool("list_domains")
|
|
|
|
assert result["success"] is True
|
|
assert len(result["data"]) == 2
|
|
assert result["data"][0]["@Name"] == "example.com"
|
|
|
|
@pytest.mark.mcp
|
|
async def test_get_domain_info_tool(self, mcp_client, sample_domain_info_response):
|
|
"""Test get domain info tool."""
|
|
with patch('mcp_namecheap.domains.get_client') as mock_get_client:
|
|
mock_client = Mock()
|
|
mock_client.get_domain_info.return_value = sample_domain_info_response
|
|
mock_get_client.return_value = mock_client
|
|
|
|
result = await mcp_client.call_tool("get_domain_info", domain_identifier="example.com")
|
|
|
|
assert result["success"] is True
|
|
assert result["data"]["DomainGetInfoResult"]["@DomainName"] == "example.com"
|
|
|
|
@pytest.mark.mcp
|
|
async def test_check_domain_availability_tool(self, mcp_client, sample_domain_check_response):
|
|
"""Test check domain availability tool."""
|
|
with patch('mcp_namecheap.domains.get_client') as mock_get_client:
|
|
mock_client = Mock()
|
|
mock_client.check_domain_availability.return_value = sample_domain_check_response["DomainCheckResult"]
|
|
mock_get_client.return_value = mock_client
|
|
|
|
result = await mcp_client.call_tool("check_domain_availability", {
|
|
"domains": ["available-domain.com", "taken-domain.com"]
|
|
})
|
|
|
|
assert result["success"] is True
|
|
assert len(result["data"]) == 2
|
|
assert result["data"][0]["@Available"] == "true"
|
|
assert result["data"][1]["@Available"] == "false"
|
|
|
|
@pytest.mark.mcp
|
|
async def test_get_dns_records_tool(self, mcp_client, sample_dns_records_response):
|
|
"""Test get DNS records tool."""
|
|
with patch('mcp_namecheap.dns.get_client') as mock_get_client:
|
|
mock_client = Mock()
|
|
mock_client.get_dns_records.return_value = sample_dns_records_response["DomainDNSGetHostsResult"]["host"]
|
|
mock_get_client.return_value = mock_client
|
|
|
|
result = await mcp_client.call_tool("get_dns_records", domain_identifier="example.com")
|
|
|
|
assert result["success"] is True
|
|
assert len(result["data"]) == 2
|
|
assert result["data"][0]["@Type"] == "A"
|
|
assert result["data"][1]["@Type"] == "CNAME"
|
|
|
|
@pytest.mark.mcp
|
|
async def test_set_dns_records_tool(self, mcp_client):
|
|
"""Test set DNS records tool."""
|
|
with patch('mcp_namecheap.dns.get_client') as mock_get_client:
|
|
mock_client = Mock()
|
|
mock_client.set_dns_records.return_value = {"Status": "OK"}
|
|
mock_get_client.return_value = mock_client
|
|
|
|
result = await mcp_client.call_tool("set_dns_records", {
|
|
"domain_identifier": "example.com",
|
|
"records": [
|
|
{
|
|
"hostname": "@",
|
|
"record_type": "A",
|
|
"address": "192.168.1.1",
|
|
"ttl": 1800
|
|
}
|
|
]
|
|
})
|
|
|
|
assert result["success"] is True
|
|
|
|
@pytest.mark.mcp
|
|
async def test_list_ssl_certificates_tool(self, mcp_client, sample_ssl_certificates_response):
|
|
"""Test list SSL certificates tool."""
|
|
with patch('mcp_namecheap.ssl.get_client') as mock_get_client:
|
|
mock_client = Mock()
|
|
mock_client.list_ssl_certificates.return_value = sample_ssl_certificates_response["SSLListResult"]["SSL"]
|
|
mock_get_client.return_value = mock_client
|
|
|
|
result = await mcp_client.call_tool("list_ssl_certificates")
|
|
|
|
assert result["success"] is True
|
|
assert len(result["data"]) == 1
|
|
assert result["data"][0]["@HostName"] == "example.com"
|
|
|
|
@pytest.mark.mcp
|
|
async def test_get_account_info_tool(self, mcp_client, sample_account_info_response):
|
|
"""Test get account info tool."""
|
|
with patch('mcp_namecheap.account.get_client') as mock_get_client:
|
|
mock_client = Mock()
|
|
mock_client.get_account_info.return_value = sample_account_info_response
|
|
mock_get_client.return_value = mock_client
|
|
|
|
result = await mcp_client.call_tool("get_account_info")
|
|
|
|
assert result["success"] is True
|
|
assert result["data"]["UserGetAccountResult"]["@UserName"] == "test_user"
|
|
|
|
@pytest.mark.mcp
|
|
async def test_get_account_balance_tool(self, mcp_client, sample_balance_response):
|
|
"""Test get account balance tool."""
|
|
with patch('mcp_namecheap.account.get_client') as mock_get_client:
|
|
mock_client = Mock()
|
|
mock_client.get_account_balance.return_value = sample_balance_response
|
|
mock_get_client.return_value = mock_client
|
|
|
|
result = await mcp_client.call_tool("get_account_balance")
|
|
|
|
assert result["success"] is True
|
|
assert result["data"]["UserGetBalancesResult"]["@AccountBalance"] == "100.00"
|
|
|
|
@pytest.mark.mcp
|
|
async def test_domains_resource(self, mcp_client, sample_domains_response):
|
|
"""Test domains resource."""
|
|
with patch('mcp_namecheap.domains.get_client') as mock_get_client:
|
|
mock_client = Mock()
|
|
mock_client.list_domains.return_value = sample_domains_response["DomainGetListResult"]["Domain"]
|
|
mock_get_client.return_value = mock_client
|
|
|
|
result = await mcp_client.read_resource("namecheap://domains")
|
|
|
|
assert "Domains in Account:" in result
|
|
assert "example.com" in result
|
|
assert "test.org" in result
|
|
|
|
@pytest.mark.mcp
|
|
async def test_domain_resource(self, mcp_client, sample_domain_info_response):
|
|
"""Test individual domain resource."""
|
|
with patch('mcp_namecheap.domains.get_client') as mock_get_client:
|
|
mock_client = Mock()
|
|
mock_client.get_domain_info.return_value = sample_domain_info_response
|
|
mock_get_client.return_value = mock_client
|
|
|
|
result = await mcp_client.read_resource("namecheap://domain/example.com")
|
|
|
|
assert "Domain Information: example.com" in result
|
|
assert "test_user" in result
|
|
|
|
@pytest.mark.mcp
|
|
async def test_dns_resource(self, mcp_client, sample_dns_records_response):
|
|
"""Test DNS records resource."""
|
|
with patch('mcp_namecheap.dns.get_client') as mock_get_client:
|
|
mock_client = Mock()
|
|
mock_client.get_dns_records.return_value = sample_dns_records_response["DomainDNSGetHostsResult"]["host"]
|
|
mock_get_client.return_value = mock_client
|
|
|
|
result = await mcp_client.read_resource("namecheap://dns/example.com")
|
|
|
|
assert "DNS Records for example.com" in result
|
|
assert "192.168.1.1" in result
|
|
|
|
@pytest.mark.mcp
|
|
async def test_ssl_resource(self, mcp_client, sample_ssl_certificates_response):
|
|
"""Test SSL certificates resource."""
|
|
with patch('mcp_namecheap.ssl.get_client') as mock_get_client:
|
|
mock_client = Mock()
|
|
mock_client.list_ssl_certificates.return_value = sample_ssl_certificates_response["SSLListResult"]["SSL"]
|
|
mock_get_client.return_value = mock_client
|
|
|
|
result = await mcp_client.read_resource("namecheap://ssl")
|
|
|
|
assert "SSL Certificates in Account:" in result
|
|
assert "example.com" in result
|
|
|
|
@pytest.mark.mcp
|
|
async def test_account_resource(self, mcp_client, sample_account_info_response):
|
|
"""Test account resource."""
|
|
with patch('mcp_namecheap.account.get_client') as mock_get_client:
|
|
mock_client = Mock()
|
|
mock_client.get_account_info.return_value = sample_account_info_response
|
|
mock_get_client.return_value = mock_client
|
|
|
|
result = await mcp_client.read_resource("namecheap://account")
|
|
|
|
assert "Account Information" in result
|
|
assert "test_user" in result
|
|
|
|
@pytest.mark.mcp
|
|
async def test_balance_resource(self, mcp_client, sample_balance_response):
|
|
"""Test balance resource."""
|
|
with patch('mcp_namecheap.account.get_client') as mock_get_client:
|
|
mock_client = Mock()
|
|
mock_client.get_account_balance.return_value = sample_balance_response
|
|
mock_get_client.return_value = mock_client
|
|
|
|
result = await mcp_client.read_resource("namecheap://balance")
|
|
|
|
assert "Account Balance" in result
|
|
assert "100.00" in result
|
|
|
|
|
|
class TestMCPErrorHandling:
|
|
"""Test MCP server error handling."""
|
|
|
|
@pytest.fixture
|
|
def mcp_client(self):
|
|
"""Create FastMCP test client."""
|
|
return testing.create_client(mcp_server)
|
|
|
|
@pytest.mark.mcp
|
|
async def test_domain_not_found_error(self, mcp_client):
|
|
"""Test domain not found error handling."""
|
|
from mcp_namecheap.server import NameCheapNotFoundError
|
|
|
|
with patch('mcp_namecheap.domains.get_client') as mock_get_client:
|
|
mock_client = Mock()
|
|
mock_client.get_domain_info.side_effect = NameCheapNotFoundError("Domain not found")
|
|
mock_get_client.return_value = mock_client
|
|
|
|
result = await mcp_client.call_tool("get_domain_info", domain_identifier="nonexistent.com")
|
|
|
|
assert result["success"] is False
|
|
assert "Domain not found" in result["error"]
|
|
|
|
@pytest.mark.mcp
|
|
async def test_validation_error(self, mcp_client):
|
|
"""Test validation error handling."""
|
|
from pydantic import ValidationError
|
|
|
|
with pytest.raises(ValidationError):
|
|
await mcp_client.call_tool("check_domain_availability", {
|
|
"domains": [] # Empty list should fail validation
|
|
})
|
|
|
|
@pytest.mark.mcp
|
|
async def test_api_error_handling(self, mcp_client):
|
|
"""Test API error handling."""
|
|
from mcp_namecheap.server import NameCheapAPIError
|
|
|
|
with patch('mcp_namecheap.domains.get_client') as mock_get_client:
|
|
mock_client = Mock()
|
|
mock_client.list_domains.side_effect = NameCheapAPIError("API Error")
|
|
mock_get_client.return_value = mock_client
|
|
|
|
result = await mcp_client.call_tool("list_domains")
|
|
|
|
assert result["success"] is False
|
|
assert "API Error" in result["error"] |