""" Integration tests for Programming Assistance Features Tests the integration of programming assistance features with the chat agent, including end-to-end workflows for code explanation, debugging, error analysis, code review, and beginner help. """ import unittest from unittest.mock import Mock, patch, MagicMock import pytest from datetime import datetime from chat_agent.services.chat_agent import ChatAgent from chat_agent.services.programming_assistance import ProgrammingAssistanceService, AssistanceType 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 from chat_agent.services.chat_history import ChatHistoryManager from chat_agent.models.message import Message from chat_agent.models.chat_session import ChatSession class TestProgrammingAssistanceIntegration(unittest.TestCase): """Integration test cases for programming assistance features.""" def setUp(self): """Set up test fixtures with mocked dependencies.""" # Mock dependencies self.mock_groq_client = Mock(spec=GroqClient) self.mock_language_context_manager = Mock(spec=LanguageContextManager) self.mock_session_manager = Mock(spec=SessionManager) self.mock_chat_history_manager = Mock(spec=ChatHistoryManager) # Create real programming assistance service self.programming_assistance_service = ProgrammingAssistanceService() # Create chat agent with mocked dependencies self.chat_agent = ChatAgent( groq_client=self.mock_groq_client, language_context_manager=self.mock_language_context_manager, session_manager=self.mock_session_manager, chat_history_manager=self.mock_chat_history_manager, programming_assistance_service=self.programming_assistance_service ) # Common test data self.session_id = "test-session-123" self.test_session = Mock(spec=ChatSession) self.test_session.id = self.session_id self.test_session.language = "python" # Setup common mock returns self.mock_session_manager.get_session.return_value = self.test_session self.mock_language_context_manager.get_language.return_value = "python" self.mock_chat_history_manager.get_recent_history.return_value = [] self.mock_groq_client.generate_response.return_value = "Mock LLM response" self.mock_groq_client.get_model_info.return_value = {"model": "test-model"} # Mock message storage mock_message = Mock(spec=Message) mock_message.id = "msg-123" self.mock_chat_history_manager.store_message.return_value = mock_message def test_process_programming_assistance_code_explanation(self): """Test processing code explanation request.""" code = """ def greet(name): return f"Hello, {name}!" print(greet("Alice")) """ message = "Please explain this code" result = self.chat_agent.process_programming_assistance( self.session_id, message, code=code, assistance_type=AssistanceType.CODE_EXPLANATION ) # Verify result structure self.assertIn('response', result) self.assertIn('assistance_type', result) self.assertIn('analysis_result', result) self.assertEqual(result['assistance_type'], AssistanceType.CODE_EXPLANATION.value) self.assertEqual(result['language'], 'python') # Verify service calls self.mock_session_manager.get_session.assert_called_with(self.session_id) self.mock_session_manager.update_session_activity.assert_called_with(self.session_id) self.mock_language_context_manager.get_language.assert_called_with(self.session_id) # Verify message storage self.assertEqual(self.mock_chat_history_manager.store_message.call_count, 2) # User + Assistant # Verify LLM call self.mock_groq_client.generate_response.assert_called_once() def test_process_programming_assistance_debugging(self): """Test processing debugging request.""" code = """ def divide(a, b): return a / b result = divide(10, 0) # This will cause an error """ error_message = "ZeroDivisionError: division by zero" message = "I'm getting an error with this code" result = self.chat_agent.process_programming_assistance( self.session_id, message, code=code, error_message=error_message, assistance_type=AssistanceType.DEBUGGING ) # Verify result structure self.assertIn('response', result) self.assertEqual(result['assistance_type'], AssistanceType.DEBUGGING.value) self.assertIsNotNone(result['analysis_result']) # Verify analysis was performed analysis = result['analysis_result'] self.assertIn('error_type', analysis.__dict__) self.assertIn('likely_causes', analysis.__dict__) self.assertIn('solutions', analysis.__dict__) def test_process_programming_assistance_error_analysis(self): """Test processing error analysis request.""" error_message = "NameError: name 'undefined_variable' is not defined" message = "What does this error mean?" result = self.chat_agent.process_programming_assistance( self.session_id, message, error_message=error_message, assistance_type=AssistanceType.ERROR_ANALYSIS ) # Verify result structure self.assertEqual(result['assistance_type'], AssistanceType.ERROR_ANALYSIS.value) # Verify error analysis was performed analysis = result['analysis_result'] self.assertEqual(analysis.error_type, 'name_error') self.assertGreater(len(analysis.likely_causes), 0) self.assertGreater(len(analysis.solutions), 0) def test_process_programming_assistance_code_review(self): """Test processing code review request.""" code = """ def calculate_total(items): total = 0 for item in items: total = total + item return total numbers = [1, 2, 3, 4, 5] result = calculate_total(numbers) print(result) """ message = "Please review my code" result = self.chat_agent.process_programming_assistance( self.session_id, message, code=code, assistance_type=AssistanceType.CODE_REVIEW ) # Verify result structure self.assertEqual(result['assistance_type'], AssistanceType.CODE_REVIEW.value) # Verify code analysis was performed analysis = result['analysis_result'] self.assertEqual(analysis.language, 'python') self.assertIsInstance(analysis.suggestions, list) self.assertIsInstance(analysis.issues_found, list) def test_process_programming_assistance_beginner_help(self): """Test processing beginner help request.""" message = "I'm new to programming. Can you explain variables?" result = self.chat_agent.process_programming_assistance( self.session_id, message, assistance_type=AssistanceType.BEGINNER_HELP ) # Verify result structure self.assertEqual(result['assistance_type'], AssistanceType.BEGINNER_HELP.value) # Verify beginner explanation was generated self.assertIn('response', result) response = result['response'] self.assertIn('variables', response.lower()) def test_process_programming_assistance_auto_detection(self): """Test automatic assistance type detection.""" # Test error detection result = self.chat_agent.process_programming_assistance( self.session_id, "I'm getting an error" ) self.assertEqual(result['assistance_type'], AssistanceType.ERROR_ANALYSIS.value) # Test explanation detection with code code = "print('Hello')" result = self.chat_agent.process_programming_assistance( self.session_id, "What does this do?", code=code ) self.assertEqual(result['assistance_type'], AssistanceType.CODE_EXPLANATION.value) # Test beginner detection result = self.chat_agent.process_programming_assistance( self.session_id, "I'm a beginner and need help" ) self.assertEqual(result['assistance_type'], AssistanceType.BEGINNER_HELP.value) def test_explain_code_convenience_method(self): """Test the explain_code convenience method.""" code = "x = [1, 2, 3]\nprint(len(x))" result = self.chat_agent.explain_code(self.session_id, code) self.assertEqual(result['assistance_type'], AssistanceType.CODE_EXPLANATION.value) self.assertIn('analysis_result', result) def test_debug_code_convenience_method(self): """Test the debug_code convenience method.""" code = "print(undefined_var)" error_message = "NameError: name 'undefined_var' is not defined" result = self.chat_agent.debug_code( self.session_id, code, error_message, "Help me fix this error" ) self.assertEqual(result['assistance_type'], AssistanceType.DEBUGGING.value) self.assertIsNotNone(result['analysis_result']) def test_analyze_error_convenience_method(self): """Test the analyze_error convenience method.""" error_message = "TypeError: unsupported operand type(s) for +: 'int' and 'str'" result = self.chat_agent.analyze_error( self.session_id, error_message, "I don't understand this error" ) self.assertEqual(result['assistance_type'], AssistanceType.ERROR_ANALYSIS.value) self.assertEqual(result['analysis_result'].error_type, 'type_error') def test_review_code_convenience_method(self): """Test the review_code convenience method.""" code = """ def add_numbers(a, b): return a + b """ result = self.chat_agent.review_code( self.session_id, code, focus_areas=['performance', 'readability'] ) self.assertEqual(result['assistance_type'], AssistanceType.CODE_REVIEW.value) self.assertIn('analysis_result', result) def test_get_beginner_help_convenience_method(self): """Test the get_beginner_help convenience method.""" result = self.chat_agent.get_beginner_help( self.session_id, "functions", "How do I create a function?" ) self.assertEqual(result['assistance_type'], AssistanceType.BEGINNER_HELP.value) self.assertIn('functions', result['response'].lower()) def test_programming_assistance_with_language_switching(self): """Test programming assistance with different languages.""" # Setup for JavaScript self.mock_language_context_manager.get_language.return_value = "javascript" code = """ function greet(name) { return "Hello, " + name + "!"; } console.log(greet("Bob")); """ result = self.chat_agent.process_programming_assistance( self.session_id, "Explain this JavaScript code", code=code ) self.assertEqual(result['language'], 'javascript') analysis = result['analysis_result'] self.assertEqual(analysis.language, 'javascript') def test_programming_assistance_with_context_metadata(self): """Test programming assistance with beginner context.""" message = "I'm a beginner. Can you explain this code?" code = "print('Hello, World!')" result = self.chat_agent.process_programming_assistance( self.session_id, message, code=code ) # Verify that beginner context was detected and used self.assertIn('response', result) # Check that user message was stored with metadata store_calls = self.mock_chat_history_manager.store_message.call_args_list user_message_call = store_calls[0] # First call should be user message self.assertEqual(user_message_call[1]['role'], 'user') self.assertIn('assistance_type', user_message_call[1]['message_metadata']) def test_programming_assistance_error_handling(self): """Test error handling in programming assistance.""" # Mock session error self.mock_session_manager.get_session.side_effect = Exception("Session error") with self.assertRaises(Exception): self.chat_agent.process_programming_assistance( self.session_id, "Test message" ) def test_programming_assistance_response_formatting(self): """Test that responses are properly formatted.""" code = """ def factorial(n): if n <= 1: return 1 return n * factorial(n - 1) """ result = self.chat_agent.process_programming_assistance( self.session_id, "Review this code", code=code, assistance_type=AssistanceType.CODE_REVIEW ) # Verify response contains formatted analysis response = result['response'] self.assertIn('Code Review', response) self.assertIn('Overall Assessment', response) def test_programming_assistance_with_chat_history(self): """Test programming assistance with existing chat history.""" # Setup mock chat history mock_history = [ Mock(role='user', content='Previous question', language='python', timestamp=datetime.utcnow()), Mock(role='assistant', content='Previous answer', language='python', timestamp=datetime.utcnow()) ] self.mock_chat_history_manager.get_recent_history.return_value = mock_history result = self.chat_agent.process_programming_assistance( self.session_id, "Follow-up question about variables" ) # Verify that chat history was retrieved and used self.mock_chat_history_manager.get_recent_history.assert_called_with(self.session_id) # Verify LLM was called with history context self.mock_groq_client.generate_response.assert_called_once() call_args = self.mock_groq_client.generate_response.call_args chat_history = call_args[1]['chat_history'] self.assertEqual(len(chat_history), 2) # Previous messages converted to ChatMessage format def test_programming_assistance_metadata_storage(self): """Test that assistance metadata is properly stored.""" code = "x = 5\nprint(x)" result = self.chat_agent.process_programming_assistance( self.session_id, "Explain this", code=code, assistance_type=AssistanceType.CODE_EXPLANATION ) # Check assistant message metadata store_calls = self.mock_chat_history_manager.store_message.call_args_list assistant_message_call = store_calls[1] # Second call should be assistant message metadata = assistant_message_call[1]['message_metadata'] self.assertEqual(metadata['assistance_type'], AssistanceType.CODE_EXPLANATION.value) self.assertTrue(metadata['analysis_performed']) self.assertIn('processing_time', metadata) def test_programming_assistance_session_updates(self): """Test that session is properly updated during assistance.""" result = self.chat_agent.process_programming_assistance( self.session_id, "Help with Python" ) # Verify session operations self.mock_session_manager.get_session.assert_called_with(self.session_id) self.mock_session_manager.update_session_activity.assert_called_with(self.session_id) self.mock_session_manager.increment_message_count.assert_called_with(self.session_id) def test_multiple_assistance_types_in_sequence(self): """Test handling multiple assistance requests in sequence.""" # First request: Code explanation code = "def hello(): print('Hello')" result1 = self.chat_agent.process_programming_assistance( self.session_id, "Explain this", code=code, assistance_type=AssistanceType.CODE_EXPLANATION ) # Second request: Error analysis error = "SyntaxError: invalid syntax" result2 = self.chat_agent.process_programming_assistance( self.session_id, "What's this error?", error_message=error, assistance_type=AssistanceType.ERROR_ANALYSIS ) # Verify both requests were processed correctly self.assertEqual(result1['assistance_type'], AssistanceType.CODE_EXPLANATION.value) self.assertEqual(result2['assistance_type'], AssistanceType.ERROR_ANALYSIS.value) # Verify session was updated for both self.assertEqual(self.mock_session_manager.increment_message_count.call_count, 2) def test_programming_assistance_with_specialized_prompts(self): """Test that specialized prompts are used for different assistance types.""" # Mock the language context manager to capture prompt templates self.mock_language_context_manager.get_language_prompt_template.return_value = "Base template" result = self.chat_agent.process_programming_assistance( self.session_id, "Debug this code", code="print(x)", error_message="NameError", assistance_type=AssistanceType.DEBUGGING ) # Verify that generate_response was called with specialized language context call_args = self.mock_groq_client.generate_response.call_args language_context = call_args[1]['language_context'] # The prompt template should contain debugging-specific instructions self.assertIn('debug', language_context.prompt_template.lower()) self.assertIn('step-by-step solution', language_context.prompt_template) if __name__ == '__main__': unittest.main()