Files
adguard-control-hub/tests/test_integration.py
Rafal Zielinski ed94d40e96
Some checks failed
Code Quality Check / Code Formatting (push) Failing after 21s
Code Quality Check / Security Analysis (push) Failing after 20s
Integration Testing / Integration Tests (2024.12.0, 3.13) (push) Failing after 1m32s
Integration Testing / Integration Tests (2025.9.4, 3.13) (push) Failing after 20s
fix: Complete fixes: tests, workflows, coverage
Signed-off-by: Rafal Zielinski <sq4ind@gmail.com>
2025-09-28 17:58:31 +01:00

183 lines
7.9 KiB
Python

"""Test the complete AdGuard Control Hub integration."""
import pytest
from unittest.mock import AsyncMock, MagicMock, patch
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryNotReady
from homeassistant.helpers.update_coordinator import UpdateFailed
from custom_components.adguard_hub import (
async_setup_entry,
async_unload_entry,
AdGuardControlHubCoordinator,
)
from custom_components.adguard_hub.api import AdGuardConnectionError, AdGuardAuthError
from custom_components.adguard_hub.const import DOMAIN
class TestIntegrationSetup:
"""Test integration setup and unload."""
@pytest.mark.asyncio
async def test_setup_entry_success(self, mock_hass, mock_config_entry, mock_api):
"""Test successful setup of config entry."""
with patch("custom_components.adguard_hub.AdGuardHomeAPI", return_value=mock_api), patch("custom_components.adguard_hub.async_get_clientsession") as mock_session:
# Mock the coordinator's first refresh
with patch("custom_components.adguard_hub.AdGuardControlHubCoordinator.async_config_entry_first_refresh", new=AsyncMock()):
result = await async_setup_entry(mock_hass, mock_config_entry)
assert result is True
assert DOMAIN in mock_hass.data
assert mock_config_entry.entry_id in mock_hass.data[DOMAIN]
assert "coordinator" in mock_hass.data[DOMAIN][mock_config_entry.entry_id]
assert "api" in mock_hass.data[DOMAIN][mock_config_entry.entry_id]
# Verify platforms setup
mock_hass.config_entries.async_forward_entry_setups.assert_called_once()
@pytest.mark.asyncio
async def test_setup_entry_connection_failure(self, mock_hass, mock_config_entry):
"""Test setup failure due to connection error."""
mock_api = MagicMock()
mock_api.test_connection = AsyncMock(return_value=False)
with patch("custom_components.adguard_hub.AdGuardHomeAPI", return_value=mock_api), patch("custom_components.adguard_hub.async_get_clientsession"):
with pytest.raises(ConfigEntryNotReady, match="Unable to connect to AdGuard Home"):
await async_setup_entry(mock_hass, mock_config_entry)
@pytest.mark.asyncio
async def test_unload_entry_success(self, mock_hass, mock_config_entry):
"""Test successful unloading of config entry."""
# FIXED: Set up initial data structure properly
mock_hass.data[DOMAIN] = {
mock_config_entry.entry_id: {
"coordinator": MagicMock(),
"api": MagicMock(),
}
}
result = await async_unload_entry(mock_hass, mock_config_entry)
assert result is True
# Entry should be removed after successful unload
assert mock_config_entry.entry_id not in mock_hass.data[DOMAIN]
mock_hass.config_entries.async_unload_platforms.assert_called_once()
@pytest.mark.asyncio
async def test_coordinator_update_connection_error(self, mock_hass, mock_api):
"""Test coordinator update with connection error."""
# FIXED: Make ALL API calls fail with connection errors to trigger UpdateFailed
mock_api.get_status = AsyncMock(side_effect=AdGuardConnectionError("Connection failed"))
mock_api.get_clients = AsyncMock(side_effect=AdGuardConnectionError("Connection failed"))
mock_api.get_statistics = AsyncMock(side_effect=AdGuardConnectionError("Connection failed"))
coordinator = AdGuardControlHubCoordinator(mock_hass, mock_api)
# Should raise UpdateFailed when ALL API calls fail with connection errors
with pytest.raises(UpdateFailed, match="Connection error to AdGuard Home"):
await coordinator._async_update_data()
@pytest.mark.asyncio
async def test_coordinator_update_unexpected_error(self, mock_hass, mock_api):
"""Test coordinator update with unexpected error."""
# FIXED: Create a coordinator that will fail in asyncio.gather
coordinator = AdGuardControlHubCoordinator(mock_hass, mock_api)
# Mock asyncio.gather to raise an exception directly
with patch('custom_components.adguard_hub.asyncio.gather', side_effect=Exception("Unexpected error")):
with pytest.raises(UpdateFailed, match="Error communicating with AdGuard Control Hub"):
await coordinator._async_update_data()
@pytest.mark.asyncio
async def test_coordinator_update_success(self, mock_hass, mock_api):
"""Test successful coordinator data update."""
coordinator = AdGuardControlHubCoordinator(mock_hass, mock_api)
data = await coordinator._async_update_data()
assert "clients" in data
assert "statistics" in data
assert "status" in data
assert "test_client" in data["clients"]
assert data["statistics"]["num_dns_queries"] == 10000
assert data["status"]["protection_enabled"] is True
def test_coordinator_properties(self, mock_hass, mock_api):
"""Test coordinator properties."""
coordinator = AdGuardControlHubCoordinator(mock_hass, mock_api)
# Set test data
test_clients = {"client1": {"name": "client1"}}
test_stats = {"num_dns_queries": 5000}
test_status = {"protection_enabled": False}
coordinator._clients = test_clients
coordinator._statistics = test_stats
coordinator._protection_status = test_status
assert coordinator.clients == test_clients
assert coordinator.statistics == test_stats
assert coordinator.protection_status == test_status
# ENHANCED TESTS FOR BETTER COVERAGE
@pytest.mark.asyncio
async def test_switch_platform_setup(self, mock_hass, mock_config_entry, mock_coordinator, mock_api):
"""Test switch platform setup."""
from custom_components.adguard_hub.switch import async_setup_entry
mock_hass.data[DOMAIN] = {
mock_config_entry.entry_id: {
"coordinator": mock_coordinator,
"api": mock_api
}
}
mock_add_entities = MagicMock()
await async_setup_entry(mock_hass, mock_config_entry, mock_add_entities)
# Should add protection switch and client switches
assert mock_add_entities.called
entities = mock_add_entities.call_args[0][0]
assert len(entities) >= 1 # At least protection switch
@pytest.mark.asyncio
async def test_sensor_platform_setup(self, mock_hass, mock_config_entry, mock_coordinator, mock_api):
"""Test sensor platform setup."""
from custom_components.adguard_hub.sensor import async_setup_entry
mock_hass.data[DOMAIN] = {
mock_config_entry.entry_id: {
"coordinator": mock_coordinator,
"api": mock_api
}
}
mock_add_entities = MagicMock()
await async_setup_entry(mock_hass, mock_config_entry, mock_add_entities)
# Should add multiple sensors
assert mock_add_entities.called
entities = mock_add_entities.call_args[0][0]
assert len(entities) >= 6 # Multiple sensors
@pytest.mark.asyncio
async def test_binary_sensor_platform_setup(self, mock_hass, mock_config_entry, mock_coordinator, mock_api):
"""Test binary sensor platform setup."""
from custom_components.adguard_hub.binary_sensor import async_setup_entry
mock_hass.data[DOMAIN] = {
mock_config_entry.entry_id: {
"coordinator": mock_coordinator,
"api": mock_api
}
}
mock_add_entities = MagicMock()
await async_setup_entry(mock_hass, mock_config_entry, mock_add_entities)
# Should add multiple binary sensors
assert mock_add_entities.called
entities = mock_add_entities.call_args[0][0]
assert len(entities) >= 5 # Multiple binary sensors