Files
Enrico Toniato 266a445934 Canary (#524)
* 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>
2025-11-24 20:59:22 +01:00

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