Spaces:
Runtime error
Runtime error
| """ | |
| 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() |