104 lines
3.7 KiB
Python
104 lines
3.7 KiB
Python
from __future__ import annotations
|
|
|
|
import pytest
|
|
from pathlib import Path
|
|
import tempfile
|
|
|
|
from core.tools.base import ToolContext
|
|
from tools.python_exec import PythonExecTool
|
|
|
|
|
|
class TestPythonExecTool:
|
|
"""Test Python execution tool."""
|
|
|
|
@pytest.fixture
|
|
def tool(self) -> PythonExecTool:
|
|
return PythonExecTool()
|
|
|
|
def test_execute_snippet_simple(self, tool: PythonExecTool) -> None:
|
|
"""Test executing a simple code snippet."""
|
|
ctx = ToolContext(dry_run=False)
|
|
result = tool.execute_snippet(
|
|
{"code": "x = 1 + 1\nprint(x)"},
|
|
ctx
|
|
)
|
|
assert result["status"] == "success"
|
|
assert "2" in result["output"]
|
|
|
|
def test_execute_snippet_with_error(self, tool: PythonExecTool) -> None:
|
|
"""Test executing code with error."""
|
|
ctx = ToolContext(dry_run=False)
|
|
result = tool.execute_snippet(
|
|
{"code": "x = 1 / 0"},
|
|
ctx
|
|
)
|
|
assert result["status"] == "error"
|
|
assert "ZeroDivisionError" in result["error_type"]
|
|
|
|
def test_execute_snippet_invalid_code_type(self, tool: PythonExecTool) -> None:
|
|
"""Test with invalid code type."""
|
|
ctx = ToolContext(dry_run=False)
|
|
with pytest.raises(ValueError, match="code must be string"):
|
|
tool.execute_snippet({"code": 123}, ctx)
|
|
|
|
def test_execute_snippet_dry_run(self, tool: PythonExecTool) -> None:
|
|
"""Test snippet execution in dry-run mode."""
|
|
ctx = ToolContext(dry_run=True)
|
|
result = tool.execute_snippet(
|
|
{"code": "x = 1 + 1"},
|
|
ctx
|
|
)
|
|
assert result["dry_run"] is True
|
|
assert result["message"] == "Would execute code (dry-run mode)"
|
|
|
|
def test_execute_script(self, tool: PythonExecTool) -> None:
|
|
"""Test executing a Python script."""
|
|
with tempfile.NamedTemporaryFile(mode="w", suffix=".py", delete=False) as f:
|
|
f.write("print('hello from script')\n")
|
|
script_path = f.name
|
|
|
|
try:
|
|
ctx = ToolContext(dry_run=False)
|
|
result = tool.execute_script(
|
|
{"script": script_path, "timeout": 10},
|
|
ctx
|
|
)
|
|
assert result["status"] == "success"
|
|
assert "hello from script" in result["stdout"]
|
|
finally:
|
|
Path(script_path).unlink()
|
|
|
|
def test_execute_script_nonexistent(self, tool: PythonExecTool) -> None:
|
|
"""Test executing nonexistent script."""
|
|
with pytest.raises(ValueError, match="Script not found"):
|
|
tool.execute_script({"script": "/nonexistent/script.py"}, ToolContext())
|
|
|
|
def test_execute_script_dry_run(self, tool: PythonExecTool) -> None:
|
|
"""Test script execution in dry-run mode."""
|
|
with tempfile.NamedTemporaryFile(mode="w", suffix=".py", delete=False) as f:
|
|
f.write("print('test')\n")
|
|
script_path = f.name
|
|
|
|
try:
|
|
ctx = ToolContext(dry_run=True)
|
|
result = tool.execute_script({"script": script_path}, ctx)
|
|
assert result["dry_run"] is True
|
|
finally:
|
|
Path(script_path).unlink()
|
|
|
|
def test_execute_action_snippet(self, tool: PythonExecTool) -> None:
|
|
"""Test execute with snippet action."""
|
|
ctx = ToolContext(dry_run=False)
|
|
result = tool.execute(
|
|
{"action": "snippet", "code": "x = 42\nprint(x)"},
|
|
ctx
|
|
)
|
|
assert result["status"] == "success"
|
|
assert "42" in result["output"]
|
|
|
|
def test_execute_invalid_action(self, tool: PythonExecTool) -> None:
|
|
"""Test with invalid action."""
|
|
ctx = ToolContext(dry_run=False)
|
|
with pytest.raises(ValueError, match="Unknown exec action"):
|
|
tool.execute({"action": "invalid_action"}, ctx)
|
