Skip to content
This repository was archived by the owner on Nov 10, 2025. It is now read-only.

Commit 9aea8ac

Browse files
committed
test: add tests to cover sandbox code execution
1 parent 3c36d30 commit 9aea8ac

File tree

2 files changed

+157
-40
lines changed

2 files changed

+157
-40
lines changed

crewai_tools/tools/code_interpreter_tool/code_interpreter_tool.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,6 @@ class SandboxPython:
4545
"eval",
4646
"open",
4747
"compile",
48-
"__import__",
4948
"input",
5049
"globals",
5150
"locals",
@@ -194,7 +193,7 @@ def run_code_safety(self, code: str, libraries_used: List[str]) -> str:
194193
return self.run_code_in_restricted_sandbox(code)
195194

196195
def run_code_in_docker(self, code: str, libraries_used: List[str]) -> str:
197-
Printer.print("Running code in docker", color="bold_blue")
196+
Printer.print("Running code in Docker environment", color="bold_blue")
198197
self._verify_docker_image()
199198
container = self._init_docker_container()
200199
self._install_libraries(container, libraries_used)
Lines changed: 156 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,57 +1,175 @@
1-
import unittest
21
from unittest.mock import patch
32

3+
import pytest
4+
45
from crewai_tools.tools.code_interpreter_tool.code_interpreter_tool import (
56
CodeInterpreterTool,
7+
SandboxPython,
68
)
79

810

9-
class TestCodeInterpreterTool(unittest.TestCase):
10-
@patch(
11-
"crewai_tools.tools.code_interpreter_tool.code_interpreter_tool.docker_from_env"
11+
@pytest.fixture
12+
def printer_mock():
13+
with patch("crewai_tools.printer.Printer.print") as mock:
14+
yield mock
15+
16+
17+
@pytest.fixture
18+
def docker_unavailable_mock():
19+
with patch(
20+
"crewai_tools.tools.code_interpreter_tool.code_interpreter_tool.CodeInterpreterTool._check_docker_available",
21+
return_value=False,
22+
) as mock:
23+
yield mock
24+
25+
26+
@patch("crewai_tools.tools.code_interpreter_tool.code_interpreter_tool.docker_from_env")
27+
def test_run_code_in_docker(docker_mock, printer_mock):
28+
tool = CodeInterpreterTool()
29+
code = "print('Hello, World!')"
30+
libraries_used = ["numpy", "pandas"]
31+
expected_output = "Hello, World!\n"
32+
33+
docker_mock().containers.run().exec_run().exit_code = 0
34+
docker_mock().containers.run().exec_run().output = expected_output.encode()
35+
36+
result = tool.run_code_in_docker(code, libraries_used)
37+
assert result == expected_output
38+
printer_mock.assert_called_with(
39+
"Running code in Docker environment", color="bold_blue"
1240
)
13-
def test_run_code_in_docker(self, docker_mock):
14-
tool = CodeInterpreterTool()
15-
code = "print('Hello, World!')"
16-
libraries_used = ["numpy", "pandas"]
17-
expected_output = "Hello, World!\n"
1841

19-
docker_mock().containers.run().exec_run().exit_code = 0
20-
docker_mock().containers.run().exec_run().output = expected_output.encode()
21-
result = tool.run_code_in_docker(code, libraries_used)
2242

23-
self.assertEqual(result, expected_output)
43+
@patch("crewai_tools.tools.code_interpreter_tool.code_interpreter_tool.docker_from_env")
44+
def test_run_code_in_docker_with_error(docker_mock, printer_mock):
45+
tool = CodeInterpreterTool()
46+
code = "print(1/0)"
47+
libraries_used = ["numpy", "pandas"]
48+
expected_output = "Something went wrong while running the code: \nZeroDivisionError: division by zero\n"
49+
50+
docker_mock().containers.run().exec_run().exit_code = 1
51+
docker_mock().containers.run().exec_run().output = (
52+
b"ZeroDivisionError: division by zero\n"
53+
)
54+
55+
result = tool.run_code_in_docker(code, libraries_used)
56+
assert result == expected_output
57+
printer_mock.assert_called_with(
58+
"Running code in Docker environment", color="bold_blue"
59+
)
60+
61+
62+
@patch("crewai_tools.tools.code_interpreter_tool.code_interpreter_tool.docker_from_env")
63+
def test_run_code_in_docker_with_script(docker_mock, printer_mock):
64+
tool = CodeInterpreterTool()
65+
code = """print("This is line 1")
66+
print("This is line 2")"""
67+
libraries_used = []
68+
expected_output = "This is line 1\nThis is line 2\n"
69+
70+
docker_mock().containers.run().exec_run().exit_code = 0
71+
docker_mock().containers.run().exec_run().output = expected_output.encode()
72+
73+
result = tool.run_code_in_docker(code, libraries_used)
74+
assert result == expected_output
75+
printer_mock.assert_called_with(
76+
"Running code in Docker environment", color="bold_blue"
77+
)
78+
2479

25-
@patch(
26-
"crewai_tools.tools.code_interpreter_tool.code_interpreter_tool.docker_from_env"
80+
def test_restricted_sandbox_basic_code_execution(printer_mock, docker_unavailable_mock):
81+
"""Test basic code execution."""
82+
tool = CodeInterpreterTool()
83+
code = """
84+
result = 2 + 2
85+
print(result)
86+
"""
87+
result = tool.run(code=code, libraries_used=[])
88+
printer_mock.assert_called_with(
89+
"Running code in restricted sandbox", color="yellow"
2790
)
28-
def test_run_code_in_docker_with_error(self, docker_mock):
29-
tool = CodeInterpreterTool()
30-
code = "print(1/0)"
31-
libraries_used = ["numpy", "pandas"]
32-
expected_output = "Something went wrong while running the code: \nZeroDivisionError: division by zero\n"
91+
assert result == 4
92+
3393

34-
docker_mock().containers.run().exec_run().exit_code = 1
35-
docker_mock().containers.run().exec_run().output = (
36-
b"ZeroDivisionError: division by zero\n"
94+
def test_restricted_sandbox_running_with_blocked_modules(
95+
printer_mock, docker_unavailable_mock
96+
):
97+
"""Test that restricted modules cannot be imported."""
98+
tool = CodeInterpreterTool()
99+
restricted_modules = SandboxPython.BLOCKED_MODULES
100+
101+
for module in restricted_modules:
102+
code = f"""
103+
import {module}
104+
result = "Import succeeded"
105+
"""
106+
result = tool.run(code=code, libraries_used=[])
107+
printer_mock.assert_called_with(
108+
"Running code in restricted sandbox", color="yellow"
37109
)
38-
result = tool.run_code_in_docker(code, libraries_used)
39110

40-
self.assertEqual(result, expected_output)
111+
assert f"An error occurred: Importing '{module}' is not allowed" in result
112+
41113

42-
@patch(
43-
"crewai_tools.tools.code_interpreter_tool.code_interpreter_tool.docker_from_env"
114+
def test_restricted_sandbox_running_with_blocked_builtins(
115+
printer_mock, docker_unavailable_mock
116+
):
117+
"""Test that restricted builtins are not available."""
118+
tool = CodeInterpreterTool()
119+
restricted_builtins = SandboxPython.UNSAFE_BUILTINS
120+
121+
for builtin in restricted_builtins:
122+
code = f"""
123+
{builtin}("test")
124+
result = "Builtin available"
125+
"""
126+
result = tool.run(code=code, libraries_used=[])
127+
printer_mock.assert_called_with(
128+
"Running code in restricted sandbox", color="yellow"
129+
)
130+
assert f"An error occurred: name '{builtin}' is not defined" in result
131+
132+
133+
def test_restricted_sandbox_running_with_no_result_variable(
134+
printer_mock, docker_unavailable_mock
135+
):
136+
"""Test behavior when no result variable is set."""
137+
tool = CodeInterpreterTool()
138+
code = """
139+
x = 10
140+
"""
141+
result = tool.run(code=code, libraries_used=[])
142+
printer_mock.assert_called_with(
143+
"Running code in restricted sandbox", color="yellow"
44144
)
45-
def test_run_code_in_docker_with_script(self, docker_mock):
46-
tool = CodeInterpreterTool()
47-
code = """print("This is line 1")
48-
print("This is line 2")"""
49-
libraries_used = [] # No additional libraries needed for this test
50-
expected_output = "This is line 1\nThis is line 2\n"
145+
assert result == "No result variable found."
51146

52-
# Mock Docker responses
53-
docker_mock().containers.run().exec_run().exit_code = 0
54-
docker_mock().containers.run().exec_run().output = expected_output.encode()
55147

56-
result = tool.run_code_in_docker(code, libraries_used)
57-
self.assertEqual(result, expected_output)
148+
def test_unsafe_mode_running_with_no_result_variable(
149+
printer_mock, docker_unavailable_mock
150+
):
151+
"""Test behavior when no result variable is set."""
152+
tool = CodeInterpreterTool(unsafe_mode=True)
153+
code = """
154+
x = 10
155+
"""
156+
result = tool.run(code=code, libraries_used=[])
157+
printer_mock.assert_called_with(
158+
"WARNING: Running code in unsafe mode", color="bold_magenta"
159+
)
160+
assert result == "No result variable found."
161+
162+
163+
def test_unsafe_mode_running_unsafe_code(printer_mock, docker_unavailable_mock):
164+
"""Test behavior when no result variable is set."""
165+
tool = CodeInterpreterTool(unsafe_mode=True)
166+
code = """
167+
import os
168+
os.system("ls -la")
169+
result = eval("5/1")
170+
"""
171+
result = tool.run(code=code, libraries_used=[])
172+
printer_mock.assert_called_with(
173+
"WARNING: Running code in unsafe mode", color="bold_magenta"
174+
)
175+
assert 5.0 == result

0 commit comments

Comments
 (0)