"""Test Name Cheap API server.""" import pytest from unittest.mock import Mock, patch import httpx import xmltodict from mcp_namecheap.server import ( NameCheapAPIServer, NameCheapConfig, NameCheapAPIError, NameCheapAuthError, NameCheapNotFoundError, NameCheapRateLimitError, NameCheapValidationError ) class TestNameCheapAPIServer: """Test Name Cheap API server functionality.""" @pytest.mark.unit def test_config_creation(self): """Test configuration creation.""" config = NameCheapConfig( api_key="test_key", username="test_user", client_ip="127.0.0.1", sandbox=True ) assert config.api_key == "test_key" assert config.username == "test_user" assert config.client_ip == "127.0.0.1" assert config.sandbox is True @pytest.mark.unit def test_server_initialization(self, mock_config): """Test server initialization.""" with patch('mcp_namecheap.server.httpx.Client') as mock_client: server = NameCheapAPIServer(mock_config) assert server.config == mock_config assert "sandbox" in server.base_url mock_client.assert_called_once() @pytest.mark.unit def test_load_config_from_env_missing_vars(self): """Test loading config from environment with missing variables.""" with patch.dict('os.environ', {}, clear=True): with pytest.raises(NameCheapAuthError): NameCheapAPIServer() @pytest.mark.unit def test_load_config_from_env_success(self): """Test loading config from environment successfully.""" env_vars = { 'NAMECHEAP_API_KEY': 'test_key', 'NAMECHEAP_USERNAME': 'test_user', 'NAMECHEAP_CLIENT_IP': '127.0.0.1', 'NAMECHEAP_SANDBOX': 'true' } with patch.dict('os.environ', env_vars): with patch('mcp_namecheap.server.httpx.Client'): server = NameCheapAPIServer() assert server.config.api_key == 'test_key' assert server.config.sandbox is True @pytest.mark.unit def test_make_request_success(self, mock_server): """Test successful API request.""" # Mock response mock_response = Mock() mock_response.text = ''' Success ''' mock_response.raise_for_status.return_value = None mock_server.client.get.return_value = mock_response result = mock_server._make_request("test.command") assert result["TestResult"] == "Success" mock_server.client.get.assert_called_once() @pytest.mark.unit def test_make_request_api_error(self, mock_server): """Test API request with API error response.""" # Mock error response mock_response = Mock() mock_response.text = ''' Test error message ''' mock_response.raise_for_status.return_value = None mock_server.client.get.return_value = mock_response with pytest.raises(NameCheapAPIError): mock_server._make_request("test.command") @pytest.mark.unit def test_make_request_http_error(self, mock_server): """Test API request with HTTP error.""" mock_response = Mock() mock_response.status_code = 401 mock_response.text = "Unauthorized" mock_server.client.get.side_effect = httpx.HTTPStatusError( "Unauthorized", request=Mock(), response=mock_response ) with pytest.raises(NameCheapAuthError): mock_server._make_request("test.command") @pytest.mark.unit def test_make_request_rate_limit(self, mock_server): """Test API request with rate limit error.""" mock_response = Mock() mock_response.status_code = 429 mock_response.text = "Too Many Requests" mock_server.client.get.side_effect = httpx.HTTPStatusError( "Too Many Requests", request=Mock(), response=mock_response ) with pytest.raises(NameCheapRateLimitError): mock_server._make_request("test.command") @pytest.mark.unit def test_make_request_timeout(self, mock_server): """Test API request with timeout.""" mock_server.client.get.side_effect = httpx.TimeoutException("Timeout") with pytest.raises(NameCheapAPIError) as exc_info: mock_server._make_request("test.command") assert "timeout" in str(exc_info.value).lower() @pytest.mark.unit def test_handle_api_errors_not_found(self, mock_server): """Test API error handling for not found errors.""" api_response = { "Errors": { "Error": { "@Number": "2030166", "#text": "Domain not found" } } } with pytest.raises(NameCheapNotFoundError): mock_server._handle_api_errors(api_response) @pytest.mark.unit def test_handle_api_errors_validation(self, mock_server): """Test API error handling for validation errors.""" api_response = { "Errors": { "Error": { "@Number": "1234", "#text": "Invalid domain format" } } } with pytest.raises(NameCheapValidationError): mock_server._handle_api_errors(api_response) @pytest.mark.unit def test_handle_api_errors_multiple(self, mock_server): """Test API error handling with multiple errors.""" api_response = { "Errors": { "Error": [ {"@Number": "1", "#text": "First error"}, {"@Number": "2", "#text": "Second error"} ] } } with pytest.raises(NameCheapAPIError) as exc_info: mock_server._handle_api_errors(api_response) assert "First error" in str(exc_info.value) assert "Second error" in str(exc_info.value) @pytest.mark.unit def test_split_domain_valid(self, mock_server): """Test domain splitting with valid domains.""" sld, tld = mock_server._split_domain("example.com") assert sld == "example" assert tld == "com" sld, tld = mock_server._split_domain("test.co.uk") assert sld == "test" assert tld == "co.uk" @pytest.mark.unit def test_split_domain_invalid(self, mock_server): """Test domain splitting with invalid domains.""" with pytest.raises(NameCheapValidationError): mock_server._split_domain("invalid") @pytest.mark.unit def test_list_domains(self, mock_server): """Test list domains method.""" mock_response = Mock() mock_response.text = ''' ''' mock_response.raise_for_status.return_value = None mock_server.client.get.return_value = mock_response domains = mock_server.list_domains() assert len(domains) == 1 assert domains[0]["@Name"] == "example.com" @pytest.mark.unit def test_check_domain_availability(self, mock_server): """Test check domain availability method.""" mock_response = Mock() mock_response.text = ''' ''' mock_response.raise_for_status.return_value = None mock_server.client.get.return_value = mock_response results = mock_server.check_domain_availability(["test.com"]) assert len(results) == 1 assert results[0]["@Available"] == "true" @pytest.mark.unit def test_get_dns_records(self, mock_server): """Test get DNS records method.""" mock_response = Mock() mock_response.text = ''' ''' mock_response.raise_for_status.return_value = None mock_server.client.get.return_value = mock_response records = mock_server.get_dns_records("example.com") assert len(records) == 1 assert records[0]["@Type"] == "A" @pytest.mark.unit def test_context_manager(self, mock_config): """Test server as context manager.""" with patch('mcp_namecheap.server.httpx.Client') as mock_client_class: mock_client = Mock() mock_client_class.return_value = mock_client with NameCheapAPIServer(mock_config) as server: assert server.config == mock_config mock_client.close.assert_called_once() class TestErrorClassification: """Test error classification logic.""" @pytest.mark.unit def test_auth_error_classification(self, mock_server): """Test authentication error classification.""" api_response = { "Errors": { "Error": { "@Number": "1001", "#text": "Authentication failed" } } } with pytest.raises(NameCheapAuthError): mock_server._handle_api_errors(api_response) @pytest.mark.unit def test_not_found_error_classification(self, mock_server): """Test not found error classification.""" api_response = { "Errors": { "Error": { "@Number": "2030166", "#text": "Domain not found" } } } with pytest.raises(NameCheapNotFoundError): mock_server._handle_api_errors(api_response) @pytest.mark.unit def test_validation_error_classification(self, mock_server): """Test validation error classification.""" api_response = { "Errors": { "Error": { "@Number": "1234", "#text": "Invalid parameter" } } } with pytest.raises(NameCheapValidationError): mock_server._handle_api_errors(api_response) @pytest.mark.unit def test_generic_error_classification(self, mock_server): """Test generic error classification.""" api_response = { "Errors": { "Error": { "@Number": "9999", "#text": "Unknown error" } } } with pytest.raises(NameCheapAPIError): mock_server._handle_api_errors(api_response)