mirror of
https://github.com/mcp-use/mcp-use.git
synced 2026-09-14 18:06:01 +08:00
266a445934
* chore: enter canary prerelease mode * Improve search tool response structure (#508) * Refactor search_tools to return metadata and results Co-authored-by: tonxipad <tonxipad@gmail.com> * docs: update API documentation * feat: enhance search_tools display and filtering capabilities - Introduced a new function to format search_tools results as a tree structure, including metadata such as total tools and namespaces. - Updated the display logic to handle both new and old formats of search_tools results for backward compatibility. - Enhanced filtering in MCPClient and CodeExecutor to include server name in search criteria, improving tool discoverability. * format * docs: update code-mode documentation to reflect changes in search_tools response structure - Modified examples to use the new response format from search_tools, which includes metadata and results. - Enhanced clarity in code snippets by adding print statements for tool discovery results. - Updated TypeScript and Python documentation to ensure consistency across both languages. * refactor: clean up whitespace in display.py for improved readability - Removed unnecessary blank lines throughout the _format_search_tools_as_tree function to enhance code clarity and maintainability. - Ensured consistent formatting in the display logic for better visual structure. * refactor: streamline line wrapping in display.py for improved readability - Consolidated parameters in the text wrapping function to enhance code clarity and maintainability. * feat: enhance search_tools metadata for improved context - Updated search_tools to return additional metadata (total_tools, namespaces, result_count) alongside results, enhancing the context for model decision-making. --------- Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: tonxipad <tonxipad@gmail.com> * chore(typescript): version packages (canary) * feat: Integrate official UI components in templates and enhance widget development experience (#517) * feat: Add ecommerce widgets and OpenAI Apps SDK UI components Co-authored-by: tonxipad <tonxipad@gmail.com> * feat: Update MCP app templates and resources - Added new fruit images to the public directory for enhanced visual content. - Removed outdated weather and ecommerce carousel components to streamline the codebase. - Updated the product search result component to utilize new UI components and improved error handling. - Modified package dependencies to include react-router and updated @openai/apps-sdk-ui version. - Enhanced the .gitignore file to include .mcp-use for better project management. Co-authored-by: tonxipad <tonxipad@gmail.com> * feat: Introduce new lockfile and update dependencies - Added a new bun.lock file to manage package dependencies for the TypeScript project. - Updated pnpm-lock.yaml to include new dependencies such as @openai/apps-sdk-ui and react-router. - Enhanced the CLI tool to support aborting fetch requests and improved error handling in widget building. - Replaced outdated image assets with PNG versions for better compatibility and removed unused JPG files. - Introduced new ErrorBoundary and Image components to improve error handling and image loading in the MCP app. Co-authored-by: tonxipad <tonxipad@gmail.com> * feat: Enhance Vite server configuration and update dependencies - Added a new plugin to Vite for watching the resources directory, ensuring hot module replacement (HMR) works seamlessly when widget source files change. - Updated pnpm-lock.yaml to include new dependencies such as @openai/apps-sdk-ui, react-router, and various development tools like @tailwindcss/vite and typescript. - Improved the Vite server configuration to explicitly watch files outside the root directory, enhancing the development experience. Co-authored-by: tonxipad <tonxipad@gmail.com> * feat: Implement carousel component for product search results - Added a new CarouselItem component to enhance the visual presentation of fruit items in the product search results. - Introduced dynamic pointer tracking for interactive hover effects on carousel items, improving user engagement. - Updated styles for carousel items and their backgrounds to create a visually appealing blur effect. - Refactored the ProductSearchResult component to utilize the new carousel structure and improve layout responsiveness. Co-authored-by: tonxipad <tonxipad@gmail.com> * feat: Update ProductSearchResult and OpenAIComponentRenderer for theme support - Added WidgetDebugger to the ProductSearchResult component for enhanced debugging capabilities. - Updated the product search result text to reflect a new template and improved context for users. - Enhanced OpenAIComponentRenderer to handle theme changes, ensuring the correct theme is applied when the iframe loads. - Modified shared-utils to include theme information in WidgetData, allowing for better theme management across components. Co-authored-by: tonxipad <tonxipad@gmail.com> * feat: Refactor ProductSearchResult component and enhance WidgetFullscreenWrapper - Replaced ThemeProvider and BrowserRouter with McpUseProvider in the ProductSearchResult component for improved context management. - Removed unused getBasename function to streamline routing logic. - Updated WidgetFullscreenWrapper to support viewControls prop, allowing conditional rendering of fullscreen and picture-in-picture buttons. - Enhanced overall structure and readability of the ProductSearchResult component. Co-authored-by: tonxipad <tonxipad@gmail.com> * feat: Update dependencies and enhance component functionality - Added `react-router-dom` as a dependency to support routing features. - Updated `detect-libc` to version 2.1.2 for improved compatibility. - Refactored various components for better readability and structure, including the `ProductSearchResult` and `OpenAIComponentRenderer`. - Introduced new auto-sizing functionality in `McpUseProvider` to notify OpenAI about container height changes. - Removed deprecated `WidgetDebugger` and `WidgetFullscreenWrapper` components to streamline the codebase. Co-authored-by: tonxipad <tonxipad@gmail.com> * refactor: Enhance OpenAIComponentRenderer and ToolResultDisplay for performance and readability - Memoized component to prevent unnecessary re-renders in OpenAIComponentRenderer. - Introduced new state management for iframe height notifications and improved height handling logic. - Refactored ToolResultDisplay to utilize memoization for args and result, optimizing rendering performance. - Cleaned up unused state variables and streamlined event handling for better maintainability. Co-authored-by: tonxipad <tonxipad@gmail.com> * refactor: Improve logging and code readability in various components - Enhanced logging statements in OpenAIComponentRenderer, McpUseProvider, and WidgetControls for better debugging and clarity. - Refactored memoization in ToolResultDisplay to improve performance and prevent unnecessary re-renders. - Cleaned up code formatting for improved readability across multiple components. Co-authored-by: tonxipad <tonxipad@gmail.com> * refactor: Update ProductSearchResult and shared routes for improved development experience - Modified the ProductSearchResult component to change the FAQ question and enhance the autosize feature description. - Simplified asset loading in shared routes to directly fetch from the dev server, improving development efficiency. - Enhanced security headers in shared-utils to include dev server origins for better handling of HMR scripts and resource loading. Co-authored-by: tonxipad <tonxipad@gmail.com> * feat: Integrate @tanstack/react-query and enhance fruits API in apps-sdk template - Added @tanstack/react-query as a dependency to improve data fetching capabilities. - Implemented a new API endpoint in the apps-sdk template to serve a list of fruits with their respective colors. - Removed the outdated ProductSearchResult component to streamline the codebase and focus on the new API integration. Co-authored-by: tonxipad <tonxipad@gmail.com> * refactor: Clean up code formatting and improve readability in product search result components - Removed unnecessary blank lines in various TypeScript files to enhance code clarity. - Simplified function parameters in CarouselItem component for better readability. - Streamlined the Accordion and AccordionItem components by removing excess whitespace. Co-authored-by: tonxipad <tonxipad@gmail.com> * change set * chore: Remove bun.lock file to streamline dependency management - Deleted the bun.lock file to eliminate potential conflicts and simplify the dependency resolution process in the project. * feat: Update documentation and components for improved clarity and organization - Added new pages for MCP UI resources and widget components in the documentation. - Enhanced the migration guide to specify applicability for version 1.5.0 and later. - Updated the ErrorBoundary component icon for better representation. - Removed outdated template comparison section to streamline the templates documentation. * fix: Correct indentation in documentation JSON for better readability - Adjusted the indentation of the "apps-sdk-resources" entry in the documentation JSON to maintain consistent formatting. - Removed unnecessary blank lines to enhance overall clarity in the documentation structure. * chore: Update dependencies and clean up code for improved performance - Updated react-router-dom to version 7.9.5 for enhanced functionality. - Removed commented-out code in ProductSearchResult component to improve clarity. - Cleaned up logging statements in OpenAIComponentRenderer for better debugging. - Refactored height notification logic in McpUseProvider to optimize performance. - Adjusted useWidget to avoid dependency issues with URL parameters. Co-authored-by: tonxipad <tonxipad@gmail.com> * chore: Update dependencies and clean up code for improved performance - Updated react-router-dom to version 7.9.5 for enhanced functionality. - Removed commented-out code in ProductSearchResult component to improve clarity. - Cleaned up logging statements in OpenAIComponentRenderer for better debugging. - Refactored height notification logic in McpUseProvider to optimize performance. - Adjusted useWidget to avoid dependency issues with URL parameters. Co-authored-by: tonxipad <tonxipad@gmail.com> * refactor: Clean up code formatting and improve readability in OpenAIComponentRenderer and useWidget - Adjusted formatting of variable declarations for better consistency and readability. - Simplified the handleError function in OpenAIComponentRenderer for clarity. - Ensured consistent indentation in useWidget for improved code structure. --------- Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: tonxipad <tonxipad@gmail.com> * chore(typescript): version packages (canary) * feat: Add JSON-RPC message logging to Inspector tabs (MCP-536) (#519) * feat: Add ecommerce widgets and OpenAI Apps SDK UI components Co-authored-by: tonxipad <tonxipad@gmail.com> * feat: Update MCP app templates and resources - Added new fruit images to the public directory for enhanced visual content. - Removed outdated weather and ecommerce carousel components to streamline the codebase. - Updated the product search result component to utilize new UI components and improved error handling. - Modified package dependencies to include react-router and updated @openai/apps-sdk-ui version. - Enhanced the .gitignore file to include .mcp-use for better project management. Co-authored-by: tonxipad <tonxipad@gmail.com> * feat: Introduce new lockfile and update dependencies - Added a new bun.lock file to manage package dependencies for the TypeScript project. - Updated pnpm-lock.yaml to include new dependencies such as @openai/apps-sdk-ui and react-router. - Enhanced the CLI tool to support aborting fetch requests and improved error handling in widget building. - Replaced outdated image assets with PNG versions for better compatibility and removed unused JPG files. - Introduced new ErrorBoundary and Image components to improve error handling and image loading in the MCP app. Co-authored-by: tonxipad <tonxipad@gmail.com> * feat: Enhance Vite server configuration and update dependencies - Added a new plugin to Vite for watching the resources directory, ensuring hot module replacement (HMR) works seamlessly when widget source files change. - Updated pnpm-lock.yaml to include new dependencies such as @openai/apps-sdk-ui, react-router, and various development tools like @tailwindcss/vite and typescript. - Improved the Vite server configuration to explicitly watch files outside the root directory, enhancing the development experience. Co-authored-by: tonxipad <tonxipad@gmail.com> * feat: Implement carousel component for product search results - Added a new CarouselItem component to enhance the visual presentation of fruit items in the product search results. - Introduced dynamic pointer tracking for interactive hover effects on carousel items, improving user engagement. - Updated styles for carousel items and their backgrounds to create a visually appealing blur effect. - Refactored the ProductSearchResult component to utilize the new carousel structure and improve layout responsiveness. Co-authored-by: tonxipad <tonxipad@gmail.com> * feat: Update ProductSearchResult and OpenAIComponentRenderer for theme support - Added WidgetDebugger to the ProductSearchResult component for enhanced debugging capabilities. - Updated the product search result text to reflect a new template and improved context for users. - Enhanced OpenAIComponentRenderer to handle theme changes, ensuring the correct theme is applied when the iframe loads. - Modified shared-utils to include theme information in WidgetData, allowing for better theme management across components. Co-authored-by: tonxipad <tonxipad@gmail.com> * feat: Refactor ProductSearchResult component and enhance WidgetFullscreenWrapper - Replaced ThemeProvider and BrowserRouter with McpUseProvider in the ProductSearchResult component for improved context management. - Removed unused getBasename function to streamline routing logic. - Updated WidgetFullscreenWrapper to support viewControls prop, allowing conditional rendering of fullscreen and picture-in-picture buttons. - Enhanced overall structure and readability of the ProductSearchResult component. Co-authored-by: tonxipad <tonxipad@gmail.com> * feat: Update dependencies and enhance component functionality - Added `react-router-dom` as a dependency to support routing features. - Updated `detect-libc` to version 2.1.2 for improved compatibility. - Refactored various components for better readability and structure, including the `ProductSearchResult` and `OpenAIComponentRenderer`. - Introduced new auto-sizing functionality in `McpUseProvider` to notify OpenAI about container height changes. - Removed deprecated `WidgetDebugger` and `WidgetFullscreenWrapper` components to streamline the codebase. Co-authored-by: tonxipad <tonxipad@gmail.com> * refactor: Enhance OpenAIComponentRenderer and ToolResultDisplay for performance and readability - Memoized component to prevent unnecessary re-renders in OpenAIComponentRenderer. - Introduced new state management for iframe height notifications and improved height handling logic. - Refactored ToolResultDisplay to utilize memoization for args and result, optimizing rendering performance. - Cleaned up unused state variables and streamlined event handling for better maintainability. Co-authored-by: tonxipad <tonxipad@gmail.com> * refactor: Improve logging and code readability in various components - Enhanced logging statements in OpenAIComponentRenderer, McpUseProvider, and WidgetControls for better debugging and clarity. - Refactored memoization in ToolResultDisplay to improve performance and prevent unnecessary re-renders. - Cleaned up code formatting for improved readability across multiple components. Co-authored-by: tonxipad <tonxipad@gmail.com> * refactor: Update ProductSearchResult and shared routes for improved development experience - Modified the ProductSearchResult component to change the FAQ question and enhance the autosize feature description. - Simplified asset loading in shared routes to directly fetch from the dev server, improving development efficiency. - Enhanced security headers in shared-utils to include dev server origins for better handling of HMR scripts and resource loading. Co-authored-by: tonxipad <tonxipad@gmail.com> * feat: Integrate @tanstack/react-query and enhance fruits API in apps-sdk template - Added @tanstack/react-query as a dependency to improve data fetching capabilities. - Implemented a new API endpoint in the apps-sdk template to serve a list of fruits with their respective colors. - Removed the outdated ProductSearchResult component to streamline the codebase and focus on the new API integration. Co-authored-by: tonxipad <tonxipad@gmail.com> * refactor: Clean up code formatting and improve readability in product search result components - Removed unnecessary blank lines in various TypeScript files to enhance code clarity. - Simplified function parameters in CarouselItem component for better readability. - Streamlined the Accordion and AccordionItem components by removing excess whitespace. Co-authored-by: tonxipad <tonxipad@gmail.com> * feat: Integrate JsonRpcLoggerView into PromptsTab, ResourcesTab, and ToolsTab for enhanced logging - Added JsonRpcLoggerView component to PromptsTab, ResourcesTab, and ToolsTab to facilitate RPC logging. - Adjusted ResizablePanel default sizes for better layout consistency across tabs. - Implemented keyboard shortcut handling in ToolExecutionPanel for executing tools with Cmd/Ctrl + Enter. Co-authored-by: tonxipad <tonxipad@gmail.com> * format * chore: changeset * docs: add rpc docs * chore: Remove bun.lock file and clean up pnpm-lock.yaml - Deleted the bun.lock file to streamline dependency management and eliminate potential conflicts. - Removed outdated dependencies from pnpm-lock.yaml, including entries for test_app and its associated packages, to simplify the lockfile and improve clarity. --------- Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: tonxipad <tonxipad@gmail.com> * chore(typescript): version packages (canary) * docs: update API documentation * docs: api update docs * trigger release * chore: Clean up pnpm-lock.yaml and remove bun.lock file for streamlined dependency management * chore(typescript): version packages (canary) * chore(inspector): update package.json with homepage, repository, and bugs information --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: tonxipad <tonxipad@gmail.com>
427 lines
12 KiB
Python
427 lines
12 KiB
Python
"""
|
|
Unit tests for CodeExecutor.
|
|
|
|
Tests the code execution functionality for MCP code mode.
|
|
"""
|
|
|
|
import asyncio
|
|
from unittest.mock import AsyncMock, MagicMock, Mock
|
|
|
|
import pytest
|
|
|
|
from mcp_use.client.client import MCPClient
|
|
from mcp_use.client.code_executor import CodeExecutor
|
|
|
|
|
|
@pytest.fixture
|
|
def mock_client():
|
|
"""Create a mock MCPClient for testing."""
|
|
client = MagicMock(spec=MCPClient)
|
|
client.sessions = {}
|
|
client.code_mode = True
|
|
return client
|
|
|
|
|
|
@pytest.fixture
|
|
def code_executor(mock_client):
|
|
"""Create a CodeExecutor instance for testing."""
|
|
return CodeExecutor(mock_client)
|
|
|
|
|
|
class TestCodeExecutorBasics:
|
|
"""Test basic code execution functionality."""
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_execute_simple_code(self, code_executor):
|
|
"""Test executing simple Python code."""
|
|
code = "result = 1 + 1\nreturn result"
|
|
|
|
result = await code_executor.execute(code, timeout=5.0)
|
|
|
|
assert result["error"] is None
|
|
assert result["result"] == 2
|
|
assert isinstance(result["execution_time"], float)
|
|
assert result["execution_time"] > 0
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_execute_with_print(self, code_executor):
|
|
"""Test that print statements are captured."""
|
|
code = """
|
|
print("Hello")
|
|
print("World")
|
|
return "done"
|
|
"""
|
|
|
|
result = await code_executor.execute(code, timeout=5.0)
|
|
|
|
assert result["error"] is None
|
|
assert result["result"] == "done"
|
|
assert "Hello" in result["logs"]
|
|
assert "World" in result["logs"]
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_execute_with_variables(self, code_executor):
|
|
"""Test executing code with variables."""
|
|
code = """
|
|
x = 10
|
|
y = 20
|
|
z = x + y
|
|
return z
|
|
"""
|
|
|
|
result = await code_executor.execute(code, timeout=5.0)
|
|
|
|
assert result["error"] is None
|
|
assert result["result"] == 30
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_execute_with_loops(self, code_executor):
|
|
"""Test executing code with loops."""
|
|
code = """
|
|
total = 0
|
|
for i in range(5):
|
|
total += i
|
|
return total
|
|
"""
|
|
|
|
result = await code_executor.execute(code, timeout=5.0)
|
|
|
|
assert result["error"] is None
|
|
assert result["result"] == 10 # 0 + 1 + 2 + 3 + 4
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_execute_with_list_comprehension(self, code_executor):
|
|
"""Test executing code with list comprehensions."""
|
|
code = """
|
|
numbers = [1, 2, 3, 4, 5]
|
|
squares = [n**2 for n in numbers]
|
|
return squares
|
|
"""
|
|
|
|
result = await code_executor.execute(code, timeout=5.0)
|
|
|
|
assert result["error"] is None
|
|
assert result["result"] == [1, 4, 9, 16, 25]
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_execute_with_dict(self, code_executor):
|
|
"""Test executing code that returns a dictionary."""
|
|
code = """
|
|
data = {
|
|
'name': 'Test',
|
|
'value': 42,
|
|
'items': [1, 2, 3]
|
|
}
|
|
return data
|
|
"""
|
|
|
|
result = await code_executor.execute(code, timeout=5.0)
|
|
|
|
assert result["error"] is None
|
|
assert result["result"]["name"] == "Test"
|
|
assert result["result"]["value"] == 42
|
|
assert result["result"]["items"] == [1, 2, 3]
|
|
|
|
|
|
class TestCodeExecutorSecurity:
|
|
"""Test security restrictions in code execution."""
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_restricted_builtins(self, code_executor):
|
|
"""Test that dangerous builtins are not available."""
|
|
code = "import os"
|
|
|
|
result = await code_executor.execute(code, timeout=5.0)
|
|
|
|
# Should fail because import is restricted
|
|
assert result["error"] is not None
|
|
assert "import" in result["error"].lower() or "name" in result["error"].lower()
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_no_file_access(self, code_executor):
|
|
"""Test that file operations are restricted."""
|
|
code = "open('/etc/passwd', 'r')"
|
|
|
|
result = await code_executor.execute(code, timeout=5.0)
|
|
|
|
# Should fail because open is not in safe builtins
|
|
assert result["error"] is not None
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_no_eval(self, code_executor):
|
|
"""Test that eval is not available."""
|
|
code = "eval('1 + 1')"
|
|
|
|
result = await code_executor.execute(code, timeout=5.0)
|
|
|
|
# Should fail because eval is restricted
|
|
assert result["error"] is not None
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_safe_builtins_available(self, code_executor):
|
|
"""Test that safe builtins are available."""
|
|
code = """
|
|
# Test safe builtins
|
|
result = {
|
|
'len': len([1, 2, 3]),
|
|
'sum': sum([1, 2, 3]),
|
|
'max': max([1, 2, 3]),
|
|
'min': min([1, 2, 3]),
|
|
'sorted': sorted([3, 1, 2]),
|
|
'str': str(42),
|
|
'int': int('42'),
|
|
'float': float('3.14'),
|
|
}
|
|
return result
|
|
"""
|
|
|
|
result = await code_executor.execute(code, timeout=5.0)
|
|
|
|
assert result["error"] is None
|
|
assert result["result"]["len"] == 3
|
|
assert result["result"]["sum"] == 6
|
|
assert result["result"]["max"] == 3
|
|
assert result["result"]["min"] == 1
|
|
assert result["result"]["sorted"] == [1, 2, 3]
|
|
assert result["result"]["str"] == "42"
|
|
assert result["result"]["int"] == 42
|
|
assert result["result"]["float"] == 3.14
|
|
|
|
|
|
class TestCodeExecutorTimeout:
|
|
"""Test timeout functionality."""
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_timeout_enforcement(self, code_executor):
|
|
"""Test that timeout is enforced."""
|
|
code = """
|
|
await asyncio.sleep(10) # Sleep longer than timeout
|
|
return "should not reach here"
|
|
"""
|
|
|
|
result = await code_executor.execute(code, timeout=0.5)
|
|
|
|
assert result["error"] is not None
|
|
assert "timeout" in result["error"].lower()
|
|
assert result["result"] is None
|
|
|
|
|
|
class TestCodeExecutorWithTools:
|
|
"""Test code execution with mock MCP tools."""
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_search_tools_function(self, mock_client, code_executor):
|
|
"""Test the search_tools function in execution namespace."""
|
|
# Mock a session with tools
|
|
mock_session = AsyncMock()
|
|
mock_tool = Mock()
|
|
mock_tool.name = "test_tool"
|
|
mock_tool.description = "A test tool"
|
|
mock_tool.inputSchema = {}
|
|
|
|
mock_session.list_tools = AsyncMock(return_value=[mock_tool])
|
|
mock_client.sessions = {"test_server": mock_session}
|
|
|
|
code = """
|
|
result = await search_tools()
|
|
tools = result['results']
|
|
return {"count": len(tools), "names": [t['name'] for t in tools], "total": result['meta']['total_tools']}
|
|
"""
|
|
|
|
result = await code_executor.execute(code, timeout=5.0)
|
|
|
|
assert result["error"] is None
|
|
assert result["result"]["count"] == 1
|
|
assert result["result"]["names"] == ["test_tool"]
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_search_tools_with_query(self, mock_client, code_executor):
|
|
"""Test search_tools with a query filter."""
|
|
# Mock session with multiple tools
|
|
mock_session = AsyncMock()
|
|
|
|
tool1 = Mock()
|
|
tool1.name = "github_get_pr"
|
|
tool1.description = "Get a GitHub pull request"
|
|
tool1.inputSchema = {}
|
|
|
|
tool2 = Mock()
|
|
tool2.name = "slack_post"
|
|
tool2.description = "Post a message to Slack"
|
|
tool2.inputSchema = {}
|
|
|
|
mock_session.list_tools = AsyncMock(return_value=[tool1, tool2])
|
|
mock_client.sessions = {"test": mock_session}
|
|
|
|
code = """
|
|
all_result = await search_tools()
|
|
github_result = await search_tools("github")
|
|
slack_result = await search_tools("slack")
|
|
|
|
all_tools = all_result['results']
|
|
github_tools = github_result['results']
|
|
slack_tools = slack_result['results']
|
|
|
|
return {
|
|
"total": len(all_tools),
|
|
"github": len(github_tools),
|
|
"slack": len(slack_tools)
|
|
}
|
|
"""
|
|
|
|
result = await code_executor.execute(code, timeout=5.0)
|
|
|
|
assert result["error"] is None
|
|
assert result["result"]["total"] == 2
|
|
assert result["result"]["github"] == 1
|
|
assert result["result"]["slack"] == 1
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_tool_namespaces_available(self, mock_client, code_executor):
|
|
"""Test that __tool_namespaces is available."""
|
|
mock_session = AsyncMock()
|
|
# Return at least one dummy tool so the namespace is created
|
|
mock_tool = Mock()
|
|
mock_tool.name = "dummy_tool"
|
|
mock_session.list_tools = AsyncMock(return_value=[mock_tool])
|
|
|
|
# Mock sessions dictionary directly on the client mock
|
|
mock_client.sessions = {"server1": mock_session, "server2": mock_session}
|
|
# Mock get_server_names to return empty list to avoid connection attempt
|
|
mock_client.get_server_names = Mock(return_value=[])
|
|
|
|
code = """
|
|
return {"namespaces": __tool_namespaces}
|
|
"""
|
|
|
|
result = await code_executor.execute(code, timeout=5.0)
|
|
|
|
assert result["error"] is None
|
|
assert set(result["result"]["namespaces"]) == {"server1", "server2"}
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_tool_wrapper_creation(self, mock_client, code_executor):
|
|
"""Test that tool wrappers are created and callable."""
|
|
# Create a mock session with a tool
|
|
mock_session = AsyncMock()
|
|
mock_tool = Mock()
|
|
mock_tool.name = "test_operation"
|
|
mock_tool.description = "Test operation"
|
|
mock_tool.inputSchema = {}
|
|
|
|
mock_session.list_tools = AsyncMock(return_value=[mock_tool])
|
|
|
|
# Mock the call_tool response
|
|
mock_result = Mock()
|
|
mock_result.content = [Mock(text="operation result")]
|
|
mock_session.call_tool = AsyncMock(return_value=mock_result)
|
|
|
|
mock_client.sessions = {"testserver": mock_session}
|
|
mock_client.get_session = Mock(return_value=mock_session)
|
|
|
|
code = """
|
|
# Call the mocked tool
|
|
result = await testserver.test_operation(param1="value1")
|
|
return {"result": result}
|
|
"""
|
|
|
|
result = await code_executor.execute(code, timeout=5.0)
|
|
|
|
assert result["error"] is None
|
|
assert result["result"]["result"] == "operation result"
|
|
mock_session.call_tool.assert_called_once_with("test_operation", {"param1": "value1"})
|
|
|
|
|
|
class TestCodeExecutorErrorHandling:
|
|
"""Test error handling in code execution."""
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_syntax_error(self, code_executor):
|
|
"""Test handling of syntax errors."""
|
|
code = "if True\nreturn 1" # Missing colon
|
|
|
|
result = await code_executor.execute(code, timeout=5.0)
|
|
|
|
assert result["error"] is not None
|
|
# The actual error message might vary by Python version
|
|
assert (
|
|
"syntax" in result["error"].lower()
|
|
or "invalid" in result["error"].lower()
|
|
or "expected" in result["error"].lower()
|
|
)
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_runtime_error(self, code_executor):
|
|
"""Test handling of runtime errors."""
|
|
code = """
|
|
x = 1 / 0 # Division by zero
|
|
return x
|
|
"""
|
|
|
|
result = await code_executor.execute(code, timeout=5.0)
|
|
|
|
assert result["error"] is not None
|
|
assert "division" in result["error"].lower() or "zero" in result["error"].lower()
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_name_error(self, code_executor):
|
|
"""Test handling of name errors."""
|
|
code = "return undefined_variable"
|
|
|
|
result = await code_executor.execute(code, timeout=5.0)
|
|
|
|
assert result["error"] is not None
|
|
assert "name" in result["error"].lower() or "undefined" in result["error"].lower()
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_type_error(self, code_executor):
|
|
"""Test handling of type errors."""
|
|
code = "return 'string' + 123"
|
|
|
|
result = await code_executor.execute(code, timeout=5.0)
|
|
|
|
assert result["error"] is not None
|
|
|
|
|
|
class TestCodeExecutorAsyncSupport:
|
|
"""Test async/await support in code execution."""
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_async_code(self, code_executor):
|
|
"""Test executing async code with await."""
|
|
code = """
|
|
async def my_async_function():
|
|
await asyncio.sleep(0.1)
|
|
return "async result"
|
|
|
|
result = await my_async_function()
|
|
return result
|
|
"""
|
|
|
|
result = await code_executor.execute(code, timeout=5.0)
|
|
|
|
assert result["error"] is None
|
|
assert result["result"] == "async result"
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_multiple_awaits(self, code_executor):
|
|
"""Test multiple await statements."""
|
|
code = """
|
|
async def func1():
|
|
await asyncio.sleep(0.05)
|
|
return 1
|
|
|
|
async def func2():
|
|
await asyncio.sleep(0.05)
|
|
return 2
|
|
|
|
a = await func1()
|
|
b = await func2()
|
|
return a + b
|
|
"""
|
|
|
|
result = await code_executor.execute(code, timeout=5.0)
|
|
|
|
assert result["error"] is None
|
|
assert result["result"] == 3
|