"""Test the complete AdGuard Control Hub integration.""" import pytest from unittest.mock import AsyncMock, MagicMock, patch from homeassistant.core import HomeAssistant from homeassistant.config_entries import ConfigEntry, SOURCE_USER from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_PORT, CONF_USERNAME from custom_components.adguard_hub import async_setup_entry, async_unload_entry from custom_components.adguard_hub.api import AdGuardHomeAPI from custom_components.adguard_hub.const import DOMAIN @pytest.fixture def mock_config_entry(): """Mock config entry compatible with HA 2025.9.4.""" return ConfigEntry( version=1, minor_version=1, domain=DOMAIN, title="Test AdGuard", data={ CONF_HOST: "192.168.1.100", CONF_PORT: 3000, CONF_USERNAME: "admin", CONF_PASSWORD: "password", }, options={}, source=SOURCE_USER, entry_id="test_entry_id", unique_id="192.168.1.100:3000", discovery_keys=set(), subentries_data={}, ) @pytest.fixture def mock_api(): """Mock API instance.""" api = MagicMock(spec=AdGuardHomeAPI) api.host = "192.168.1.100" api.port = 3000 api.test_connection = AsyncMock(return_value=True) api.get_status = AsyncMock(return_value={ "protection_enabled": True, "version": "v0.107.0", "dns_port": 53, "running": True, }) api.get_clients = AsyncMock(return_value={ "clients": [ { "name": "test_client", "ids": ["192.168.1.50"], "filtering_enabled": True, "blocked_services": {"ids": ["youtube"]}, } ] }) api.get_statistics = AsyncMock(return_value={ "num_dns_queries": 1000, "num_blocked_filtering": 100, "num_dns_queries_today": 500, "num_blocked_filtering_today": 50, "filtering_rules_count": 50000, "avg_processing_time": 2.5, }) return api @pytest.mark.asyncio async def test_setup_entry_success(hass: HomeAssistant, 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"), \ patch.object(hass.config_entries, "async_forward_entry_setups", return_value=True): result = await async_setup_entry(hass, mock_config_entry) assert result is True assert DOMAIN in hass.data assert mock_config_entry.entry_id in hass.data[DOMAIN] assert "coordinator" in hass.data[DOMAIN][mock_config_entry.entry_id] assert "api" in hass.data[DOMAIN][mock_config_entry.entry_id] @pytest.mark.asyncio async def test_setup_entry_connection_failure(hass: HomeAssistant, mock_config_entry): """Test setup failure due to connection error.""" mock_api = MagicMock(spec=AdGuardHomeAPI) 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(Exception): # Should raise ConfigEntryNotReady await async_setup_entry(hass, mock_config_entry) @pytest.mark.asyncio async def test_unload_entry(hass: HomeAssistant, mock_config_entry): """Test unloading of config entry.""" # Set up initial data hass.data[DOMAIN] = { mock_config_entry.entry_id: { "coordinator": MagicMock(), "api": MagicMock(), } } with patch.object(hass.config_entries, "async_unload_platforms", return_value=True): result = await async_unload_entry(hass, mock_config_entry) assert result is True assert mock_config_entry.entry_id not in hass.data[DOMAIN] @pytest.mark.asyncio async def test_coordinator_data_update(hass: HomeAssistant, mock_api): """Test coordinator data update functionality.""" from custom_components.adguard_hub import AdGuardControlHubCoordinator coordinator = AdGuardControlHubCoordinator(hass, mock_api) # Test successful data update 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"] == 1000 assert data["status"]["protection_enabled"] is True @pytest.mark.asyncio async def test_api_error_handling(mock_api): """Test API error handling.""" from custom_components.adguard_hub.api import AdGuardConnectionError, AdGuardAuthError # Test connection error mock_api.get_status = AsyncMock(side_effect=AdGuardConnectionError("Connection failed")) with pytest.raises(AdGuardConnectionError): await mock_api.get_status() # Test auth error mock_api.get_clients = AsyncMock(side_effect=AdGuardAuthError("Auth failed")) with pytest.raises(AdGuardAuthError): await mock_api.get_clients() def test_services_registration(hass: HomeAssistant): """Test that services are properly registered.""" from custom_components.adguard_hub.services import AdGuardControlHubServices # Create services without async context services = AdGuardControlHubServices(hass) services.register_services() # Check that services are registered assert hass.services.has_service(DOMAIN, "block_services") assert hass.services.has_service(DOMAIN, "unblock_services") assert hass.services.has_service(DOMAIN, "emergency_unblock") assert hass.services.has_service(DOMAIN, "add_client") assert hass.services.has_service(DOMAIN, "remove_client") assert hass.services.has_service(DOMAIN, "bulk_update_clients") # Clean up services.unregister_services() def test_blocked_services_constants(): """Test that blocked services are properly defined.""" from custom_components.adguard_hub.const import BLOCKED_SERVICES assert "youtube" in BLOCKED_SERVICES assert "netflix" in BLOCKED_SERVICES assert "gaming" in BLOCKED_SERVICES assert "facebook" in BLOCKED_SERVICES # Check friendly names are defined assert BLOCKED_SERVICES["youtube"] == "YouTube" assert BLOCKED_SERVICES["netflix"] == "Netflix" def test_api_endpoints(): """Test that API endpoints are properly defined.""" from custom_components.adguard_hub.const import API_ENDPOINTS required_endpoints = [ "status", "clients", "stats", "protection", "clients_add", "clients_update", "clients_delete" ] for endpoint in required_endpoints: assert endpoint in API_ENDPOINTS assert API_ENDPOINTS[endpoint].startswith("/") @pytest.mark.asyncio async def test_client_operations(mock_api): """Test client add/update/delete operations.""" # Test add client client_data = { "name": "new_client", "ids": ["192.168.1.200"], "filtering_enabled": True, } mock_api.add_client = AsyncMock(return_value={"success": True}) result = await mock_api.add_client(client_data) assert result["success"] is True # Test update client update_data = { "name": "new_client", "data": {"filtering_enabled": False} } mock_api.update_client = AsyncMock(return_value={"success": True}) result = await mock_api.update_client(update_data) assert result["success"] is True # Test delete client mock_api.delete_client = AsyncMock(return_value={"success": True}) result = await mock_api.delete_client("new_client") assert result["success"] is True