Files
adguard-control-hub/tests/test_api.py

289 lines
10 KiB
Python

"""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()