This commit is contained in:
Fahad
2025-06-15 15:32:41 +04:00
parent 3bc7956239
commit 86728a1442
13 changed files with 1261 additions and 1178 deletions

420
tests/test_tracer.py Normal file
View File

@@ -0,0 +1,420 @@
"""
Tests for the tracer tool functionality
"""
from unittest.mock import Mock, patch
import pytest
from tools.models import ToolModelCategory
from tools.tracer import TracerRequest, TracerTool
class TestTracerTool:
"""Test suite for the Tracer tool"""
@pytest.fixture
def tracer_tool(self):
"""Create a tracer tool instance for testing"""
return TracerTool()
@pytest.fixture
def mock_model_response(self):
"""Create a mock model response for call path analysis"""
def _create_response(content=None):
if content is None:
content = """## Call Path Summary
1. 🟢 `BookingManager::finalizeInvoice()` at booking.py:45 → calls `PaymentProcessor.process()`
2. 🟢 `PaymentProcessor::process()` at payment.py:123 → calls `validation.validate_payment()`
3. 🟡 `validation.validate_payment()` at validation.py:67 → conditionally calls `Logger.log()`
## Value-Driven Flow Analysis
**Scenario 1**: `invoice_id=123, payment_method="credit_card"`
- Path: BookingManager → PaymentProcessor → CreditCardValidator → StripeGateway
- Key decision at payment.py:156: routes to Stripe integration
## Side Effects & External Dependencies
### Database Interactions
- **Transaction.save()** at models.py:234 → inserts payment record
### Network Calls
- **StripeGateway.charge()** → HTTPS POST to Stripe API
## Code Anchors
- Entry point: `BookingManager::finalizeInvoice` at booking.py:45
- Critical branch: Payment method selection at payment.py:156
"""
return Mock(
content=content,
usage={"input_tokens": 150, "output_tokens": 300, "total_tokens": 450},
model_name="test-model",
metadata={"finish_reason": "STOP"},
)
return _create_response
def test_get_name(self, tracer_tool):
"""Test that the tool returns the correct name"""
assert tracer_tool.get_name() == "tracer"
def test_get_description(self, tracer_tool):
"""Test that the tool returns a comprehensive description"""
description = tracer_tool.get_description()
assert "STATIC CODE ANALYSIS" in description
assert "execution flow" in description
assert "dependency mappings" in description
assert "precision" in description
assert "dependencies" in description
def test_get_input_schema(self, tracer_tool):
"""Test that the input schema includes all required fields"""
schema = tracer_tool.get_input_schema()
assert schema["type"] == "object"
assert "prompt" in schema["properties"]
assert "files" in schema["properties"]
assert "trace_mode" in schema["properties"]
# Check required fields
required_fields = schema["required"]
assert "prompt" in required_fields
assert "files" in required_fields
assert "trace_mode" in required_fields
# Check enum values for trace_mode
trace_mode_enum = schema["properties"]["trace_mode"]["enum"]
assert "precision" in trace_mode_enum
assert "dependencies" in trace_mode_enum
def test_get_model_category(self, tracer_tool):
"""Test that the tool uses extended reasoning category"""
category = tracer_tool.get_model_category()
assert category == ToolModelCategory.EXTENDED_REASONING
def test_request_model_validation(self):
"""Test request model validation"""
# Valid request
request = TracerRequest(
prompt="Trace BookingManager::finalizeInvoice method with invoice_id=123",
files=["/test/booking.py", "/test/payment.py"],
trace_mode="precision",
)
assert request.prompt == "Trace BookingManager::finalizeInvoice method with invoice_id=123"
assert len(request.files) == 2
assert request.trace_mode == "precision"
# Invalid request (missing required fields)
with pytest.raises(ValueError):
TracerRequest(files=["/test/file.py"]) # Missing prompt and trace_mode
# Invalid trace_mode value
with pytest.raises(ValueError):
TracerRequest(prompt="Test", files=["/test/file.py"], trace_mode="invalid_type")
def test_language_detection_python(self, tracer_tool):
"""Test language detection for Python files"""
files = ["/test/booking.py", "/test/payment.py", "/test/utils.py"]
language = tracer_tool.detect_primary_language(files)
assert language == "python"
def test_language_detection_javascript(self, tracer_tool):
"""Test language detection for JavaScript files"""
files = ["/test/app.js", "/test/component.jsx", "/test/utils.js"]
language = tracer_tool.detect_primary_language(files)
assert language == "javascript"
def test_language_detection_typescript(self, tracer_tool):
"""Test language detection for TypeScript files"""
files = ["/test/app.ts", "/test/component.tsx", "/test/utils.ts"]
language = tracer_tool.detect_primary_language(files)
assert language == "typescript"
def test_language_detection_csharp(self, tracer_tool):
"""Test language detection for C# files"""
files = ["/test/BookingService.cs", "/test/PaymentProcessor.cs"]
language = tracer_tool.detect_primary_language(files)
assert language == "csharp"
def test_language_detection_java(self, tracer_tool):
"""Test language detection for Java files"""
files = ["/test/BookingManager.java", "/test/PaymentService.java"]
language = tracer_tool.detect_primary_language(files)
assert language == "java"
def test_language_detection_mixed(self, tracer_tool):
"""Test language detection for mixed language files"""
files = ["/test/app.py", "/test/service.js", "/test/model.java"]
language = tracer_tool.detect_primary_language(files)
assert language == "mixed"
def test_language_detection_unknown(self, tracer_tool):
"""Test language detection for unknown extensions"""
files = ["/test/config.xml", "/test/readme.txt"]
language = tracer_tool.detect_primary_language(files)
assert language == "unknown"
# Removed parse_entry_point tests as method no longer exists in simplified interface
@pytest.mark.asyncio
async def test_prepare_prompt_basic(self, tracer_tool):
"""Test basic prompt preparation"""
request = TracerRequest(
prompt="Trace BookingManager::finalizeInvoice method with invoice_id=123",
files=["/test/booking.py"],
trace_mode="precision",
)
# Mock file content preparation
with patch.object(tracer_tool, "_prepare_file_content_for_prompt") as mock_prep:
mock_prep.return_value = "def finalizeInvoice(self, invoice_id):\n pass"
with patch.object(tracer_tool, "check_prompt_size") as mock_check:
mock_check.return_value = None
prompt = await tracer_tool.prepare_prompt(request)
assert "ANALYSIS REQUEST" in prompt
assert "Trace BookingManager::finalizeInvoice method" in prompt
assert "precision" in prompt
assert "CODE TO ANALYZE" in prompt
@pytest.mark.asyncio
async def test_prepare_prompt_with_dependencies(self, tracer_tool):
"""Test prompt preparation with dependencies type"""
request = TracerRequest(
prompt="Analyze dependencies for payment.process_payment function with amount=100.50",
files=["/test/payment.py"],
trace_mode="dependencies",
)
with patch.object(tracer_tool, "_prepare_file_content_for_prompt") as mock_prep:
mock_prep.return_value = "def process_payment(amount, method):\n pass"
with patch.object(tracer_tool, "check_prompt_size") as mock_check:
mock_check.return_value = None
prompt = await tracer_tool.prepare_prompt(request)
assert "Analyze dependencies for payment.process_payment" in prompt
assert "Trace Mode: dependencies" in prompt
@pytest.mark.asyncio
async def test_prepare_prompt_with_security_context(self, tracer_tool):
"""Test prompt preparation with security context"""
request = TracerRequest(
prompt="Trace UserService::authenticate method focusing on security implications and potential vulnerabilities",
files=["/test/auth.py"],
trace_mode="precision",
)
with patch.object(tracer_tool, "_prepare_file_content_for_prompt") as mock_prep:
mock_prep.return_value = "def authenticate(self, username, password):\n pass"
with patch.object(tracer_tool, "check_prompt_size") as mock_check:
mock_check.return_value = None
prompt = await tracer_tool.prepare_prompt(request)
assert "security implications and potential vulnerabilities" in prompt
assert "Trace Mode: precision" in prompt
def test_format_response_precision(self, tracer_tool):
"""Test response formatting for precision trace"""
request = TracerRequest(
prompt="Trace BookingManager::finalizeInvoice method", files=["/test/booking.py"], trace_mode="precision"
)
response = '{"status": "trace_complete", "trace_type": "precision"}'
model_info = {"model_response": Mock(friendly_name="Gemini Pro")}
formatted = tracer_tool.format_response(response, request, model_info)
assert response in formatted
assert "Analysis Complete" in formatted
assert "Gemini Pro" in formatted
assert "precision analysis" in formatted
assert "CALL FLOW DIAGRAM" in formatted
assert "BRANCHING & SIDE EFFECT TABLE" in formatted
def test_format_response_dependencies(self, tracer_tool):
"""Test response formatting for dependencies trace"""
request = TracerRequest(
prompt="Analyze dependencies for payment.process function",
files=["/test/payment.py"],
trace_mode="dependencies",
)
response = '{"status": "trace_complete", "trace_type": "dependencies"}'
formatted = tracer_tool.format_response(response, request)
assert response in formatted
assert "dependencies analysis" in formatted
assert "DEPENDENCY FLOW GRAPH" in formatted
assert "DEPENDENCY TABLE" in formatted
# Removed PlantUML test as export_format is no longer a parameter
def test_get_default_temperature(self, tracer_tool):
"""Test that the tool uses analytical temperature"""
from config import TEMPERATURE_ANALYTICAL
assert tracer_tool.get_default_temperature() == TEMPERATURE_ANALYTICAL
def test_wants_line_numbers_by_default(self, tracer_tool):
"""Test that line numbers are enabled by default"""
# The base class should enable line numbers by default for precise references
# We test that this isn't overridden to disable them
assert hasattr(tracer_tool, "wants_line_numbers_by_default")
def test_trace_mode_validation(self):
"""Test trace mode validation"""
# Valid trace modes
request1 = TracerRequest(prompt="Test precision", files=["/test/file.py"], trace_mode="precision")
assert request1.trace_mode == "precision"
request2 = TracerRequest(prompt="Test dependencies", files=["/test/file.py"], trace_mode="dependencies")
assert request2.trace_mode == "dependencies"
# Invalid trace mode should raise ValidationError
with pytest.raises(ValueError):
TracerRequest(prompt="Test", files=["/test/file.py"], trace_mode="invalid_type")
def test_get_rendering_instructions(self, tracer_tool):
"""Test the main rendering instructions dispatcher method"""
# Test precision mode
precision_instructions = tracer_tool._get_rendering_instructions("precision")
assert "MANDATORY RENDERING INSTRUCTIONS FOR PRECISION TRACE" in precision_instructions
assert "CALL FLOW DIAGRAM" in precision_instructions
assert "BRANCHING & SIDE EFFECT TABLE" in precision_instructions
# Test dependencies mode
dependencies_instructions = tracer_tool._get_rendering_instructions("dependencies")
assert "MANDATORY RENDERING INSTRUCTIONS FOR DEPENDENCIES TRACE" in dependencies_instructions
assert "DEPENDENCY FLOW GRAPH" in dependencies_instructions
assert "DEPENDENCY TABLE" in dependencies_instructions
def test_get_precision_rendering_instructions(self, tracer_tool):
"""Test precision mode rendering instructions"""
instructions = tracer_tool._get_precision_rendering_instructions()
# Check for required sections
assert "MANDATORY RENDERING INSTRUCTIONS FOR PRECISION TRACE" in instructions
assert "1. CALL FLOW DIAGRAM (TOP-DOWN)" in instructions
assert "2. BRANCHING & SIDE EFFECT TABLE" in instructions
# Check for specific formatting requirements
assert "[Class::Method] (file: /path, line: ##)" in instructions
assert "Chain each call using ↓ or → for readability" in instructions
assert "If ambiguous, mark with `⚠️ ambiguous branch`" in instructions
assert "Side Effects:" in instructions
assert "[database] description (File.ext:##)" in instructions
# Check for critical rules
assert "CRITICAL RULES:" in instructions
assert "Use exact filenames, class names, and line numbers from JSON" in instructions
assert "DO NOT invent function names or examples" in instructions
def test_get_dependencies_rendering_instructions(self, tracer_tool):
"""Test dependencies mode rendering instructions"""
instructions = tracer_tool._get_dependencies_rendering_instructions()
# Check for required sections
assert "MANDATORY RENDERING INSTRUCTIONS FOR DEPENDENCIES TRACE" in instructions
assert "1. DEPENDENCY FLOW GRAPH" in instructions
assert "2. DEPENDENCY TABLE" in instructions
# Check for specific formatting requirements
assert "Called by:" in instructions
assert "[CallerClass::callerMethod] ← /path/file.ext:##" in instructions
assert "Calls:" in instructions
assert "[Logger::logAction] → /utils/log.ext:##" in instructions
assert "Type Dependencies:" in instructions
assert "State Access:" in instructions
# Check for arrow rules
assert "`←` for incoming (who calls this)" in instructions
assert "`→` for outgoing (what this calls)" in instructions
# Check for dependency table format
assert "| Type | From/To | Method | File | Line |" in instructions
assert "| direct_call | From: CallerClass | callerMethod |" in instructions
# Check for critical rules
assert "CRITICAL RULES:" in instructions
assert "Use exact filenames, class names, and line numbers from JSON" in instructions
assert "Show directional dependencies with proper arrows" in instructions
def test_format_response_uses_private_methods(self, tracer_tool):
"""Test that format_response correctly uses the refactored private methods"""
# Test precision mode
precision_request = TracerRequest(prompt="Test precision", files=["/test/file.py"], trace_mode="precision")
precision_response = tracer_tool.format_response('{"test": "response"}', precision_request)
# Should contain precision-specific instructions
assert "CALL FLOW DIAGRAM" in precision_response
assert "BRANCHING & SIDE EFFECT TABLE" in precision_response
assert "precision analysis" in precision_response
# Test dependencies mode
dependencies_request = TracerRequest(
prompt="Test dependencies", files=["/test/file.py"], trace_mode="dependencies"
)
dependencies_response = tracer_tool.format_response('{"test": "response"}', dependencies_request)
# Should contain dependencies-specific instructions
assert "DEPENDENCY FLOW GRAPH" in dependencies_response
assert "DEPENDENCY TABLE" in dependencies_response
assert "dependencies analysis" in dependencies_response
def test_rendering_instructions_consistency(self, tracer_tool):
"""Test that private methods return consistent instructions"""
# Get instructions through both paths
precision_direct = tracer_tool._get_precision_rendering_instructions()
precision_via_dispatcher = tracer_tool._get_rendering_instructions("precision")
dependencies_direct = tracer_tool._get_dependencies_rendering_instructions()
dependencies_via_dispatcher = tracer_tool._get_rendering_instructions("dependencies")
# Should be identical
assert precision_direct == precision_via_dispatcher
assert dependencies_direct == dependencies_via_dispatcher
def test_rendering_instructions_completeness(self, tracer_tool):
"""Test that rendering instructions contain all required elements"""
precision_instructions = tracer_tool._get_precision_rendering_instructions()
dependencies_instructions = tracer_tool._get_dependencies_rendering_instructions()
# Both should have mandatory sections
for instructions in [precision_instructions, dependencies_instructions]:
assert "MANDATORY RENDERING INSTRUCTIONS" in instructions
assert "You MUST render" in instructions
assert "exactly two views" in instructions
assert "CRITICAL RULES:" in instructions
assert "ALWAYS render both views unless data is missing" in instructions
assert "Use exact filenames, class names, and line numbers from JSON" in instructions
assert "DO NOT invent function names or examples" in instructions
def test_rendering_instructions_mode_specific_content(self, tracer_tool):
"""Test that each mode has unique content"""
precision_instructions = tracer_tool._get_precision_rendering_instructions()
dependencies_instructions = tracer_tool._get_dependencies_rendering_instructions()
# Precision-specific content should not be in dependencies
assert "CALL FLOW DIAGRAM" in precision_instructions
assert "CALL FLOW DIAGRAM" not in dependencies_instructions
assert "BRANCHING & SIDE EFFECT TABLE" in precision_instructions
assert "BRANCHING & SIDE EFFECT TABLE" not in dependencies_instructions
# Dependencies-specific content should not be in precision
assert "DEPENDENCY FLOW GRAPH" in dependencies_instructions
assert "DEPENDENCY FLOW GRAPH" not in precision_instructions
assert "DEPENDENCY TABLE" in dependencies_instructions
assert "DEPENDENCY TABLE" not in precision_instructions
# Mode-specific symbols and patterns
assert "" in precision_instructions # Flow arrows
assert "" in dependencies_instructions # Incoming arrow
assert "" in dependencies_instructions # Outgoing arrow
assert "Side Effects:" in precision_instructions
assert "Called by:" in dependencies_instructions