195 lines
5.9 KiB
Python
195 lines
5.9 KiB
Python
"""Tests for HttpClientManager."""
|
|
import pytest
|
|
from unittest.mock import MagicMock, AsyncMock, patch
|
|
|
|
|
|
class TestHttpClientManager:
|
|
"""Tests for HTTP client pool management."""
|
|
|
|
@pytest.mark.unit
|
|
def test_manager_initializes_with_settings(self):
|
|
"""Manager should initialize with config settings."""
|
|
from app.http_client import HttpClientManager
|
|
from app.config import Settings
|
|
|
|
settings = Settings(
|
|
http_max_connections=50,
|
|
http_connect_timeout=15.0,
|
|
)
|
|
manager = HttpClientManager(settings)
|
|
|
|
assert manager.max_connections == 50
|
|
assert manager.connect_timeout == 15.0
|
|
assert manager._default_client is None # Not started yet
|
|
|
|
@pytest.mark.asyncio
|
|
@pytest.mark.unit
|
|
async def test_startup_creates_client(self):
|
|
"""Startup should create the default async client."""
|
|
from app.http_client import HttpClientManager
|
|
from app.config import Settings
|
|
|
|
settings = Settings()
|
|
manager = HttpClientManager(settings)
|
|
|
|
await manager.startup()
|
|
|
|
assert manager._default_client is not None
|
|
|
|
await manager.shutdown()
|
|
|
|
@pytest.mark.asyncio
|
|
@pytest.mark.unit
|
|
async def test_shutdown_closes_client(self):
|
|
"""Shutdown should close all clients gracefully."""
|
|
from app.http_client import HttpClientManager
|
|
from app.config import Settings
|
|
|
|
settings = Settings()
|
|
manager = HttpClientManager(settings)
|
|
|
|
await manager.startup()
|
|
client = manager._default_client
|
|
|
|
await manager.shutdown()
|
|
|
|
assert manager._default_client is None
|
|
assert client.is_closed
|
|
|
|
@pytest.mark.asyncio
|
|
@pytest.mark.unit
|
|
async def test_get_client_returns_default(self):
|
|
"""get_client() should return the default client."""
|
|
from app.http_client import HttpClientManager
|
|
from app.config import Settings
|
|
|
|
settings = Settings()
|
|
manager = HttpClientManager(settings)
|
|
await manager.startup()
|
|
|
|
client = manager.get_client()
|
|
|
|
assert client is manager._default_client
|
|
|
|
await manager.shutdown()
|
|
|
|
@pytest.mark.asyncio
|
|
@pytest.mark.unit
|
|
async def test_get_client_raises_if_not_started(self):
|
|
"""get_client() should raise RuntimeError if manager not started."""
|
|
from app.http_client import HttpClientManager
|
|
from app.config import Settings
|
|
|
|
settings = Settings()
|
|
manager = HttpClientManager(settings)
|
|
|
|
with pytest.raises(RuntimeError, match="not started"):
|
|
manager.get_client()
|
|
|
|
@pytest.mark.asyncio
|
|
@pytest.mark.unit
|
|
async def test_run_blocking_executes_in_thread_pool(self):
|
|
"""run_blocking should execute sync functions in thread pool."""
|
|
from app.http_client import HttpClientManager
|
|
from app.config import Settings
|
|
import threading
|
|
|
|
settings = Settings()
|
|
manager = HttpClientManager(settings)
|
|
await manager.startup()
|
|
|
|
main_thread = threading.current_thread()
|
|
execution_thread = None
|
|
|
|
def blocking_func():
|
|
nonlocal execution_thread
|
|
execution_thread = threading.current_thread()
|
|
return "result"
|
|
|
|
result = await manager.run_blocking(blocking_func)
|
|
|
|
assert result == "result"
|
|
assert execution_thread is not main_thread
|
|
|
|
await manager.shutdown()
|
|
|
|
@pytest.mark.asyncio
|
|
@pytest.mark.unit
|
|
async def test_run_blocking_raises_if_not_started(self):
|
|
"""run_blocking should raise RuntimeError if manager not started."""
|
|
from app.http_client import HttpClientManager
|
|
from app.config import Settings
|
|
|
|
settings = Settings()
|
|
manager = HttpClientManager(settings)
|
|
|
|
with pytest.raises(RuntimeError, match="not started"):
|
|
await manager.run_blocking(lambda: None)
|
|
|
|
@pytest.mark.asyncio
|
|
@pytest.mark.unit
|
|
async def test_startup_idempotent(self):
|
|
"""Calling startup multiple times should be safe."""
|
|
from app.http_client import HttpClientManager
|
|
from app.config import Settings
|
|
|
|
settings = Settings()
|
|
manager = HttpClientManager(settings)
|
|
|
|
await manager.startup()
|
|
client1 = manager._default_client
|
|
|
|
await manager.startup() # Should not create a new client
|
|
client2 = manager._default_client
|
|
|
|
assert client1 is client2 # Same client instance
|
|
|
|
await manager.shutdown()
|
|
|
|
@pytest.mark.asyncio
|
|
@pytest.mark.unit
|
|
async def test_shutdown_idempotent(self):
|
|
"""Calling shutdown multiple times should be safe."""
|
|
from app.http_client import HttpClientManager
|
|
from app.config import Settings
|
|
|
|
settings = Settings()
|
|
manager = HttpClientManager(settings)
|
|
|
|
await manager.startup()
|
|
await manager.shutdown()
|
|
await manager.shutdown() # Should not raise
|
|
|
|
assert manager._default_client is None
|
|
|
|
@pytest.mark.asyncio
|
|
@pytest.mark.unit
|
|
async def test_properties_return_configured_values(self):
|
|
"""Properties should return configured values."""
|
|
from app.http_client import HttpClientManager
|
|
from app.config import Settings
|
|
|
|
settings = Settings(
|
|
http_max_connections=75,
|
|
http_worker_threads=16,
|
|
)
|
|
manager = HttpClientManager(settings)
|
|
await manager.startup()
|
|
|
|
assert manager.pool_size == 75
|
|
assert manager.executor_max == 16
|
|
|
|
await manager.shutdown()
|
|
|
|
@pytest.mark.asyncio
|
|
@pytest.mark.unit
|
|
async def test_active_connections_when_not_started(self):
|
|
"""active_connections should return 0 when not started."""
|
|
from app.http_client import HttpClientManager
|
|
from app.config import Settings
|
|
|
|
settings = Settings()
|
|
manager = HttpClientManager(settings)
|
|
|
|
assert manager.active_connections == 0
|