Skip to content

Commit 6a896ca

Browse files
tholorclaude
andcommitted
test: add integration tests for e2b sandbox tools
Adds real end-to-end integration tests (marked @pytest.mark.integration) that exercise all four tools against a live E2B sandbox: - RunBashCommandTool: echo, non-zero exit code, stderr capture - WriteFileTool + ReadFileTool: round-trip, nested directory creation - ListDirectoryTool: list /tmp, list after write - E2BToolset: warm_up/close lifecycle, shared sandbox state across tools Also suppresses S108 (/tmp path warning) in test per-file-ignores — /tmp is correct and intentional inside a sandboxed environment. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 87825f4 commit 6a896ca

2 files changed

Lines changed: 118 additions & 1 deletion

File tree

integrations/e2b/pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ ban-relative-imports = "parents"
144144

145145
[tool.ruff.lint.per-file-ignores]
146146
# Tests can use magic values, assertions, relative imports, and don't need type annotations
147-
"tests/**/*" = ["PLR2004", "S101", "TID252", "D", "ANN"]
147+
"tests/**/*" = ["PLR2004", "S101", "S108", "TID252", "D", "ANN"]
148148
"examples/**/*" = ["T201", "E501", "ANN", "D", "S101"]
149149

150150
[tool.coverage.run]
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
# SPDX-FileCopyrightText: 2022-present deepset GmbH <info@deepset.ai>
2+
#
3+
# SPDX-License-Identifier: Apache-2.0
4+
5+
"""
6+
Integration tests for the E2B sandbox tools.
7+
8+
These tests require a valid E2B_API_KEY environment variable and will
9+
spin up a real cloud sandbox on each run.
10+
"""
11+
12+
import pytest
13+
14+
from haystack_integrations.tools.e2b import (
15+
E2BSandbox,
16+
E2BToolset,
17+
ListDirectoryTool,
18+
ReadFileTool,
19+
RunBashCommandTool,
20+
WriteFileTool,
21+
)
22+
23+
24+
@pytest.fixture(scope="module")
25+
def sandbox():
26+
"""Shared sandbox for the module — spun up once, torn down after all tests."""
27+
sb = E2BSandbox()
28+
sb.warm_up()
29+
yield sb
30+
sb.close()
31+
32+
33+
@pytest.mark.integration
34+
class TestRunBashCommandToolIntegration:
35+
def test_echo_command(self, sandbox):
36+
tool = RunBashCommandTool(sandbox=sandbox)
37+
result = tool.invoke(command="echo 'hello from e2b'")
38+
assert "hello from e2b" in result
39+
assert "exit_code: 0" in result
40+
41+
def test_exit_code_nonzero(self, sandbox):
42+
tool = RunBashCommandTool(sandbox=sandbox)
43+
result = tool.invoke(command="exit 42")
44+
assert "exit_code: 42" in result
45+
46+
def test_stderr_captured(self, sandbox):
47+
tool = RunBashCommandTool(sandbox=sandbox)
48+
result = tool.invoke(command="echo error_msg >&2")
49+
assert "error_msg" in result
50+
51+
52+
@pytest.mark.integration
53+
class TestWriteAndReadFileToolIntegration:
54+
def test_write_then_read(self, sandbox):
55+
write_tool = WriteFileTool(sandbox=sandbox)
56+
read_tool = ReadFileTool(sandbox=sandbox)
57+
58+
write_result = write_tool.invoke(path="/tmp/test_haystack.txt", content="haystack e2b integration")
59+
assert "/tmp/test_haystack.txt" in write_result
60+
61+
read_result = read_tool.invoke(path="/tmp/test_haystack.txt")
62+
assert read_result == "haystack e2b integration"
63+
64+
def test_write_creates_parent_dirs(self, sandbox):
65+
write_tool = WriteFileTool(sandbox=sandbox)
66+
read_tool = ReadFileTool(sandbox=sandbox)
67+
68+
write_tool.invoke(path="/tmp/e2b_test_dir/nested/file.txt", content="nested content")
69+
result = read_tool.invoke(path="/tmp/e2b_test_dir/nested/file.txt")
70+
assert result == "nested content"
71+
72+
73+
@pytest.mark.integration
74+
class TestListDirectoryToolIntegration:
75+
def test_list_tmp(self, sandbox):
76+
tool = ListDirectoryTool(sandbox=sandbox)
77+
result = tool.invoke(path="/tmp")
78+
# /tmp always exists and is listable; result is a newline-separated string or "(empty directory)"
79+
assert isinstance(result, str)
80+
81+
def test_lists_written_file(self, sandbox):
82+
write_tool = WriteFileTool(sandbox=sandbox)
83+
list_tool = ListDirectoryTool(sandbox=sandbox)
84+
85+
write_tool.invoke(path="/tmp/e2b_list_test/myfile.txt", content="data")
86+
result = list_tool.invoke(path="/tmp/e2b_list_test")
87+
assert "myfile.txt" in result
88+
89+
90+
@pytest.mark.integration
91+
class TestE2BToolsetIntegration:
92+
def test_toolset_warm_up_and_close(self):
93+
ts = E2BToolset()
94+
ts.warm_up()
95+
# Verify sandbox is live by running a command through the bash tool
96+
bash_tool = next(t for t in ts if t.name == "run_bash_command")
97+
result = bash_tool.invoke(command="echo 'toolset ok'")
98+
assert "toolset ok" in result
99+
ts.close()
100+
101+
def test_all_tools_share_sandbox(self):
102+
ts = E2BToolset()
103+
ts.warm_up()
104+
105+
write_tool = next(t for t in ts if t.name == "write_file")
106+
read_tool = next(t for t in ts if t.name == "read_file")
107+
bash_tool = next(t for t in ts if t.name == "run_bash_command")
108+
109+
# Write via write_file, read back via bash — proves shared sandbox
110+
write_tool.invoke(path="/tmp/shared_test.txt", content="shared sandbox state")
111+
bash_result = bash_tool.invoke(command="cat /tmp/shared_test.txt")
112+
assert "shared sandbox state" in bash_result
113+
114+
read_result = read_tool.invoke(path="/tmp/shared_test.txt")
115+
assert read_result == "shared sandbox state"
116+
117+
ts.close()

0 commit comments

Comments
 (0)