| | """ |
| | 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']) |
| |
|