scratch_chat / tests /unit /test_programming_assistance.py
WebashalarForML's picture
Upload 178 files
330b6e4 verified
"""
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()