Refactor DNS resolution: remove background service components, simplify manual resolution, and update configuration and tests accordingly.

This commit is contained in:
Philip Henning 2025-08-18 11:06:02 +02:00
parent b2d48be045
commit f8b235ab24
6 changed files with 177 additions and 348 deletions

View file

@ -253,66 +253,23 @@ class TestDNSService:
def test_initialization(self):
"""Test DNS service initialization."""
service = DNSService(update_interval=600, enabled=True, timeout=10.0)
service = DNSService(enabled=True, timeout=10.0)
assert service.update_interval == 600
assert service.enabled is True
assert service.timeout == 10.0
assert service._background_task is None
assert service._resolution_cache == {}
def test_update_callback_setting(self):
"""Test setting update callback."""
def test_initialization_defaults(self):
"""Test DNS service initialization with defaults."""
service = DNSService()
callback = MagicMock()
service.set_update_callback(callback)
assert service._update_callback is callback
assert service.enabled is True
assert service.timeout == 5.0
@pytest.mark.asyncio
async def test_background_service_lifecycle(self):
"""Test starting and stopping background service."""
async def test_resolve_entry_async_enabled(self):
"""Test async resolution when service is enabled."""
service = DNSService(enabled=True)
# Start service
await service.start_background_resolution()
assert service._background_task is not None
assert not service._stop_event.is_set()
# Stop service
await service.stop_background_resolution()
assert service._background_task is None
@pytest.mark.asyncio
async def test_background_service_disabled(self):
"""Test background service when disabled."""
service = DNSService(enabled=False)
await service.start_background_resolution()
assert service._background_task is None
@pytest.mark.asyncio
async def test_resolve_entry_async_cache_hit(self):
"""Test async resolution with cache hit."""
service = DNSService()
# Add entry to cache
cached_resolution = DNSResolution(
hostname="example.com",
resolved_ip="192.0.2.1",
status=DNSResolutionStatus.RESOLVED,
resolved_at=datetime.now(),
)
service._resolution_cache["example.com"] = cached_resolution
resolution = await service.resolve_entry_async("example.com")
assert resolution is cached_resolution
@pytest.mark.asyncio
async def test_resolve_entry_async_cache_miss(self):
"""Test async resolution with cache miss."""
service = DNSService()
with patch("src.hosts.core.dns.resolve_hostname") as mock_resolve:
mock_resolution = DNSResolution(
hostname="example.com",
@ -325,84 +282,47 @@ class TestDNSService:
resolution = await service.resolve_entry_async("example.com")
assert resolution is mock_resolution
assert service._resolution_cache["example.com"] is mock_resolution
mock_resolve.assert_called_once_with("example.com", 5.0)
def test_resolve_entry_sync_cache_hit(self):
"""Test synchronous resolution with cache hit."""
service = DNSService()
# Add entry to cache
cached_resolution = DNSResolution(
hostname="example.com",
resolved_ip="192.0.2.1",
status=DNSResolutionStatus.RESOLVED,
resolved_at=datetime.now(),
)
service._resolution_cache["example.com"] = cached_resolution
resolution = service.resolve_entry("example.com")
assert resolution is cached_resolution
def test_resolve_entry_sync_cache_miss(self):
"""Test synchronous resolution with cache miss."""
service = DNSService(enabled=True)
with patch("asyncio.create_task") as mock_create_task:
resolution = service.resolve_entry("example.com")
assert resolution.hostname == "example.com"
assert resolution.status == DNSResolutionStatus.RESOLVING
assert resolution.resolved_ip is None
mock_create_task.assert_called_once()
def test_resolve_entry_sync_disabled(self):
"""Test synchronous resolution when service is disabled."""
@pytest.mark.asyncio
async def test_resolve_entry_async_disabled(self):
"""Test async resolution when service is disabled."""
service = DNSService(enabled=False)
with patch("asyncio.create_task") as mock_create_task:
resolution = service.resolve_entry("example.com")
resolution = await service.resolve_entry_async("example.com")
assert resolution.hostname == "example.com"
assert resolution.status == DNSResolutionStatus.RESOLVING
mock_create_task.assert_not_called()
assert resolution.hostname == "example.com"
assert resolution.resolved_ip is None
assert resolution.status == DNSResolutionStatus.NOT_RESOLVED
assert resolution.error_message == "DNS resolution is disabled"
@pytest.mark.asyncio
async def test_refresh_entry(self):
"""Test manual entry refresh."""
service = DNSService()
# Add stale entry to cache
stale_resolution = DNSResolution(
hostname="example.com",
resolved_ip="192.0.2.1",
status=DNSResolutionStatus.RESOLVED,
resolved_at=datetime.now() - timedelta(hours=1),
)
service._resolution_cache["example.com"] = stale_resolution
service = DNSService(enabled=True)
with patch("src.hosts.core.dns.resolve_hostname") as mock_resolve:
fresh_resolution = DNSResolution(
mock_resolution = DNSResolution(
hostname="example.com",
resolved_ip="192.0.2.2",
resolved_ip="192.0.2.1",
status=DNSResolutionStatus.RESOLVED,
resolved_at=datetime.now(),
)
mock_resolve.return_value = fresh_resolution
mock_resolve.return_value = mock_resolution
result = await service.refresh_entry("example.com")
assert result is fresh_resolution
assert service._resolution_cache["example.com"] is fresh_resolution
assert "example.com" not in service._resolution_cache or service._resolution_cache["example.com"].resolved_ip == "192.0.2.2"
assert result is mock_resolution
mock_resolve.assert_called_once_with("example.com", 5.0)
@pytest.mark.asyncio
async def test_refresh_all_entries(self):
"""Test manual refresh of all entries."""
service = DNSService()
async def test_refresh_all_entries_enabled(self):
"""Test manual refresh of all entries when enabled."""
service = DNSService(enabled=True)
hostnames = ["example.com", "test.example"]
with patch("src.hosts.core.dns.resolve_hostnames_batch") as mock_batch:
fresh_resolutions = [
mock_resolutions = [
DNSResolution(
hostname="example.com",
resolved_ip="192.0.2.1",
@ -416,57 +336,27 @@ class TestDNSService:
resolved_at=datetime.now(),
),
]
mock_batch.return_value = fresh_resolutions
mock_batch.return_value = mock_resolutions
results = await service.refresh_all_entries(hostnames)
assert results == fresh_resolutions
assert len(service._resolution_cache) == 2
assert service._resolution_cache["example.com"].resolved_ip == "192.0.2.1"
assert service._resolution_cache["test.example"].resolved_ip == "192.0.2.2"
assert results == mock_resolutions
mock_batch.assert_called_once_with(hostnames, 5.0)
def test_cache_operations(self):
"""Test cache operations."""
service = DNSService()
@pytest.mark.asyncio
async def test_refresh_all_entries_disabled(self):
"""Test manual refresh of all entries when disabled."""
service = DNSService(enabled=False)
hostnames = ["example.com", "test.example"]
# Test empty cache
assert service.get_cached_resolution("example.com") is None
results = await service.refresh_all_entries(hostnames)
# Add to cache
resolution = DNSResolution(
hostname="example.com",
resolved_ip="192.0.2.1",
status=DNSResolutionStatus.RESOLVED,
resolved_at=datetime.now(),
)
service._resolution_cache["example.com"] = resolution
# Test cache retrieval
assert service.get_cached_resolution("example.com") is resolution
# Test cache stats
stats = service.get_cache_stats()
assert stats["total_entries"] == 1
assert stats["successful"] == 1
assert stats["failed"] == 0
# Add failed resolution
failed_resolution = DNSResolution(
hostname="failed.example",
resolved_ip=None,
status=DNSResolutionStatus.RESOLUTION_FAILED,
resolved_at=datetime.now(),
)
service._resolution_cache["failed.example"] = failed_resolution
stats = service.get_cache_stats()
assert stats["total_entries"] == 2
assert stats["successful"] == 1
assert stats["failed"] == 1
# Clear cache
service.clear_cache()
assert len(service._resolution_cache) == 0
assert len(results) == 2
for i, result in enumerate(results):
assert result.hostname == hostnames[i]
assert result.resolved_ip is None
assert result.status == DNSResolutionStatus.NOT_RESOLVED
assert result.error_message == "DNS resolution is disabled"
class TestHostEntryDNSIntegration: