🚀 Major Enhancement: Workflow-Based Tool Architecture v5.5.0 (#95)
* WIP: new workflow architecture * WIP: further improvements and cleanup * WIP: cleanup and docks, replace old tool with new * WIP: cleanup and docks, replace old tool with new * WIP: new planner implementation using workflow * WIP: precommit tool working as a workflow instead of a basic tool Support for passing False to use_assistant_model to skip external models completely and use Claude only * WIP: precommit workflow version swapped with old * WIP: codereview * WIP: replaced codereview * WIP: replaced codereview * WIP: replaced refactor * WIP: workflow for thinkdeep * WIP: ensure files get embedded correctly * WIP: thinkdeep replaced with workflow version * WIP: improved messaging when an external model's response is received * WIP: analyze tool swapped * WIP: updated tests * Extract only the content when building history * Use "relevant_files" for workflow tools only * WIP: updated tests * Extract only the content when building history * Use "relevant_files" for workflow tools only * WIP: fixed get_completion_next_steps_message missing param * Fixed tests Request for files consistently * Fixed tests Request for files consistently * Fixed tests * New testgen workflow tool Updated docs * Swap testgen workflow * Fix CI test failures by excluding API-dependent tests - Update GitHub Actions workflow to exclude simulation tests that require API keys - Fix collaboration tests to properly mock workflow tool expert analysis calls - Update test assertions to handle new workflow tool response format - Ensure unit tests run without external API dependencies in CI 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> * WIP - Update tests to match new tools * WIP - Update tests to match new tools --------- Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
committed by
GitHub
parent
4dae6e457e
commit
69a3121452
@@ -3,7 +3,6 @@ Tests for the refactor tool functionality
|
||||
"""
|
||||
|
||||
import json
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
import pytest
|
||||
|
||||
@@ -68,181 +67,38 @@ class TestRefactorTool:
|
||||
def test_get_description(self, refactor_tool):
|
||||
"""Test that the tool returns a comprehensive description"""
|
||||
description = refactor_tool.get_description()
|
||||
assert "INTELLIGENT CODE REFACTORING" in description
|
||||
assert "codesmells" in description
|
||||
assert "decompose" in description
|
||||
assert "modernize" in description
|
||||
assert "organization" in description
|
||||
assert "COMPREHENSIVE REFACTORING WORKFLOW" in description
|
||||
assert "code smell detection" in description
|
||||
assert "decomposition planning" in description
|
||||
assert "modernization opportunities" in description
|
||||
assert "organization improvements" in description
|
||||
|
||||
def test_get_input_schema(self, refactor_tool):
|
||||
"""Test that the input schema includes all required fields"""
|
||||
"""Test that the input schema includes all required workflow fields"""
|
||||
schema = refactor_tool.get_input_schema()
|
||||
|
||||
assert schema["type"] == "object"
|
||||
assert "files" in schema["properties"]
|
||||
assert "prompt" in schema["properties"]
|
||||
|
||||
# Check workflow-specific fields
|
||||
assert "step" in schema["properties"]
|
||||
assert "step_number" in schema["properties"]
|
||||
assert "total_steps" in schema["properties"]
|
||||
assert "next_step_required" in schema["properties"]
|
||||
assert "findings" in schema["properties"]
|
||||
assert "files_checked" in schema["properties"]
|
||||
assert "relevant_files" in schema["properties"]
|
||||
|
||||
# Check refactor-specific fields
|
||||
assert "refactor_type" in schema["properties"]
|
||||
assert "confidence" in schema["properties"]
|
||||
|
||||
# Check refactor_type enum values
|
||||
refactor_enum = schema["properties"]["refactor_type"]["enum"]
|
||||
expected_types = ["codesmells", "decompose", "modernize", "organization"]
|
||||
assert all(rt in refactor_enum for rt in expected_types)
|
||||
|
||||
def test_language_detection_python(self, refactor_tool):
|
||||
"""Test language detection for Python files"""
|
||||
files = ["/test/file1.py", "/test/file2.py", "/test/utils.py"]
|
||||
language = refactor_tool.detect_primary_language(files)
|
||||
assert language == "python"
|
||||
|
||||
def test_language_detection_javascript(self, refactor_tool):
|
||||
"""Test language detection for JavaScript files"""
|
||||
files = ["/test/app.js", "/test/component.jsx", "/test/utils.js"]
|
||||
language = refactor_tool.detect_primary_language(files)
|
||||
assert language == "javascript"
|
||||
|
||||
def test_language_detection_mixed(self, refactor_tool):
|
||||
"""Test language detection for mixed language files"""
|
||||
files = ["/test/app.py", "/test/script.js", "/test/main.java"]
|
||||
language = refactor_tool.detect_primary_language(files)
|
||||
assert language == "mixed"
|
||||
|
||||
def test_language_detection_unknown(self, refactor_tool):
|
||||
"""Test language detection for unknown file types"""
|
||||
files = ["/test/data.txt", "/test/config.json"]
|
||||
language = refactor_tool.detect_primary_language(files)
|
||||
assert language == "unknown"
|
||||
|
||||
def test_language_specific_guidance_python(self, refactor_tool):
|
||||
"""Test language-specific guidance for Python modernization"""
|
||||
guidance = refactor_tool.get_language_specific_guidance("python", "modernize")
|
||||
assert "f-strings" in guidance
|
||||
assert "dataclasses" in guidance
|
||||
assert "type hints" in guidance
|
||||
|
||||
def test_language_specific_guidance_javascript(self, refactor_tool):
|
||||
"""Test language-specific guidance for JavaScript modernization"""
|
||||
guidance = refactor_tool.get_language_specific_guidance("javascript", "modernize")
|
||||
assert "async/await" in guidance
|
||||
assert "destructuring" in guidance
|
||||
assert "arrow functions" in guidance
|
||||
|
||||
def test_language_specific_guidance_unknown(self, refactor_tool):
|
||||
"""Test language-specific guidance for unknown languages"""
|
||||
guidance = refactor_tool.get_language_specific_guidance("unknown", "modernize")
|
||||
assert guidance == ""
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_execute_basic_refactor(self, refactor_tool, mock_model_response):
|
||||
"""Test basic refactor tool execution"""
|
||||
with patch.object(refactor_tool, "get_model_provider") as mock_get_provider:
|
||||
mock_provider = MagicMock()
|
||||
mock_provider.get_provider_type.return_value = MagicMock(value="test")
|
||||
mock_provider.supports_thinking_mode.return_value = False
|
||||
mock_provider.generate_content.return_value = mock_model_response()
|
||||
mock_get_provider.return_value = mock_provider
|
||||
|
||||
# Mock file processing
|
||||
with patch.object(refactor_tool, "_prepare_file_content_for_prompt") as mock_prepare:
|
||||
mock_prepare.return_value = ("def test(): pass", ["/test/file.py"])
|
||||
|
||||
result = await refactor_tool.execute(
|
||||
{
|
||||
"files": ["/test/file.py"],
|
||||
"prompt": "Find code smells in this Python code",
|
||||
"refactor_type": "codesmells",
|
||||
}
|
||||
)
|
||||
|
||||
assert len(result) == 1
|
||||
output = json.loads(result[0].text)
|
||||
assert output["status"] == "success"
|
||||
# The format_response method adds markdown instructions, so content_type should be "markdown"
|
||||
# It could also be "json" or "text" depending on the response format
|
||||
assert output["content_type"] in ["json", "text", "markdown"]
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_execute_with_style_guide(self, refactor_tool, mock_model_response):
|
||||
"""Test refactor tool execution with style guide examples"""
|
||||
with patch.object(refactor_tool, "get_model_provider") as mock_get_provider:
|
||||
mock_provider = MagicMock()
|
||||
mock_provider.get_provider_type.return_value = MagicMock(value="test")
|
||||
mock_provider.supports_thinking_mode.return_value = False
|
||||
mock_provider.generate_content.return_value = mock_model_response()
|
||||
mock_get_provider.return_value = mock_provider
|
||||
|
||||
# Mock file processing
|
||||
with patch.object(refactor_tool, "_prepare_file_content_for_prompt") as mock_prepare:
|
||||
mock_prepare.return_value = ("def example(): pass", ["/test/file.py"])
|
||||
|
||||
with patch.object(refactor_tool, "_process_style_guide_examples") as mock_style:
|
||||
mock_style.return_value = ("# style guide content", "")
|
||||
|
||||
result = await refactor_tool.execute(
|
||||
{
|
||||
"files": ["/test/file.py"],
|
||||
"prompt": "Modernize this code following our style guide",
|
||||
"refactor_type": "modernize",
|
||||
"style_guide_examples": ["/test/style_example.py"],
|
||||
}
|
||||
)
|
||||
|
||||
assert len(result) == 1
|
||||
output = json.loads(result[0].text)
|
||||
assert output["status"] == "success"
|
||||
|
||||
def test_format_response_valid_json(self, refactor_tool):
|
||||
"""Test response formatting with valid structured JSON"""
|
||||
valid_json_response = json.dumps(
|
||||
{
|
||||
"status": "refactor_analysis_complete",
|
||||
"refactor_opportunities": [
|
||||
{
|
||||
"id": "test-001",
|
||||
"type": "codesmells",
|
||||
"severity": "medium",
|
||||
"file": "/test.py",
|
||||
"start_line": 1,
|
||||
"end_line": 5,
|
||||
"context_start_text": "def test():",
|
||||
"context_end_text": " pass",
|
||||
"issue": "Test issue",
|
||||
"suggestion": "Test suggestion",
|
||||
"rationale": "Test rationale",
|
||||
"code_to_replace": "old code",
|
||||
"replacement_code_snippet": "new code",
|
||||
}
|
||||
],
|
||||
"priority_sequence": ["test-001"],
|
||||
"next_actions_for_claude": [],
|
||||
}
|
||||
)
|
||||
|
||||
# Create a mock request
|
||||
request = MagicMock()
|
||||
request.refactor_type = "codesmells"
|
||||
|
||||
formatted = refactor_tool.format_response(valid_json_response, request)
|
||||
|
||||
# Should contain the original response plus implementation instructions
|
||||
assert valid_json_response in formatted
|
||||
assert "MANDATORY NEXT STEPS" in formatted
|
||||
assert "Start executing the refactoring plan immediately" in formatted
|
||||
assert "MANDATORY: MUST start executing the refactor plan" in formatted
|
||||
|
||||
def test_format_response_invalid_json(self, refactor_tool):
|
||||
"""Test response formatting with invalid JSON - now handled by base tool"""
|
||||
invalid_response = "This is not JSON content"
|
||||
|
||||
# Create a mock request
|
||||
request = MagicMock()
|
||||
request.refactor_type = "codesmells"
|
||||
|
||||
formatted = refactor_tool.format_response(invalid_response, request)
|
||||
|
||||
# Should contain the original response plus implementation instructions
|
||||
assert invalid_response in formatted
|
||||
assert "MANDATORY NEXT STEPS" in formatted
|
||||
assert "Start executing the refactoring plan immediately" in formatted
|
||||
# Note: Old language detection and execution tests removed -
|
||||
# new workflow-based refactor tool has different architecture
|
||||
|
||||
def test_model_category(self, refactor_tool):
|
||||
"""Test that the refactor tool uses EXTENDED_REASONING category"""
|
||||
@@ -258,56 +114,7 @@ class TestRefactorTool:
|
||||
temp = refactor_tool.get_default_temperature()
|
||||
assert temp == TEMPERATURE_ANALYTICAL
|
||||
|
||||
def test_format_response_more_refactor_required(self, refactor_tool):
|
||||
"""Test that format_response handles more_refactor_required field"""
|
||||
more_refactor_response = json.dumps(
|
||||
{
|
||||
"status": "refactor_analysis_complete",
|
||||
"refactor_opportunities": [
|
||||
{
|
||||
"id": "refactor-001",
|
||||
"type": "decompose",
|
||||
"severity": "critical",
|
||||
"file": "/test/file.py",
|
||||
"start_line": 1,
|
||||
"end_line": 10,
|
||||
"context_start_text": "def test_function():",
|
||||
"context_end_text": " return True",
|
||||
"issue": "Function too large",
|
||||
"suggestion": "Break into smaller functions",
|
||||
"rationale": "Improves maintainability",
|
||||
"code_to_replace": "original code",
|
||||
"replacement_code_snippet": "refactored code",
|
||||
"new_code_snippets": [],
|
||||
}
|
||||
],
|
||||
"priority_sequence": ["refactor-001"],
|
||||
"next_actions_for_claude": [
|
||||
{
|
||||
"action_type": "EXTRACT_METHOD",
|
||||
"target_file": "/test/file.py",
|
||||
"source_lines": "1-10",
|
||||
"description": "Extract method from large function",
|
||||
}
|
||||
],
|
||||
"more_refactor_required": True,
|
||||
"continuation_message": "Large codebase requires extensive refactoring across multiple files",
|
||||
}
|
||||
)
|
||||
|
||||
# Create a mock request
|
||||
request = MagicMock()
|
||||
request.refactor_type = "decompose"
|
||||
|
||||
formatted = refactor_tool.format_response(more_refactor_response, request)
|
||||
|
||||
# Should contain the original response plus continuation instructions
|
||||
assert more_refactor_response in formatted
|
||||
assert "MANDATORY NEXT STEPS" in formatted
|
||||
assert "Start executing the refactoring plan immediately" in formatted
|
||||
assert "MANDATORY: MUST start executing the refactor plan" in formatted
|
||||
assert "AFTER IMPLEMENTING ALL ABOVE" in formatted # Special instruction for more_refactor_required
|
||||
assert "continuation_id" in formatted
|
||||
# Note: format_response tests removed - workflow tools use different response format
|
||||
|
||||
|
||||
class TestFileUtilsLineNumbers:
|
||||
|
||||
Reference in New Issue
Block a user