""" Integration tests for ChatAgent service. Tests the complete message processing flow including session management, language context switching, chat history, and LLM integration. """ import pytest import os from unittest.mock import Mock, patch, MagicMock from datetime import datetime, timedelta import redis from chat_agent.services.chat_agent import ChatAgent, ChatAgentError, create_chat_agent from chat_agent.services.groq_client import GroqClient, ChatMessage, LanguageContext from chat_agent.services.language_context import LanguageContextManager from chat_agent.services.session_manager import SessionManager, SessionNotFoundError from chat_agent.services.chat_history import ChatHistoryManager from chat_agent.models.chat_session import ChatSession from chat_agent.models.message import Message from chat_agent.models.base import db @pytest.fixture def mock_redis(): """Mock Redis client for testing.""" return Mock(spec=redis.Redis) @pytest.fixture def mock_groq_client(): """Mock Groq client for testing.""" client = Mock(spec=GroqClient) client.generate_response.return_value = "Test response from LLM" client.stream_response.return_value = iter(["Test ", "streaming ", "response"]) client.get_model_info.return_value = { "model": "mixtral-8x7b-32768", "temperature": 0.7, "max_tokens": 2048 } return client @pytest.fixture def language_context_manager(): """Create language context manager for testing.""" return LanguageContextManager() @pytest.fixture def mock_session_manager(): """Mock session manager for testing.""" manager = Mock(spec=SessionManager) # Create a mock session mock_session = Mock(spec=ChatSession) mock_session.id = "test-session-id" mock_session.user_id = "test-user-id" mock_session.language = "python" mock_session.created_at = datetime.utcnow() mock_session.last_active = datetime.utcnow() mock_session.message_count = 0 mock_session.is_active = True mock_session.session_metadata = {} manager.get_session.return_value = mock_session manager.update_session_activity.return_value = None manager.increment_message_count.return_value = None manager.set_session_language.return_value = None return manager @pytest.fixture def mock_chat_history_manager(): """Mock chat history manager for testing.""" manager = Mock(spec=ChatHistoryManager) # Create mock messages mock_user_message = Mock(spec=Message) mock_user_message.id = "user-msg-id" mock_user_message.session_id = "test-session-id" mock_user_message.role = "user" mock_user_message.content = "Test user message" mock_user_message.language = "python" mock_user_message.timestamp = datetime.utcnow() mock_user_message.message_metadata = {} mock_assistant_message = Mock(spec=Message) mock_assistant_message.id = "assistant-msg-id" mock_assistant_message.session_id = "test-session-id" mock_assistant_message.role = "assistant" mock_assistant_message.content = "Test assistant response" mock_assistant_message.language = "python" mock_assistant_message.timestamp = datetime.utcnow() mock_assistant_message.message_metadata = {} manager.store_message.side_effect = [mock_user_message, mock_assistant_message] manager.get_recent_history.return_value = [mock_user_message] manager.get_message_count.return_value = 2 manager.get_cache_stats.return_value = { 'session_id': 'test-session-id', 'cached_messages': 2, 'cache_ttl': 3600, 'max_cache_size': 20 } return manager @pyte st.fixture def chat_agent(mock_groq_client, language_context_manager, mock_session_manager, mock_chat_history_manager): """Create ChatAgent instance for testing.""" return ChatAgent( groq_client=mock_groq_client, language_context_manager=language_context_manager, session_manager=mock_session_manager, chat_history_manager=mock_chat_history_manager ) class TestChatAgentMessageProcessing: """Test complete message processing workflow.""" def test_process_message_success(self, chat_agent, mock_groq_client, mock_session_manager, mock_chat_history_manager): """Test successful message processing flow.""" # Arrange session_id = "test-session-id" message = "How do I create a list in Python?" # Act result = chat_agent.process_message(session_id, message) # Assert assert result['response'] == "Test response from LLM" assert result['language'] == "python" assert result['session_id'] == session_id assert 'message_id' in result assert 'timestamp' in result assert 'metadata' in result # Verify service calls mock_session_manager.get_session.assert_called_once_with(session_id) mock_session_manager.update_session_activity.assert_called_once_with(session_id) mock_session_manager.increment_message_count.assert_called_once_with(session_id) # Verify message storage (user message, then assistant message) assert mock_chat_history_manager.store_message.call_count == 2 # Verify LLM call mock_groq_client.generate_response.assert_called_once() def test_process_message_with_language_override(self, chat_agent, mock_groq_client, mock_session_manager): """Test message processing with language override.""" # Arrange session_id = "test-session-id" message = "How do I create an array in JavaScript?" language = "javascript" # Act result = chat_agent.process_message(session_id, message, language) # Assert assert result['language'] == "javascript" mock_session_manager.set_session_language.assert_called_once_with(session_id, language) def test_process_message_invalid_session(self, chat_agent, mock_session_manager): """Test message processing with invalid session.""" # Arrange mock_session_manager.get_session.side_effect = SessionNotFoundError("Session not found") # Act & Assert with pytest.raises(ChatAgentError, match="Session error"): chat_agent.process_message("invalid-session", "test message") def test_process_message_invalid_language(self, chat_agent, mock_session_manager): """Test message processing with invalid language falls back to session default.""" # Arrange session_id = "test-session-id" message = "Test message" invalid_language = "invalid-lang" # Act result = chat_agent.process_message(session_id, message, invalid_language) # Assert - should fall back to session default (python) assert result['language'] == "python" class TestChatAgentLanguageSwitching: """Test language switching functionality.""" def test_switch_language_success(self, chat_agent, mock_session_manager, mock_chat_history_manager): """Test successful language switching.""" # Arrange session_id = "test-session-id" new_language = "javascript" # Act result = chat_agent.switch_language(session_id, new_language) # Assert assert result['success'] is True assert result['new_language'] == new_language assert result['previous_language'] == "python" assert result['session_id'] == session_id assert 'message' in result assert 'timestamp' in result # Verify service calls mock_session_manager.get_session.assert_called_once_with(session_id) mock_session_manager.set_session_language.assert_called_once_with(session_id, new_language) # Verify switch message is stored mock_chat_history_manager.store_message.assert_called_once() store_call = mock_chat_history_manager.store_message.call_args assert store_call[1]['role'] == 'assistant' assert store_call[1]['language'] == new_language assert 'language_switch' in store_call[1]['message_metadata']['type'] def test_switch_language_invalid_language(self, chat_agent): """Test language switching with invalid language.""" # Arrange session_id = "test-session-id" invalid_language = "invalid-lang" # Act & Assert with pytest.raises(ChatAgentError, match="Unsupported language"): chat_agent.switch_language(session_id, invalid_language) def test_switch_language_invalid_session(self, chat_agent, mock_session_manager): """Test language switching with invalid session.""" # Arrange mock_session_manager.get_session.side_effect = SessionNotFoundError("Session not found") # Act & Assert with pytest.raises(ChatAgentError, match="Session error"): chat_agent.switch_language("invalid-session", "javascript") cla ss TestChatAgentStreaming: """Test streaming response functionality.""" def test_stream_response_success(self, chat_agent, mock_groq_client, mock_session_manager, mock_chat_history_manager): """Test successful streaming response.""" # Arrange session_id = "test-session-id" message = "Explain Python functions" # Act stream_results = list(chat_agent.stream_response(session_id, message)) # Assert assert len(stream_results) >= 5 # start + 3 chunks + complete # Check start event start_event = stream_results[0] assert start_event['type'] == 'start' assert start_event['session_id'] == session_id assert start_event['language'] == 'python' # Check chunk events chunk_events = [event for event in stream_results if event['type'] == 'chunk'] assert len(chunk_events) == 3 assert chunk_events[0]['content'] == "Test " assert chunk_events[1]['content'] == "streaming " assert chunk_events[2]['content'] == "response" # Check complete event complete_event = stream_results[-1] assert complete_event['type'] == 'complete' assert complete_event['session_id'] == session_id assert complete_event['total_chunks'] == 3 assert 'processing_time' in complete_event # Verify service calls mock_session_manager.get_session.assert_called_once_with(session_id) mock_session_manager.increment_message_count.assert_called_once_with(session_id) # Verify message storage assert mock_chat_history_manager.store_message.call_count == 2 def test_stream_response_error(self, chat_agent, mock_session_manager): """Test streaming response with session error.""" # Arrange mock_session_manager.get_session.side_effect = SessionNotFoundError("Session not found") # Act stream_results = list(chat_agent.stream_response("invalid-session", "test message")) # Assert assert len(stream_results) == 1 error_event = stream_results[0] assert error_event['type'] == 'error' assert 'Session error' in error_event['error'] class TestChatAgentHistory: """Test chat history retrieval functionality.""" def test_get_chat_history_success(self, chat_agent, mock_session_manager, mock_chat_history_manager): """Test successful chat history retrieval.""" # Arrange session_id = "test-session-id" limit = 5 # Act history = chat_agent.get_chat_history(session_id, limit) # Assert assert isinstance(history, list) assert len(history) == 1 # Based on mock setup message = history[0] assert 'id' in message assert 'role' in message assert 'content' in message assert 'language' in message assert 'timestamp' in message assert 'metadata' in message # Verify service calls mock_session_manager.get_session.assert_called_once_with(session_id) mock_chat_history_manager.get_recent_history.assert_called_once_with(session_id, limit) def test_get_chat_history_invalid_session(self, chat_agent, mock_session_manager): """Test chat history retrieval with invalid session.""" # Arrange mock_session_manager.get_session.side_effect = SessionNotFoundError("Session not found") # Act & Assert with pytest.raises(ChatAgentError, match="Session error"): chat_agent.get_chat_history("invalid-session") class TestChatAgentSessionInfo: """Test session information retrieval.""" def test_get_session_info_success(self, chat_agent, mock_session_manager, mock_chat_history_manager, language_context_manager): """Test successful session info retrieval.""" # Arrange session_id = "test-session-id" # Act info = chat_agent.get_session_info(session_id) # Assert assert 'session' in info assert 'language_context' in info assert 'statistics' in info assert 'supported_languages' in info # Check session info session_info = info['session'] assert session_info['id'] == session_id assert session_info['language'] == 'python' assert session_info['is_active'] is True # Check statistics stats = info['statistics'] assert 'total_messages' in stats assert 'session_message_count' in stats assert 'cache_stats' in stats # Check supported languages assert isinstance(info['supported_languages'], list) assert 'python' in info['supported_languages'] assert 'javascript' in info['supported_languages'] # Verify service calls mock_session_manager.get_session.assert_called_once_with(session_id) mock_chat_history_manager.get_message_count.assert_called_once_with(session_id) mock_chat_history_manager.get_cache_stats.assert_called_once_with(session_id) class TestChatAgentFactory: """Test ChatAgent factory function.""" def test_create_chat_agent(self, mock_groq_client, language_context_manager, mock_session_manager, mock_chat_history_manager): """Test ChatAgent factory function.""" # Act agent = create_chat_agent( mock_groq_client, language_context_manager, mock_session_manager, mock_chat_history_manager ) # Assert assert isinstance(agent, ChatAgent) assert agent.groq_client == mock_groq_client assert agent.language_context_manager == language_context_manager assert agent.session_manager == mock_session_manager assert agent.chat_history_manager == mock_chat_history_manager class TestChatAgentIntegrationFlow: """Test complete integration flows.""" def test_complete_conversation_flow(self, chat_agent, mock_groq_client, mock_session_manager, mock_chat_history_manager): """Test a complete conversation flow with multiple messages.""" session_id = "test-session-id" # First message result1 = chat_agent.process_message(session_id, "What is Python?") assert result1['language'] == 'python' # Switch language switch_result = chat_agent.switch_language(session_id, "javascript") assert switch_result['success'] is True assert switch_result['new_language'] == 'javascript' # Second message in new language result2 = chat_agent.process_message(session_id, "What is JavaScript?") assert result2['language'] == 'javascript' # Get session info info = chat_agent.get_session_info(session_id) assert info['session']['language'] == 'javascript' # Verify all service interactions occurred assert mock_session_manager.get_session.call_count >= 3 assert mock_session_manager.set_session_language.call_count >= 1 assert mock_chat_history_manager.store_message.call_count >= 5 # 2 conversations + 1 switch message def test_streaming_with_language_switch(self, chat_agent, mock_session_manager, mock_chat_history_manager): """Test streaming response after language switch.""" session_id = "test-session-id" # Switch language first chat_agent.switch_language(session_id, "java") # Stream response in new language stream_results = list(chat_agent.stream_response(session_id, "Explain Java classes", "java")) # Verify language context is maintained start_event = stream_results[0] assert start_event['language'] == 'java' # Verify session language was set mock_session_manager.set_session_language.assert_called_with(session_id, "java")