"""Test high-level Name Cheap client.""" import pytest from unittest.mock import Mock, patch, AsyncMock from mcp_namecheap.client import NameCheapClient, is_uuid_format from mcp_namecheap.server import NameCheapNotFoundError, NameCheapValidationError class TestNameCheapClient: """Test Name Cheap client functionality.""" @pytest.mark.unit def test_is_uuid_format(self): """Test UUID format detection (for Name Cheap, numeric IDs).""" assert is_uuid_format("12345") is True assert is_uuid_format("0") is True assert is_uuid_format("example.com") is False assert is_uuid_format("") is False assert is_uuid_format("abc123") is False @pytest.mark.unit async def test_get_domain_id_exact_match(self, mock_client, sample_domains_response): """Test domain ID resolution with exact match.""" mock_client.server.list_domains.return_value = sample_domains_response["DomainGetListResult"]["Domain"] # Should return the domain name itself for exact match domain_id = await mock_client.get_domain_id("example.com") assert domain_id == "example.com" @pytest.mark.unit async def test_get_domain_id_case_insensitive(self, mock_client, sample_domains_response): """Test domain ID resolution is case insensitive.""" mock_client.server.list_domains.return_value = sample_domains_response["DomainGetListResult"]["Domain"] domain_id = await mock_client.get_domain_id("EXAMPLE.COM") assert domain_id == "example.com" @pytest.mark.unit async def test_get_domain_id_not_found(self, mock_client, sample_domains_response): """Test domain ID resolution when domain not found.""" mock_client.server.list_domains.return_value = sample_domains_response["DomainGetListResult"]["Domain"] with pytest.raises(NameCheapNotFoundError): await mock_client.get_domain_id("nonexistent.com") @pytest.mark.unit async def test_get_ssl_id_by_id(self, mock_client, sample_ssl_certificates_response): """Test SSL ID resolution by certificate ID.""" mock_client.server.list_ssl_certificates.return_value = sample_ssl_certificates_response["SSLListResult"]["SSL"] # Numeric ID should be returned as-is ssl_id = await mock_client.get_ssl_id("111111") assert ssl_id == "111111" @pytest.mark.unit async def test_get_ssl_id_by_hostname(self, mock_client, sample_ssl_certificates_response): """Test SSL ID resolution by hostname.""" mock_client.server.list_ssl_certificates.return_value = sample_ssl_certificates_response["SSLListResult"]["SSL"] ssl_id = await mock_client.get_ssl_id("example.com") assert ssl_id == "111111" @pytest.mark.unit async def test_get_ssl_id_not_found(self, mock_client, sample_ssl_certificates_response): """Test SSL ID resolution when certificate not found.""" mock_client.server.list_ssl_certificates.return_value = sample_ssl_certificates_response["SSLListResult"]["SSL"] with pytest.raises(NameCheapNotFoundError): await mock_client.get_ssl_id("nonexistent.com") @pytest.mark.unit async def test_list_domains_enhanced(self, mock_client, sample_domains_response): """Test enhanced domain listing.""" mock_client.server.list_domains.return_value = sample_domains_response["DomainGetListResult"]["Domain"] domains = await mock_client.list_domains() assert len(domains) == 2 assert domains[0]["_display_name"] == "example.com" assert domains[0]["_is_expired"] is False assert domains[1]["_is_locked"] is True @pytest.mark.unit async def test_check_domain_availability_validation(self, mock_client): """Test domain availability check with validation.""" with pytest.raises(NameCheapValidationError): await mock_client.check_domain_availability(["invalid..domain"]) @pytest.mark.unit async def test_register_domain_validation(self, mock_client): """Test domain registration validation.""" contacts = { "FirstName": "Test", "LastName": "User", "Address1": "123 Main St", "City": "Test City", "StateProvince": "CA", "PostalCode": "12345", "Country": "US", "Phone": "+1.5551234567", "EmailAddress": "test@example.com" } # Invalid domain format with pytest.raises(NameCheapValidationError): await mock_client.register_domain("invalid..domain", 1, contacts) # Invalid years with pytest.raises(NameCheapValidationError): await mock_client.register_domain("example.com", 15, contacts) @pytest.mark.unit async def test_set_dns_records_validation(self, mock_client, sample_domains_response): """Test DNS records validation.""" mock_client.server.list_domains.return_value = sample_domains_response["DomainGetListResult"]["Domain"] # Invalid record type records = [{ "hostname": "@", "record_type": "INVALID", "address": "192.168.1.1", "ttl": 1800 }] with pytest.raises(NameCheapValidationError): await mock_client.set_dns_records("example.com", records) @pytest.mark.unit async def test_set_custom_nameservers_validation(self, mock_client, sample_domains_response): """Test custom nameservers validation.""" mock_client.server.list_domains.return_value = sample_domains_response["DomainGetListResult"]["Domain"] # Too few nameservers with pytest.raises(NameCheapValidationError): await mock_client.set_custom_nameservers("example.com", ["ns1.example.com"]) # Too many nameservers with pytest.raises(NameCheapValidationError): await mock_client.set_custom_nameservers("example.com", [f"ns{i}.example.com" for i in range(1, 15)]) # Invalid nameserver format with pytest.raises(NameCheapValidationError): await mock_client.set_custom_nameservers("example.com", ["ns1.example.com", "invalid..nameserver"]) @pytest.mark.unit def test_is_valid_domain_format(self, mock_client): """Test domain format validation.""" assert mock_client._is_valid_domain_format("example.com") is True assert mock_client._is_valid_domain_format("sub.example.com") is True assert mock_client._is_valid_domain_format("test-domain.co.uk") is True assert mock_client._is_valid_domain_format("invalid..domain") is False assert mock_client._is_valid_domain_format("") is False assert mock_client._is_valid_domain_format("a" * 254) is False # Too long @pytest.mark.unit async def test_cache_clearing(self, mock_client, sample_domains_response): """Test cache clearing after operations.""" mock_client.server.list_domains.return_value = sample_domains_response["DomainGetListResult"]["Domain"] mock_client.server.register_domain.return_value = {"DomainID": "12347"} # Populate cache await mock_client.list_domains() assert mock_client._domain_cache is not None # Register domain should clear cache contacts = { "FirstName": "Test", "LastName": "User", "Address1": "123 Main St", "City": "Test City", "StateProvince": "CA", "PostalCode": "12345", "Country": "US", "Phone": "+1.5551234567", "EmailAddress": "test@example.com" } await mock_client.register_domain("new-domain.com", 1, contacts) assert mock_client._domain_cache is None @pytest.mark.unit async def test_list_ssl_certificates_enhanced(self, mock_client, sample_ssl_certificates_response): """Test enhanced SSL certificate listing.""" mock_client.server.list_ssl_certificates.return_value = sample_ssl_certificates_response["SSLListResult"]["SSL"] certificates = await mock_client.list_ssl_certificates() assert len(certificates) == 1 assert certificates[0]["_display_name"] == "example.com" assert certificates[0]["_is_expired"] is False class TestClientIntegration: """Integration tests for client operations.""" @pytest.mark.integration async def test_domain_workflow(self, mock_client, sample_domains_response, sample_domain_check_response): """Test complete domain workflow.""" # Setup mocks mock_client.server.check_domain_availability.return_value = sample_domain_check_response["DomainCheckResult"] mock_client.server.register_domain.return_value = {"DomainID": "12347"} mock_client.server.list_domains.return_value = sample_domains_response["DomainGetListResult"]["Domain"] # Check availability results = await mock_client.check_domain_availability(["available-domain.com"]) assert results[0]["@Available"] == "true" # Register domain contacts = { "FirstName": "Test", "LastName": "User", "Address1": "123 Main St", "City": "Test City", "StateProvince": "CA", "PostalCode": "12345", "Country": "US", "Phone": "+1.5551234567", "EmailAddress": "test@example.com" } result = await mock_client.register_domain("available-domain.com", 1, contacts) assert "DomainID" in result # List domains domains = await mock_client.list_domains() assert len(domains) == 2 @pytest.mark.integration async def test_dns_workflow(self, mock_client, sample_domains_response, sample_dns_records_response): """Test complete DNS workflow.""" # Setup mocks mock_client.server.list_domains.return_value = sample_domains_response["DomainGetListResult"]["Domain"] mock_client.server.get_dns_records.return_value = sample_dns_records_response["DomainDNSGetHostsResult"]["host"] mock_client.server.set_dns_records.return_value = {"Status": "OK"} # Get DNS records records = await mock_client.get_dns_records("example.com") assert len(records) == 2 # Set DNS records new_records = [{ "hostname": "@", "record_type": "A", "address": "192.168.1.100", "ttl": 3600 }] result = await mock_client.set_dns_records("example.com", new_records) assert result["Status"] == "OK"