"""Tests for async logger service.""" import pytest import asyncio from unittest.mock import patch, AsyncMock, MagicMock from app.services.logger import AsyncLogger, LogEntry, async_logger class TestLogEntry: """Test LogEntry dataclass.""" def test_create_log_entry(self): """Test creating a LogEntry.""" entry = LogEntry( client_key_id=1, provider_id=2, model="gpt-4o", prompt_tokens=10, completion_tokens=20, latency_ms=100, success=True ) assert entry.client_key_id == 1 assert entry.model == "gpt-4o" assert entry.success is True def test_log_entry_with_error(self): """Test LogEntry with error.""" entry = LogEntry( client_key_id=1, provider_id=2, model="gpt-4o", success=False, error_message="Rate limit exceeded" ) assert entry.success is False assert entry.error_message == "Rate limit exceeded" class TestAsyncLogger: """Test AsyncLogger.""" @pytest.mark.asyncio async def test_start_creates_task(self): """Test start creates background task.""" logger = AsyncLogger() assert logger._task is None await logger.start() assert logger._task is not None await logger.stop() @pytest.mark.asyncio async def test_stop_cancels_task(self): """Test stop cancels background task.""" logger = AsyncLogger() await logger.start() assert logger._task is not None await logger.stop() assert logger._task is None @pytest.mark.asyncio async def test_log_queues_entry(self): """Test log queues an entry.""" logger = AsyncLogger() entry = LogEntry( client_key_id=1, provider_id=2, model="gpt-4o", success=True ) await logger.log(entry) assert not logger._queue.empty() @pytest.mark.asyncio async def test_flush_batch_empty_queue(self, mock_db): """Test flush with empty queue.""" logger = AsyncLogger() with patch("app.services.logger.db_connection", mock_db): await logger._flush_batch() # Should not raise @pytest.mark.asyncio async def test_flush_batch_with_entries(self, mock_db): """Test flush with entries in queue.""" logger = AsyncLogger() entry = LogEntry( client_key_id=1, provider_id=2, model="gpt-4o", prompt_tokens=10, completion_tokens=20, latency_ms=100, success=True ) await logger.log(entry) with patch("app.services.logger.db_connection", mock_db): await logger._flush_batch() assert logger._queue.empty() @pytest.mark.asyncio async def test_flush_all(self, mock_db): """Test flush_all drains all entries.""" logger = AsyncLogger() # Add multiple entries for i in range(5): entry = LogEntry( client_key_id=1, provider_id=2, model=f"model-{i}", success=True ) await logger.log(entry) with patch("app.services.logger.db_connection", mock_db): await logger._flush_all() assert logger._queue.empty() @pytest.mark.asyncio async def test_start_stop_cycle(self): """Test full start/stop cycle.""" logger = AsyncLogger() await logger.start() await asyncio.sleep(0.1) # Let task run briefly await logger.stop() assert logger._task is None @pytest.mark.asyncio async def test_multiple_start_calls_safe(self): """Test multiple start calls are safe.""" logger = AsyncLogger() await logger.start() first_task = logger._task await logger.start() # Should not create new task assert logger._task is first_task await logger.stop() @pytest.mark.asyncio async def test_stop_without_start_safe(self): """Test stop without start is safe.""" logger = AsyncLogger() await logger.stop() # Should not raise def test_global_instance_exists(self): """Test global async_logger instance exists.""" assert async_logger is not None assert isinstance(async_logger, AsyncLogger)