"""Test API functionality.""" import pytest from unittest.mock import AsyncMock, MagicMock, patch from aiohttp import ClientError, ClientTimeout from custom_components.adguard_hub.api import ( AdGuardHomeAPI, AdGuardHomeError, AdGuardConnectionError, AdGuardAuthError, AdGuardNotFoundError, AdGuardTimeoutError, ) class TestAdGuardHomeAPI: """Test the AdGuard Home API wrapper.""" def test_api_initialization(self): """Test API initialization.""" api = AdGuardHomeAPI( host="192.168.1.100", port=3000, username="admin", password="password", ssl=True, ) assert api.host == "192.168.1.100" assert api.port == 3000 assert api.username == "admin" assert api.password == "password" assert api.ssl is True assert api.base_url == "https://192.168.1.100:3000" def test_api_initialization_defaults(self): """Test API initialization with defaults.""" api = AdGuardHomeAPI(host="192.168.1.100") assert api.host == "192.168.1.100" assert api.port == 3000 assert api.username is None assert api.password is None assert api.ssl is False assert api.base_url == "http://192.168.1.100:3000" @pytest.mark.asyncio async def test_api_context_manager(self): """Test API as async context manager.""" async with AdGuardHomeAPI(host="192.168.1.100", port=3000) as api: assert api is not None assert api.host == "192.168.1.100" assert api.port == 3000 @pytest.mark.asyncio async def test_test_connection_success(self, mock_aiohttp_session): """Test successful connection test.""" mock_aiohttp_session.request.return_value.__aenter__.return_value.json = AsyncMock( return_value={"protection_enabled": True} ) api = AdGuardHomeAPI(host="192.168.1.100", session=mock_aiohttp_session) result = await api.test_connection() assert result is True mock_aiohttp_session.request.assert_called() @pytest.mark.asyncio async def test_test_connection_failure(self, mock_aiohttp_session): """Test failed connection test.""" mock_aiohttp_session.request.side_effect = ClientError("Connection failed") api = AdGuardHomeAPI(host="192.168.1.100", session=mock_aiohttp_session) result = await api.test_connection() assert result is False @pytest.mark.asyncio async def test_get_status_success(self, mock_aiohttp_session): """Test successful status retrieval.""" expected_status = { "protection_enabled": True, "version": "v0.107.0", "running": True, } mock_aiohttp_session.request.return_value.__aenter__.return_value.json = AsyncMock( return_value=expected_status ) api = AdGuardHomeAPI(host="192.168.1.100", session=mock_aiohttp_session) status = await api.get_status() assert status == expected_status @pytest.mark.asyncio async def test_get_clients_success(self, mock_aiohttp_session): """Test successful clients retrieval.""" expected_clients = { "clients": [ {"name": "client1", "ids": ["192.168.1.50"]}, {"name": "client2", "ids": ["192.168.1.51"]}, ] } mock_aiohttp_session.request.return_value.__aenter__.return_value.json = AsyncMock( return_value=expected_clients ) api = AdGuardHomeAPI(host="192.168.1.100", session=mock_aiohttp_session) clients = await api.get_clients() assert clients == expected_clients @pytest.mark.asyncio async def test_get_statistics_success(self, mock_aiohttp_session): """Test successful statistics retrieval.""" expected_stats = { "num_dns_queries": 10000, "num_blocked_filtering": 1500, } mock_aiohttp_session.request.return_value.__aenter__.return_value.json = AsyncMock( return_value=expected_stats ) api = AdGuardHomeAPI(host="192.168.1.100", session=mock_aiohttp_session) stats = await api.get_statistics() assert stats == expected_stats @pytest.mark.asyncio async def test_set_protection_enable(self, mock_aiohttp_session): """Test enabling protection.""" mock_aiohttp_session.request.return_value.__aenter__.return_value.json = AsyncMock( return_value={"success": True} ) api = AdGuardHomeAPI(host="192.168.1.100", session=mock_aiohttp_session) result = await api.set_protection(True) assert result == {"success": True} @pytest.mark.asyncio async def test_add_client_success(self, mock_aiohttp_session): """Test successful client addition.""" client_data = { "name": "test_client", "ids": ["192.168.1.100"], } mock_aiohttp_session.request.return_value.__aenter__.return_value.json = AsyncMock( return_value={"success": True} ) api = AdGuardHomeAPI(host="192.168.1.100", session=mock_aiohttp_session) result = await api.add_client(client_data) assert result == {"success": True} @pytest.mark.asyncio async def test_add_client_missing_name(self, mock_aiohttp_session): """Test client addition with missing name.""" client_data = {"ids": ["192.168.1.100"]} api = AdGuardHomeAPI(host="192.168.1.100", session=mock_aiohttp_session) with pytest.raises(ValueError, match="Client name is required"): await api.add_client(client_data) @pytest.mark.asyncio async def test_add_client_missing_ids(self, mock_aiohttp_session): """Test client addition with missing IDs.""" client_data = {"name": "test_client"} api = AdGuardHomeAPI(host="192.168.1.100", session=mock_aiohttp_session) with pytest.raises(ValueError, match="Client IDs are required"): await api.add_client(client_data) @pytest.mark.asyncio async def test_get_client_by_name_found(self, mock_aiohttp_session): """Test finding client by name.""" clients_data = { "clients": [ {"name": "test_client", "ids": ["192.168.1.50"]}, {"name": "other_client", "ids": ["192.168.1.51"]}, ] } mock_aiohttp_session.request.return_value.__aenter__.return_value.json = AsyncMock( return_value=clients_data ) api = AdGuardHomeAPI(host="192.168.1.100", session=mock_aiohttp_session) client = await api.get_client_by_name("test_client") assert client == {"name": "test_client", "ids": ["192.168.1.50"]} @pytest.mark.asyncio async def test_get_client_by_name_not_found(self, mock_aiohttp_session): """Test client not found by name.""" clients_data = {"clients": []} mock_aiohttp_session.request.return_value.__aenter__.return_value.json = AsyncMock( return_value=clients_data ) api = AdGuardHomeAPI(host="192.168.1.100", session=mock_aiohttp_session) client = await api.get_client_by_name("nonexistent_client") assert client is None @pytest.mark.asyncio async def test_update_client_blocked_services_client_not_found(self, mock_aiohttp_session): """Test blocked services update with client not found.""" api = AdGuardHomeAPI(host="192.168.1.100", session=mock_aiohttp_session) api.get_client_by_name = AsyncMock(return_value=None) with pytest.raises(AdGuardNotFoundError, match="Client 'nonexistent' not found"): await api.update_client_blocked_services("nonexistent", ["youtube"]) @pytest.mark.asyncio async def test_auth_error_handling(self, mock_aiohttp_session): """Test 401 authentication error handling.""" mock_response = mock_aiohttp_session.request.return_value.__aenter__.return_value mock_response.status = 401 api = AdGuardHomeAPI(host="192.168.1.100", session=mock_aiohttp_session) with pytest.raises(AdGuardAuthError, match="Authentication failed"): await api.get_status() @pytest.mark.asyncio async def test_not_found_error_handling(self, mock_aiohttp_session): """Test 404 not found error handling.""" mock_response = mock_aiohttp_session.request.return_value.__aenter__.return_value mock_response.status = 404 api = AdGuardHomeAPI(host="192.168.1.100", session=mock_aiohttp_session) with pytest.raises(AdGuardNotFoundError): await api.get_status() @pytest.mark.asyncio async def test_server_error_handling(self, mock_aiohttp_session): """Test 500 server error handling.""" mock_response = mock_aiohttp_session.request.return_value.__aenter__.return_value mock_response.status = 500 api = AdGuardHomeAPI(host="192.168.1.100", session=mock_aiohttp_session) with pytest.raises(AdGuardConnectionError, match="Server error 500"): await api.get_status() @pytest.mark.asyncio async def test_client_error_handling(self, mock_aiohttp_session): """Test client error handling.""" mock_aiohttp_session.request.side_effect = ClientError("Client error") api = AdGuardHomeAPI(host="192.168.1.100", session=mock_aiohttp_session) with pytest.raises(AdGuardConnectionError, match="Client error"): await api.get_status() @pytest.mark.asyncio async def test_empty_response_handling(self, mock_aiohttp_session): """Test empty response handling.""" mock_response = mock_aiohttp_session.request.return_value.__aenter__.return_value mock_response.status = 204 mock_response.content_length = 0 api = AdGuardHomeAPI(host="192.168.1.100", session=mock_aiohttp_session) result = await api._request("POST", "/control/protection", {"enabled": True}) assert result == {} @pytest.mark.asyncio async def test_close_session(self): """Test closing API session.""" api = AdGuardHomeAPI(host="192.168.1.100") # Create session async with api: assert api._session is not None # Close session await api.close()