Skip to main content
Glama

MCP Claude Code

by SDGLBL
test_multi_edit.py9.21 kB
"""Tests for the multi_edit tool.""" import pytest from unittest.mock import AsyncMock from mcp_claude_code.tools.common.permissions import PermissionManager from mcp_claude_code.tools.filesystem.multi_edit import MultiEdit class TestMultiEdit: """Test cases for the MultiEdit tool.""" @pytest.fixture def permission_manager(self): """Create a permission manager for testing.""" return PermissionManager() @pytest.fixture def multi_edit_tool(self, permission_manager): """Create a MultiEdit tool instance for testing.""" return MultiEdit(permission_manager) @pytest.fixture def mock_ctx(self): """Create a mock MCP context.""" ctx = AsyncMock() ctx.session_id = "test_session" return ctx def test_tool_properties(self, multi_edit_tool): """Test basic tool properties.""" assert multi_edit_tool.name == "multi_edit" assert "multiple edits" in multi_edit_tool.description.lower() @pytest.mark.asyncio async def test_invalid_edit_object(self, multi_edit_tool, mock_ctx): """Test handling of invalid edit objects.""" result = await multi_edit_tool.call( mock_ctx, file_path="/tmp/test.txt", edits=["invalid_edit"] ) assert "Error: Edit at index 0 must be an object" in result @pytest.mark.asyncio async def test_missing_old_string(self, multi_edit_tool, mock_ctx): """Test handling of missing old_string in edit.""" result = await multi_edit_tool.call( mock_ctx, file_path="/tmp/test.txt", edits=[{"new_string": "new"}] ) assert ( "Error: Parameter 'old_string' in edit at index 0 is required but was None" in result ) @pytest.mark.asyncio async def test_missing_new_string(self, multi_edit_tool, mock_ctx): """Test handling of missing new_string in edit.""" result = await multi_edit_tool.call( mock_ctx, file_path="/tmp/test.txt", edits=[{"old_string": "old"}] ) assert ( "Error: Parameter 'new_string' in edit at index 0 is required but was None" in result ) @pytest.mark.asyncio async def test_identical_old_new_strings(self, multi_edit_tool, mock_ctx): """Test handling of identical old and new strings.""" result = await multi_edit_tool.call( mock_ctx, file_path="/tmp/test.txt", edits=[{"old_string": "same", "new_string": "same"}], ) assert ( "Error: Edit at index 0: old_string and new_string are identical" in result ) @pytest.mark.asyncio async def test_invalid_expected_replacements(self, multi_edit_tool, mock_ctx): """Test handling of invalid expected_replacements value.""" result = await multi_edit_tool.call( mock_ctx, file_path="/tmp/test.txt", edits=[ {"old_string": "old", "new_string": "new", "expected_replacements": -1} ], ) assert ( "Error: Parameter 'expected_replacements' in edit at index 0 must be a non-negative number" in result ) @pytest.mark.asyncio async def test_successful_multiple_edits(self, multi_edit_tool, mock_ctx, tmp_path): """Test successful application of multiple edits.""" # Create a test file test_file = tmp_path / "test.txt" test_content = """line 1 line 2 line 3 line 4""" test_file.write_text(test_content) # Temporarily patch the permission manager to allow the temp path original_is_path_allowed = multi_edit_tool.permission_manager.is_path_allowed multi_edit_tool.permission_manager.is_path_allowed = ( lambda path: str(tmp_path) in path ) try: result = await multi_edit_tool.call( mock_ctx, file_path=str(test_file), edits=[ {"old_string": "line 1", "new_string": "modified line 1"}, {"old_string": "line 3", "new_string": "modified line 3"}, ], ) # Check that the operation was successful assert "Successfully applied 2 edits" in result assert "2 total replacements" in result # Verify the file content was updated correctly updated_content = test_file.read_text() assert "modified line 1" in updated_content assert "modified line 3" in updated_content assert "line 2" in updated_content # unchanged assert "line 4" in updated_content # unchanged finally: # Restore the original permission manager multi_edit_tool.permission_manager.is_path_allowed = ( original_is_path_allowed ) @pytest.mark.asyncio async def test_file_creation_with_multiple_edits( self, multi_edit_tool, mock_ctx, tmp_path ): """Test creating a new file with multiple edits.""" # Test file that doesn't exist yet test_file = tmp_path / "new_file.txt" # Temporarily patch the permission manager to allow the temp path original_is_path_allowed = multi_edit_tool.permission_manager.is_path_allowed multi_edit_tool.permission_manager.is_path_allowed = ( lambda path: str(tmp_path) in path ) try: result = await multi_edit_tool.call( mock_ctx, file_path=str(test_file), edits=[ {"old_string": "", "new_string": "Initial content\nSecond line"}, {"old_string": "Second line", "new_string": "Modified second line"}, ], ) # Check that the operation was successful assert "Successfully created file" in result # Verify the file was created with correct content assert test_file.exists() content = test_file.read_text() assert "Initial content" in content assert "Modified second line" in content finally: # Restore the original permission manager multi_edit_tool.permission_manager.is_path_allowed = ( original_is_path_allowed ) @pytest.mark.asyncio async def test_edit_not_found(self, multi_edit_tool, mock_ctx, tmp_path): """Test handling when old_string is not found in file.""" # Create a test file test_file = tmp_path / "test.txt" test_content = "line 1\nline 2" test_file.write_text(test_content) # Temporarily patch the permission manager to allow the temp path original_is_path_allowed = multi_edit_tool.permission_manager.is_path_allowed multi_edit_tool.permission_manager.is_path_allowed = ( lambda path: str(tmp_path) in path ) try: result = await multi_edit_tool.call( mock_ctx, file_path=str(test_file), edits=[ {"old_string": "line 1", "new_string": "modified line 1"}, {"old_string": "nonexistent line", "new_string": "new content"}, ], ) # Check that the operation failed with appropriate error assert "Error: Edit 2: The specified old_string was not found" in result finally: # Restore the original permission manager multi_edit_tool.permission_manager.is_path_allowed = ( original_is_path_allowed ) @pytest.mark.asyncio async def test_expected_replacements_mismatch( self, multi_edit_tool, mock_ctx, tmp_path ): """Test handling when expected_replacements doesn't match actual occurrences.""" # Create a test file with duplicate content test_file = tmp_path / "test.txt" test_content = "duplicate\nduplicate\nother line" test_file.write_text(test_content) # Temporarily patch the permission manager to allow the temp path original_is_path_allowed = multi_edit_tool.permission_manager.is_path_allowed multi_edit_tool.permission_manager.is_path_allowed = ( lambda path: str(tmp_path) in path ) try: result = await multi_edit_tool.call( mock_ctx, file_path=str(test_file), edits=[ { "old_string": "duplicate", "new_string": "modified", "expected_replacements": 1, } ], ) # Check that the operation failed with appropriate error assert ( "Error: Edit 1: Found 2 occurrences of the specified old_string, but expected 1" in result ) finally: # Restore the original permission manager multi_edit_tool.permission_manager.is_path_allowed = ( original_is_path_allowed )

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/SDGLBL/mcp-claude-code'

If you have feedback or need assistance with the MCP directory API, please join our Discord server