mirror of
https://github.com/crewAIInc/crewAI.git
synced 2026-01-10 16:48:30 +00:00
- Remove unused os import from edit_file_tool.py - Fix f-string without placeholders - Remove unused pathlib.Path import from test file - Remove unused pytest import from integration test file Co-Authored-By: João <joao@crewai.com>
182 lines
7.0 KiB
Python
182 lines
7.0 KiB
Python
import pytest
|
|
import tempfile
|
|
import os
|
|
from unittest.mock import Mock, patch
|
|
from crewai.tools.agent_tools.edit_file_tool import EditFileTool, EditFileToolInput
|
|
|
|
|
|
class TestEditFileTool:
|
|
|
|
def setup_method(self):
|
|
"""Set up test fixtures."""
|
|
self.tool = EditFileTool()
|
|
|
|
def test_tool_initialization(self):
|
|
"""Test that the tool initializes correctly."""
|
|
assert self.tool.name == "edit_file"
|
|
assert "Fast Apply" in self.tool.description
|
|
assert self.tool.args_schema == EditFileToolInput
|
|
|
|
def test_input_schema_validation(self):
|
|
"""Test input schema validation."""
|
|
valid_input = EditFileToolInput(
|
|
file_path="/path/to/file.py",
|
|
edit_instructions="Add a new function",
|
|
context="This is for testing"
|
|
)
|
|
assert valid_input.file_path == "/path/to/file.py"
|
|
assert valid_input.edit_instructions == "Add a new function"
|
|
assert valid_input.context == "This is for testing"
|
|
|
|
with pytest.raises(ValueError):
|
|
EditFileToolInput(file_path="/path/to/file.py")
|
|
|
|
def test_file_not_exists(self):
|
|
"""Test handling of non-existent files."""
|
|
result = self.tool._run(
|
|
file_path="/non/existent/file.py",
|
|
edit_instructions="Add a function"
|
|
)
|
|
assert "Error: File /non/existent/file.py does not exist" in result
|
|
|
|
def test_path_is_directory(self):
|
|
"""Test handling when path points to a directory."""
|
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
result = self.tool._run(
|
|
file_path=temp_dir,
|
|
edit_instructions="Add a function"
|
|
)
|
|
assert f"Error: {temp_dir} is not a file" in result
|
|
|
|
def test_binary_file_handling(self):
|
|
"""Test handling of binary files."""
|
|
with tempfile.NamedTemporaryFile(delete=False, suffix='.bin') as temp_file:
|
|
temp_file.write(b'\x00\x01\x02\x03')
|
|
temp_file.flush()
|
|
|
|
try:
|
|
result = self.tool._run(
|
|
file_path=temp_file.name,
|
|
edit_instructions="Edit this file"
|
|
)
|
|
assert "Cannot read" in result and "unsupported encoding" in result
|
|
finally:
|
|
os.unlink(temp_file.name)
|
|
|
|
@patch('crewai.tools.agent_tools.edit_file_tool.LLM')
|
|
def test_successful_file_edit(self, mock_llm_class):
|
|
"""Test successful file editing."""
|
|
mock_llm = Mock()
|
|
mock_llm.call.return_value = "def new_function():\n return 'edited'"
|
|
mock_llm_class.return_value = mock_llm
|
|
|
|
with tempfile.NamedTemporaryFile(mode='w', delete=False, suffix='.py') as temp_file:
|
|
temp_file.write("def old_function():\n return 'original'")
|
|
temp_file.flush()
|
|
|
|
try:
|
|
tool = EditFileTool()
|
|
result = tool._run(
|
|
file_path=temp_file.name,
|
|
edit_instructions="Replace old_function with new_function"
|
|
)
|
|
|
|
assert "Successfully edited" in result
|
|
assert "Backup saved" in result
|
|
|
|
with open(temp_file.name, 'r') as f:
|
|
content = f.read()
|
|
assert "new_function" in content
|
|
assert "old_function" not in content
|
|
|
|
backup_path = f"{temp_file.name}.backup"
|
|
assert os.path.exists(backup_path)
|
|
with open(backup_path, 'r') as f:
|
|
backup_content = f.read()
|
|
assert "old_function" in backup_content
|
|
|
|
os.unlink(backup_path)
|
|
|
|
finally:
|
|
os.unlink(temp_file.name)
|
|
|
|
def test_build_fast_apply_prompt(self):
|
|
"""Test Fast Apply prompt building."""
|
|
prompt = self.tool._build_fast_apply_prompt(
|
|
current_content="print('hello')",
|
|
edit_instructions="Change hello to world",
|
|
file_path="/test/file.py",
|
|
context="Testing context"
|
|
)
|
|
|
|
assert "Fast Apply file editing" in prompt
|
|
assert "print('hello')" in prompt
|
|
assert "Change hello to world" in prompt
|
|
assert "/test/file.py" in prompt
|
|
assert "Testing context" in prompt
|
|
assert "COMPLETE rewritten file content" in prompt
|
|
|
|
def test_extract_file_content_plain(self):
|
|
"""Test extracting content from plain LLM response."""
|
|
response = "def hello():\n print('world')"
|
|
content = self.tool._extract_file_content(response)
|
|
assert content == "def hello():\n print('world')"
|
|
|
|
def test_extract_file_content_markdown(self):
|
|
"""Test extracting content from markdown code blocks."""
|
|
response = "```python\ndef hello():\n print('world')\n```"
|
|
content = self.tool._extract_file_content(response)
|
|
assert content == "def hello():\n print('world')"
|
|
|
|
def test_extract_file_content_empty(self):
|
|
"""Test handling empty LLM response."""
|
|
content = self.tool._extract_file_content("")
|
|
assert content is None
|
|
|
|
content = self.tool._extract_file_content("```\n```")
|
|
assert content == ""
|
|
|
|
@patch('crewai.tools.agent_tools.edit_file_tool.LLM')
|
|
def test_llm_failure_handling(self, mock_llm_class):
|
|
"""Test handling of LLM failures."""
|
|
mock_llm = Mock()
|
|
mock_llm.call.side_effect = Exception("LLM error")
|
|
mock_llm_class.return_value = mock_llm
|
|
|
|
with tempfile.NamedTemporaryFile(mode='w', delete=False, suffix='.py') as temp_file:
|
|
temp_file.write("original content")
|
|
temp_file.flush()
|
|
|
|
try:
|
|
tool = EditFileTool()
|
|
result = tool._run(
|
|
file_path=temp_file.name,
|
|
edit_instructions="Edit this file"
|
|
)
|
|
|
|
assert "Error editing file" in result
|
|
assert "LLM error" in result
|
|
|
|
finally:
|
|
os.unlink(temp_file.name)
|
|
|
|
def test_context_parameter(self):
|
|
"""Test that context parameter is properly handled."""
|
|
prompt_with_context = self.tool._build_fast_apply_prompt(
|
|
current_content="test",
|
|
edit_instructions="edit",
|
|
file_path="/test.py",
|
|
context="important context"
|
|
)
|
|
|
|
prompt_without_context = self.tool._build_fast_apply_prompt(
|
|
current_content="test",
|
|
edit_instructions="edit",
|
|
file_path="/test.py",
|
|
context=None
|
|
)
|
|
|
|
assert "important context" in prompt_with_context
|
|
assert "ADDITIONAL CONTEXT" in prompt_with_context
|
|
assert "ADDITIONAL CONTEXT" not in prompt_without_context
|