"""
Unit tests for domain checking functionality.
"""
import pytest
from unittest.mock import AsyncMock, patch
import asyncio
from main import (
DomainChecker,
DomainRequest,
check_domain_availability,
validate_domain_syntax,
DomainCheckError,
)
class TestDomainRequest:
"""Test cases for DomainRequest validation."""
def test_valid_domain_request(self):
"""Test valid domain request creation."""
request = DomainRequest(domain="test", tld="com")
assert request.domain == "test"
assert request.tld == "com"
def test_domain_with_existing_tld(self):
"""Test domain that already includes TLD."""
request = DomainRequest(domain="test.com")
assert request.domain == "test.com"
assert request.tld == "com"
def test_domain_cleanup(self):
"""Test domain cleanup (www removal, lowercase)."""
request = DomainRequest(domain="WWW.TEST")
assert request.domain == "test"
assert request.tld == "com"
def test_invalid_domain_format(self):
"""Test invalid domain format validation."""
with pytest.raises(ValueError, match="Invalid domain name format"):
DomainRequest(domain="-invalid")
def test_empty_domain(self):
"""Test empty domain validation."""
with pytest.raises(ValueError, match="Domain cannot be empty"):
DomainRequest(domain="")
def test_invalid_tld_format(self):
"""Test invalid TLD format validation."""
with pytest.raises(ValueError, match="Invalid TLD format"):
DomainRequest(domain="test", tld="123")
class TestDomainChecker:
"""Test cases for DomainChecker class."""
@pytest.fixture
async def domain_checker(self):
"""Create a DomainChecker instance for testing."""
checker = DomainChecker(timeout=5)
yield checker
await checker.client.aclose()
def test_build_full_domain(self, domain_checker):
"""Test building full domain names."""
# Domain without TLD
result = domain_checker._build_full_domain("test", "com")
assert result == "test.com"
# Domain with existing TLD
result = domain_checker._build_full_domain("test.org", "com")
assert result == "test.org"
@pytest.mark.asyncio
async def test_check_dns_resolution_available(self, domain_checker):
"""Test DNS resolution for available domain."""
with patch("socket.gethostbyname", side_effect=OSError("Name not found")):
result = await domain_checker.check_dns_resolution(
"nonexistent-test-domain-12345.com"
)
assert result is True
@pytest.mark.asyncio
async def test_check_dns_resolution_unavailable(self, domain_checker):
"""Test DNS resolution for unavailable domain."""
with patch("socket.gethostbyname", return_value="1.2.3.4"):
result = await domain_checker.check_dns_resolution("google.com")
assert result is False
@pytest.mark.asyncio
async def test_check_whois_api_mock(self, domain_checker):
"""Test WHOIS API mock functionality."""
# Test different hash results
result1 = await domain_checker.check_whois_api("test1.com")
result2 = await domain_checker.check_whois_api("test2.com")
# Results should be consistent for same domain
result1_repeat = await domain_checker.check_whois_api("test1.com")
assert result1 == result1_repeat
# Results should be boolean or None
assert result1 in [True, False, None]
assert result2 in [True, False, None]
@pytest.mark.asyncio
async def test_check_availability_success(self, domain_checker):
"""Test successful availability check."""
with patch.object(domain_checker, "check_whois_api", return_value=True):
result = await domain_checker.check_availability("test", "com")
assert result is True
@pytest.mark.asyncio
async def test_check_availability_fallback_to_dns(self, domain_checker):
"""Test fallback to DNS when WHOIS fails."""
with patch.object(domain_checker, "check_whois_api", return_value=None):
with patch.object(
domain_checker, "check_dns_resolution", return_value=False
):
result = await domain_checker.check_availability("test", "com")
assert result is False
@pytest.mark.asyncio
async def test_check_availability_all_methods_fail(self, domain_checker):
"""Test error when all checking methods fail."""
with patch.object(
domain_checker, "check_whois_api", side_effect=Exception("API Error")
):
with patch.object(
domain_checker,
"check_dns_resolution",
side_effect=Exception("DNS Error"),
):
with pytest.raises(
DomainCheckError, match="Failed to check domain availability"
):
await domain_checker.check_availability("test", "com")
class TestMCPTools:
"""Test cases for MCP tool functions."""
@pytest.mark.asyncio
async def test_check_domain_availability_success(self):
"""Test successful domain availability check through MCP tool."""
with patch("main.domain_checker") as mock_checker:
mock_checker.check_availability.return_value = True
result = await check_domain_availability("test", "com")
assert result is True
mock_checker.check_availability.assert_called_once_with("test", "com")
@pytest.mark.asyncio
async def test_check_domain_availability_invalid_domain(self):
"""Test domain availability check with invalid domain."""
with pytest.raises(ValueError, match="Invalid domain format"):
await check_domain_availability("-invalid")
@pytest.mark.asyncio
async def test_validate_domain_syntax_valid(self):
"""Test domain syntax validation for valid domain."""
result = await validate_domain_syntax("test")
assert result["valid"] is True
assert result["domain"] == "test"
assert result["tld"] == "com"
assert result["full_domain"] == "test.com"
assert "length" in result
@pytest.mark.asyncio
async def test_validate_domain_syntax_invalid(self):
"""Test domain syntax validation for invalid domain."""
result = await validate_domain_syntax("-invalid")
assert result["valid"] is False
assert "error" in result
assert result["domain"] == "-invalid"
@pytest.mark.asyncio
async def test_validate_domain_syntax_too_long(self):
"""Test domain syntax validation for overly long domain."""
long_domain = "a" * 70 # Exceeds 63 character limit
result = await validate_domain_syntax(long_domain)
assert result["valid"] is False
assert "exceeds 63 characters" in result["error"]
class TestIntegration:
"""Integration test cases."""
@pytest.mark.asyncio
async def test_end_to_end_domain_check(self):
"""Test end-to-end domain checking workflow."""
# Test with a domain that should be available (very unique)
test_domain = "test-unique-domain-12345-abcdef"
# This should work with the actual DNS checking
result = await check_domain_availability(test_domain, "com")
# Result should be boolean
assert isinstance(result, bool)
@pytest.mark.asyncio
async def test_multiple_concurrent_checks(self):
"""Test multiple concurrent domain checks."""
domains = [f"test{i}" for i in range(10)]
tasks = [check_domain_availability(domain, "com") for domain in domains]
results = await asyncio.gather(*tasks, return_exceptions=True)
# All results should be boolean or exceptions
for result in results:
assert isinstance(result, (bool, Exception))
# At least some should succeed
successful_results = [r for r in results if isinstance(r, bool)]
assert len(successful_results) > 0
if __name__ == "__main__":
pytest.main([__file__, "-v"])