Spaces:
Runtime error
Runtime error
| """ | |
| Unit tests for Programming Assistance Service | |
| Tests the specialized programming assistance features including | |
| code explanation, debugging, error analysis, code review, and | |
| beginner-friendly explanations. | |
| """ | |
| import unittest | |
| from unittest.mock import Mock, patch, MagicMock | |
| import pytest | |
| from chat_agent.services.programming_assistance import ( | |
| ProgrammingAssistanceService, | |
| AssistanceType, | |
| CodeAnalysis, | |
| ErrorAnalysis | |
| ) | |
| class TestProgrammingAssistanceService(unittest.TestCase): | |
| """Test cases for ProgrammingAssistanceService.""" | |
| def setUp(self): | |
| """Set up test fixtures.""" | |
| self.service = ProgrammingAssistanceService() | |
| def test_initialization(self): | |
| """Test service initialization.""" | |
| self.assertIsInstance(self.service, ProgrammingAssistanceService) | |
| self.assertIsNotNone(self.service.code_patterns) | |
| self.assertIsNotNone(self.service.error_patterns) | |
| def test_get_assistance_prompt_template_code_explanation(self): | |
| """Test getting prompt template for code explanation.""" | |
| template = self.service.get_assistance_prompt_template( | |
| AssistanceType.CODE_EXPLANATION, | |
| 'python' | |
| ) | |
| self.assertIn('expert python programming tutor', template) | |
| self.assertIn('Explain the provided python code', template) | |
| self.assertIn('step-by-step breakdown', template) | |
| def test_get_assistance_prompt_template_debugging(self): | |
| """Test getting prompt template for debugging.""" | |
| template = self.service.get_assistance_prompt_template( | |
| AssistanceType.DEBUGGING, | |
| 'javascript' | |
| ) | |
| self.assertIn('Help debug the provided javascript code', template) | |
| self.assertIn('Step-by-step solution', template) | |
| self.assertIn('corrected code', template) | |
| def test_get_assistance_prompt_template_with_beginner_context(self): | |
| """Test prompt template with beginner context.""" | |
| template = self.service.get_assistance_prompt_template( | |
| AssistanceType.CONCEPT_CLARIFICATION, | |
| 'python', | |
| {'beginner_mode': True} | |
| ) | |
| self.assertIn('beginner', template.lower()) | |
| self.assertIn('simple language', template) | |
| self.assertIn('step-by-step', template) | |
| def test_analyze_code_python_function(self): | |
| """Test code analysis for Python function.""" | |
| code = """ | |
| def greet(name): | |
| print("Hello, " + name + "!") | |
| return "Greeting sent" | |
| greet("Alice") | |
| """ | |
| analysis = self.service.analyze_code(code, 'python') | |
| self.assertIsInstance(analysis, CodeAnalysis) | |
| self.assertEqual(analysis.language, 'python') | |
| self.assertEqual(analysis.code_type, 'function_def') | |
| self.assertIn(analysis.complexity_level, ['beginner', 'intermediate', 'advanced']) | |
| self.assertIsInstance(analysis.issues_found, list) | |
| self.assertIsInstance(analysis.suggestions, list) | |
| self.assertIsInstance(analysis.explanation, str) | |
| def test_analyze_code_javascript_class(self): | |
| """Test code analysis for JavaScript class.""" | |
| code = """ | |
| class Person { | |
| constructor(name) { | |
| this.name = name; | |
| } | |
| greet() { | |
| console.log("Hello, " + this.name); | |
| } | |
| } | |
| const person = new Person("Bob"); | |
| person.greet(); | |
| """ | |
| analysis = self.service.analyze_code(code, 'javascript') | |
| self.assertIsInstance(analysis, CodeAnalysis) | |
| self.assertEqual(analysis.language, 'javascript') | |
| self.assertEqual(analysis.code_type, 'class_def') | |
| def test_analyze_code_with_issues(self): | |
| """Test code analysis that finds issues.""" | |
| code = """ | |
| def check_empty(items): | |
| if len(items) == 0: | |
| return True | |
| return False | |
| result = check_empty([]) | |
| print result # Python 2 syntax | |
| """ | |
| analysis = self.service.analyze_code(code, 'python') | |
| self.assertGreater(len(analysis.issues_found), 0) | |
| self.assertGreater(len(analysis.suggestions), 0) | |
| # Check for specific issues | |
| issues_text = ' '.join(analysis.issues_found) | |
| self.assertIn('print', issues_text.lower()) | |
| def test_analyze_error_python_name_error(self): | |
| """Test error analysis for Python NameError.""" | |
| error_message = "NameError: name 'variable_name' is not defined" | |
| analysis = self.service.analyze_error(error_message, language='python') | |
| self.assertIsInstance(analysis, ErrorAnalysis) | |
| self.assertEqual(analysis.error_type, 'name_error') | |
| self.assertIn('not defined', analysis.error_message) | |
| self.assertGreater(len(analysis.likely_causes), 0) | |
| self.assertGreater(len(analysis.solutions), 0) | |
| # Check for specific causes and solutions | |
| causes_text = ' '.join(analysis.likely_causes) | |
| self.assertIn('misspelled', causes_text.lower()) | |
| solutions_text = ' '.join(analysis.solutions) | |
| self.assertIn('spelling', solutions_text.lower()) | |
| def test_analyze_error_javascript_reference_error(self): | |
| """Test error analysis for JavaScript ReferenceError.""" | |
| error_message = "ReferenceError: myVariable is not defined" | |
| analysis = self.service.analyze_error(error_message, language='javascript') | |
| self.assertIsInstance(analysis, ErrorAnalysis) | |
| self.assertEqual(analysis.error_type, 'reference_error') | |
| def test_analyze_error_with_code(self): | |
| """Test error analysis with code context.""" | |
| error_message = "TypeError: unsupported operand type(s) for +: 'int' and 'str'" | |
| code = """ | |
| age = 25 | |
| name = "Alice" | |
| result = age + name # This causes the error | |
| """ | |
| analysis = self.service.analyze_error(error_message, code, 'python') | |
| self.assertEqual(analysis.error_type, 'type_error') | |
| self.assertGreater(len(analysis.code_fixes), 0) | |
| def test_generate_beginner_explanation_variables(self): | |
| """Test beginner explanation for variables.""" | |
| explanation = self.service.generate_beginner_explanation('variables', 'python') | |
| self.assertIn('What is variables?', explanation) | |
| self.assertIn('Why do we use variables?', explanation) | |
| self.assertIn('Simple Example:', explanation) | |
| self.assertIn('```python', explanation) | |
| self.assertIn('What to Learn Next:', explanation) | |
| def test_generate_beginner_explanation_functions(self): | |
| """Test beginner explanation for functions.""" | |
| explanation = self.service.generate_beginner_explanation('functions', 'javascript') | |
| self.assertIn('What is functions?', explanation) | |
| self.assertIn('reusable blocks', explanation.lower()) | |
| self.assertIn('```javascript', explanation) | |
| def test_generate_beginner_explanation_with_code_example(self): | |
| """Test beginner explanation with provided code example.""" | |
| code_example = "x = 10\ny = 20\nsum = x + y" | |
| explanation = self.service.generate_beginner_explanation( | |
| 'variables', 'python', code_example | |
| ) | |
| self.assertIn(code_example, explanation) | |
| self.assertIn('```python', explanation) | |
| def test_detect_assistance_type_error_keywords(self): | |
| """Test assistance type detection for error-related messages.""" | |
| test_cases = [ | |
| ("I'm getting an error", AssistanceType.ERROR_ANALYSIS), | |
| ("My code is broken", AssistanceType.ERROR_ANALYSIS), | |
| ("This is not working properly", AssistanceType.ERROR_ANALYSIS), | |
| ("Exception occurred", AssistanceType.ERROR_ANALYSIS) | |
| ] | |
| for message, expected_type in test_cases: | |
| with self.subTest(message=message): | |
| detected_type = self.service.detect_assistance_type(message) | |
| self.assertEqual(detected_type, expected_type) | |
| def test_detect_assistance_type_with_code(self): | |
| """Test assistance type detection when code is provided.""" | |
| code = "def hello(): print('Hello')" | |
| test_cases = [ | |
| ("I have an error", AssistanceType.DEBUGGING), | |
| ("Explain this code", AssistanceType.CODE_EXPLANATION), | |
| ("Review my code", AssistanceType.CODE_REVIEW) | |
| ] | |
| for message, expected_type in test_cases: | |
| with self.subTest(message=message): | |
| detected_type = self.service.detect_assistance_type(message, code) | |
| self.assertEqual(detected_type, expected_type) | |
| def test_detect_assistance_type_beginner_keywords(self): | |
| """Test assistance type detection for beginner-related messages.""" | |
| test_cases = [ | |
| ("I'm a beginner", AssistanceType.BEGINNER_HELP), | |
| ("I'm new to programming", AssistanceType.BEGINNER_HELP), | |
| ("Just started learning", AssistanceType.BEGINNER_HELP), | |
| ("Basic question about", AssistanceType.BEGINNER_HELP) | |
| ] | |
| for message, expected_type in test_cases: | |
| with self.subTest(message=message): | |
| detected_type = self.service.detect_assistance_type(message) | |
| self.assertEqual(detected_type, expected_type) | |
| def test_detect_assistance_type_explanation_keywords(self): | |
| """Test assistance type detection for explanation requests.""" | |
| test_cases = [ | |
| ("What does this mean?", AssistanceType.CONCEPT_CLARIFICATION), | |
| ("How does this work?", AssistanceType.CONCEPT_CLARIFICATION), | |
| ("Explain loops", AssistanceType.CONCEPT_CLARIFICATION), | |
| ("I don't understand", AssistanceType.CONCEPT_CLARIFICATION) | |
| ] | |
| for message, expected_type in test_cases: | |
| with self.subTest(message=message): | |
| detected_type = self.service.detect_assistance_type(message) | |
| self.assertEqual(detected_type, expected_type) | |
| def test_format_assistance_response_code_explanation(self): | |
| """Test formatting code explanation response.""" | |
| analysis = CodeAnalysis( | |
| code_type='function_def', | |
| language='python', | |
| complexity_level='beginner', | |
| issues_found=['Missing docstring'], | |
| suggestions=['Add function documentation'], | |
| explanation='This is a simple function that greets users.' | |
| ) | |
| response = self.service.format_assistance_response( | |
| AssistanceType.CODE_EXPLANATION, analysis, 'python' | |
| ) | |
| self.assertIn('Code Analysis', response) | |
| self.assertIn('Python', response) | |
| self.assertIn('Function Def', response) | |
| self.assertIn('Beginner', response) | |
| self.assertIn('Missing docstring', response) | |
| self.assertIn('Add function documentation', response) | |
| def test_format_assistance_response_error_analysis(self): | |
| """Test formatting error analysis response.""" | |
| analysis = ErrorAnalysis( | |
| error_type='name_error', | |
| error_message='name is not defined', | |
| likely_causes=['Variable misspelled'], | |
| solutions=['Check spelling'], | |
| code_fixes=['Fix variable name'] | |
| ) | |
| response = self.service.format_assistance_response( | |
| AssistanceType.ERROR_ANALYSIS, analysis, 'python' | |
| ) | |
| self.assertIn('Error Analysis', response) | |
| self.assertIn('Name Error', response) | |
| self.assertIn('Variable misspelled', response) | |
| self.assertIn('Check spelling', response) | |
| self.assertIn('Fix variable name', response) | |
| def test_format_assistance_response_code_review(self): | |
| """Test formatting code review response.""" | |
| analysis = CodeAnalysis( | |
| code_type='script', | |
| language='javascript', | |
| complexity_level='intermediate', | |
| issues_found=['Using var instead of let'], | |
| suggestions=['Use modern JavaScript syntax'], | |
| explanation='This script demonstrates basic concepts.' | |
| ) | |
| response = self.service.format_assistance_response( | |
| AssistanceType.CODE_REVIEW, analysis, 'javascript' | |
| ) | |
| self.assertIn('Code Review', response) | |
| self.assertIn('intermediate', response) | |
| self.assertIn('Using var instead of let', response) | |
| self.assertIn('Use modern JavaScript syntax', response) | |
| self.assertIn('Keep up the great work', response) | |
| def test_code_pattern_detection_python(self): | |
| """Test code pattern detection for Python.""" | |
| test_cases = [ | |
| ("def my_function():", 'function_def'), | |
| ("class MyClass:", 'class_def'), | |
| ("import os", 'import'), | |
| ("for i in range(10):", 'loop'), | |
| ("if condition:", 'conditional'), | |
| ("try:", 'exception') | |
| ] | |
| for code, expected_type in test_cases: | |
| with self.subTest(code=code): | |
| detected_type = self.service._detect_code_type(code, 'python') | |
| self.assertEqual(detected_type, expected_type) | |
| def test_code_pattern_detection_javascript(self): | |
| """Test code pattern detection for JavaScript.""" | |
| test_cases = [ | |
| ("function myFunc() {}", 'function_def'), | |
| ("const func = () => {}", 'function_def'), | |
| ("class MyClass {}", 'class_def'), | |
| ("import React from 'react'", 'import'), | |
| ("for (let i = 0; i < 10; i++) {}", 'loop'), | |
| ("if (condition) {}", 'conditional') | |
| ] | |
| for code, expected_type in test_cases: | |
| with self.subTest(code=code): | |
| detected_type = self.service._detect_code_type(code, 'javascript') | |
| self.assertEqual(detected_type, expected_type) | |
| def test_complexity_assessment(self): | |
| """Test code complexity assessment.""" | |
| # Beginner level (short code) | |
| simple_code = "x = 5\nprint(x)" | |
| complexity = self.service._assess_complexity(simple_code, 'python') | |
| self.assertEqual(complexity, 'beginner') | |
| # Intermediate level (medium code) | |
| medium_code = "\n".join([f"line_{i} = {i}" for i in range(10)]) | |
| complexity = self.service._assess_complexity(medium_code, 'python') | |
| self.assertEqual(complexity, 'intermediate') | |
| # Advanced level (long code) | |
| long_code = "\n".join([f"line_{i} = {i}" for i in range(25)]) | |
| complexity = self.service._assess_complexity(long_code, 'python') | |
| self.assertEqual(complexity, 'advanced') | |
| def test_error_type_detection_python(self): | |
| """Test error type detection for Python errors.""" | |
| test_cases = [ | |
| ("NameError: name 'x' is not defined", 'name_error'), | |
| ("SyntaxError: invalid syntax", 'syntax_error'), | |
| ("TypeError: unsupported operand", 'type_error'), | |
| ("IndexError: list index out of range", 'index_error'), | |
| ("KeyError: 'missing_key'", 'key_error'), | |
| ("AttributeError: has no attribute", 'attribute_error'), | |
| ("ImportError: No module named", 'import_error') | |
| ] | |
| for error_msg, expected_type in test_cases: | |
| with self.subTest(error_msg=error_msg): | |
| detected_type = self.service._detect_error_type(error_msg, 'python') | |
| self.assertEqual(detected_type, expected_type) | |
| def test_error_type_detection_javascript(self): | |
| """Test error type detection for JavaScript errors.""" | |
| test_cases = [ | |
| ("ReferenceError: x is not defined", 'reference_error'), | |
| ("SyntaxError: Unexpected token", 'syntax_error'), | |
| ("TypeError: x is not a function", 'type_error'), | |
| ("RangeError: Invalid array length", 'range_error') | |
| ] | |
| for error_msg, expected_type in test_cases: | |
| with self.subTest(error_msg=error_msg): | |
| detected_type = self.service._detect_error_type(error_msg, 'javascript') | |
| self.assertEqual(detected_type, expected_type) | |
| def test_clean_error_message(self): | |
| """Test error message cleaning.""" | |
| messy_error = """ | |
| Traceback (most recent call last): | |
| File "test.py", line 5, in <module> | |
| print(undefined_var) | |
| NameError: name 'undefined_var' is not defined | |
| """ | |
| clean_error = self.service._clean_error_message(messy_error) | |
| self.assertEqual(clean_error, "NameError: name 'undefined_var' is not defined") | |
| def test_find_code_issues_python(self): | |
| """Test finding code issues in Python.""" | |
| problematic_code = """ | |
| print "Hello World" # Python 2 syntax | |
| if x == True: # Explicit True comparison | |
| if len(items) == 0: # Length check | |
| pass | |
| """ | |
| issues = self.service._find_code_issues(problematic_code, 'python') | |
| self.assertGreater(len(issues), 0) | |
| issues_text = ' '.join(issues) | |
| self.assertIn('print', issues_text.lower()) | |
| def test_find_code_issues_javascript(self): | |
| """Test finding code issues in JavaScript.""" | |
| problematic_code = """ | |
| var x = 5; // Using var | |
| if (x == "5") { // Loose equality | |
| console.log("Equal"); | |
| } | |
| """ | |
| issues = self.service._find_code_issues(problematic_code, 'javascript') | |
| self.assertGreater(len(issues), 0) | |
| issues_text = ' '.join(issues) | |
| self.assertIn('var', issues_text.lower()) | |
| self.assertIn('equality', issues_text.lower()) | |
| def test_generate_code_suggestions(self): | |
| """Test generating code improvement suggestions.""" | |
| issues = [ | |
| "Using Python 2 print syntax - should use print() function", | |
| "Using loose equality (==) - consider strict equality (===)" | |
| ] | |
| suggestions = self.service._generate_code_suggestions("test code", 'python', issues) | |
| self.assertGreater(len(suggestions), 0) | |
| suggestions_text = ' '.join(suggestions) | |
| self.assertIn('Python 3', suggestions_text) | |
| def test_concept_info_retrieval(self): | |
| """Test retrieving concept information.""" | |
| # Test known concept | |
| variables_info = self.service._get_concept_info('variables', 'python') | |
| self.assertIn('simple_definition', variables_info) | |
| self.assertIn('purpose', variables_info) | |
| self.assertIn('example', variables_info) | |
| # Test unknown concept | |
| unknown_info = self.service._get_concept_info('unknown_concept', 'python') | |
| self.assertIn('simple_definition', unknown_info) | |
| self.assertIn('important programming concept', unknown_info['simple_definition']) | |
| def test_error_handling_in_analyze_code(self): | |
| """Test error handling in code analysis.""" | |
| # Test with invalid input | |
| analysis = self.service.analyze_code("", "invalid_language") | |
| self.assertIsInstance(analysis, CodeAnalysis) | |
| self.assertEqual(analysis.language, "invalid_language") | |
| self.assertEqual(analysis.code_type, "script") # Default fallback | |
| def test_error_handling_in_analyze_error(self): | |
| """Test error handling in error analysis.""" | |
| # Test with empty error message | |
| analysis = self.service.analyze_error("", language="python") | |
| self.assertIsInstance(analysis, ErrorAnalysis) | |
| self.assertEqual(analysis.error_type, "unknown_error") | |
| def test_error_handling_in_beginner_explanation(self): | |
| """Test error handling in beginner explanation generation.""" | |
| # This should not raise an exception | |
| explanation = self.service.generate_beginner_explanation("", "python") | |
| self.assertIsInstance(explanation, str) | |
| self.assertGreater(len(explanation), 0) | |
| if __name__ == '__main__': | |
| unittest.main() |