| """
|
| Unit tests for async API client
|
| Test async HTTP operations, retry logic, and error handling
|
| """
|
|
|
| import pytest
|
| import aiohttp
|
| from unittest.mock import AsyncMock, patch, MagicMock
|
| import asyncio
|
|
|
| import sys
|
| from pathlib import Path
|
| sys.path.insert(0, str(Path(__file__).parent.parent))
|
|
|
| from utils.async_api_client import AsyncAPIClient, safe_api_call, parallel_api_calls
|
|
|
|
|
| @pytest.mark.asyncio
|
| class TestAsyncAPIClient:
|
| """Test AsyncAPIClient class"""
|
|
|
| async def test_client_initialization(self):
|
| """Test client initialization with context manager"""
|
| async with AsyncAPIClient() as client:
|
| assert client._session is not None
|
| assert isinstance(client._session, aiohttp.ClientSession)
|
|
|
| async def test_successful_get_request(self):
|
| """Test successful GET request"""
|
| mock_response_data = {"status": "success", "data": "test"}
|
|
|
| async with AsyncAPIClient() as client:
|
| with patch.object(
|
| client._session,
|
| 'get',
|
| return_value=AsyncMock(
|
| json=AsyncMock(return_value=mock_response_data),
|
| raise_for_status=MagicMock(),
|
| __aenter__=AsyncMock(),
|
| __aexit__=AsyncMock()
|
| )
|
| ):
|
| result = await client.get("https://api.example.com/data")
|
|
|
|
|
| async def test_retry_on_timeout(self):
|
| """Test retry logic on timeout"""
|
| async with AsyncAPIClient(max_retries=3, retry_delay=0.1) as client:
|
|
|
| client._session.get = AsyncMock(side_effect=asyncio.TimeoutError())
|
|
|
| result = await client.get("https://api.example.com/data")
|
|
|
|
|
| assert result is None
|
|
|
| assert client._session.get.call_count == 3
|
|
|
| async def test_retry_on_server_error(self):
|
| """Test retry on 5xx server errors"""
|
|
|
| pass
|
|
|
| async def test_no_retry_on_client_error(self):
|
| """Test that client errors (4xx) don't trigger retries"""
|
|
|
| pass
|
|
|
| async def test_parallel_requests(self):
|
| """Test parallel request execution"""
|
| urls = [
|
| "https://api.example.com/endpoint1",
|
| "https://api.example.com/endpoint2",
|
| "https://api.example.com/endpoint3"
|
| ]
|
|
|
| async with AsyncAPIClient() as client:
|
|
|
| mock_data = [{"id": i} for i in range(len(urls))]
|
|
|
|
|
|
|
| pass
|
|
|
|
|
| @pytest.mark.asyncio
|
| class TestConvenienceFunctions:
|
| """Test convenience functions"""
|
|
|
| async def test_safe_api_call(self):
|
| """Test safe_api_call convenience function"""
|
|
|
| with patch('utils.async_api_client.AsyncAPIClient') as MockClient:
|
| mock_instance = MockClient.return_value.__aenter__.return_value
|
| mock_instance.get = AsyncMock(return_value={"success": True})
|
|
|
| result = await safe_api_call("https://api.example.com/test")
|
|
|
|
|
| async def test_parallel_api_calls(self):
|
| """Test parallel_api_calls convenience function"""
|
| urls = ["https://api.example.com/1", "https://api.example.com/2"]
|
|
|
| with patch('utils.async_api_client.AsyncAPIClient') as MockClient:
|
| mock_instance = MockClient.return_value.__aenter__.return_value
|
| mock_instance.gather_requests = AsyncMock(
|
| return_value=[{"id": 1}, {"id": 2}]
|
| )
|
|
|
| results = await parallel_api_calls(urls)
|
|
|
|
|
|
|
| if __name__ == '__main__':
|
| pytest.main([__file__, '-v'])
|
|
|