"""Tests for the Docker-sandboxed code interpreter tool.""" from __future__ import annotations import sys from unittest.mock import MagicMock, patch def _make_docker_mock(): """Create a mock docker module and return (mock_module, mock_client).""" mock_docker = MagicMock() mock_client = MagicMock() mock_docker.from_env.return_value = mock_client return mock_docker, mock_client class TestDockerCodeInterpreterTool: def test_spec(self): from openjarvis.tools.code_interpreter_docker import ( DockerCodeInterpreterTool, ) tool = DockerCodeInterpreterTool() spec = tool.spec assert spec.name == "code_interpreter_docker" assert "code" in spec.parameters["properties"] assert spec.category == "code" def test_empty_code(self): from openjarvis.tools.code_interpreter_docker import ( DockerCodeInterpreterTool, ) tool = DockerCodeInterpreterTool() result = tool.execute(code="") assert not result.success assert "No code" in result.content def test_successful_execution(self): from openjarvis.tools.code_interpreter_docker import ( DockerCodeInterpreterTool, ) mock_docker, mock_client = _make_docker_mock() mock_container = MagicMock() mock_container.wait.return_value = {"StatusCode": 0} mock_container.logs.side_effect = [ b"Hello World\n", # stdout b"", # stderr ] mock_client.containers.run.return_value = mock_container with patch.dict(sys.modules, {"docker": mock_docker}): tool = DockerCodeInterpreterTool() result = tool.execute(code="print('Hello World')") assert result.success assert "Hello World" in result.content mock_container.remove.assert_called_once_with(force=True) def test_execution_error(self): from openjarvis.tools.code_interpreter_docker import ( DockerCodeInterpreterTool, ) mock_docker, mock_client = _make_docker_mock() mock_container = MagicMock() mock_container.wait.return_value = {"StatusCode": 1} mock_container.logs.side_effect = [ b"", b"NameError: name 'foo' is not defined\n", ] mock_client.containers.run.return_value = mock_container with patch.dict(sys.modules, {"docker": mock_docker}): tool = DockerCodeInterpreterTool() result = tool.execute(code="print(foo)") assert not result.success assert "NameError" in result.content def test_container_resource_limits(self): from openjarvis.tools.code_interpreter_docker import ( DockerCodeInterpreterTool, ) mock_docker, mock_client = _make_docker_mock() mock_container = MagicMock() mock_container.wait.return_value = {"StatusCode": 0} mock_container.logs.side_effect = [b"ok\n", b""] mock_client.containers.run.return_value = mock_container tool = DockerCodeInterpreterTool( memory_limit="256m", cpu_count=2, network_disabled=True, pids_limit=50, ) with patch.dict(sys.modules, {"docker": mock_docker}): tool.execute(code="print('ok')") call_kwargs = mock_client.containers.run.call_args assert call_kwargs[1]["mem_limit"] == "256m" assert call_kwargs[1]["nano_cpus"] == 2 * 10**9 assert call_kwargs[1]["network_disabled"] is True assert call_kwargs[1]["pids_limit"] == 50 assert call_kwargs[1]["read_only"] is True def test_output_truncation(self): from openjarvis.tools.code_interpreter_docker import ( DockerCodeInterpreterTool, ) mock_docker, mock_client = _make_docker_mock() mock_container = MagicMock() mock_container.wait.return_value = {"StatusCode": 0} mock_container.logs.side_effect = [b"x" * 20000, b""] mock_client.containers.run.return_value = mock_container with patch.dict(sys.modules, {"docker": mock_docker}): tool = DockerCodeInterpreterTool(max_output=100) result = tool.execute(code="print('x' * 20000)") assert result.success assert len(result.content) < 200 assert "truncated" in result.content def test_container_cleanup_on_error(self): from openjarvis.tools.code_interpreter_docker import ( DockerCodeInterpreterTool, ) mock_docker, mock_client = _make_docker_mock() mock_container = MagicMock() mock_container.wait.side_effect = Exception("timeout") mock_client.containers.run.return_value = mock_container with patch.dict(sys.modules, {"docker": mock_docker}): tool = DockerCodeInterpreterTool() result = tool.execute(code="import time; time.sleep(999)") assert not result.success mock_container.remove.assert_called_once_with(force=True)