"""Unit tests for ``E2BSandboxProvider`` and its companion ``E2BSandbox``.""" from __future__ import annotations import importlib import threading from collections import OrderedDict from types import SimpleNamespace from typing import Any from unittest.mock import MagicMock import pytest from deerflow.config.paths import Paths # ────────────────────────────────────────────────────────────────────────────── # Fakes for the e2b SDK # ────────────────────────────────────────────────────────────────────────────── class FakeCommandsAPI: """Stand-in for ``client.commands``.""" GONE = "__GONE__" NOT_FOUND_MSG = "The sandbox was not found: This error is likely due to sandbox timeout." def __init__(self, responses: list[Any] | None = None) -> None: self.calls: list[str] = [] self._responses = list(responses or []) def _next(self) -> Any: if not self._responses: return SimpleNamespace(stdout="BOOTSTRAP_OK", stderr="", exit_code=0) head = self._responses.pop(0) return head def run(self, cmd: str, envs: dict[str, str] | None = None, **kwargs) -> SimpleNamespace: self.calls.append(cmd) self.envs = getattr(self, "envs", []) self.envs.append(envs) head = self._next() if head == self.GONE: raise RuntimeError(self.NOT_FOUND_MSG) if callable(head): return head(cmd) if isinstance(head, SimpleNamespace): return head return SimpleNamespace(stdout=str(head), stderr="", exit_code=0) class _FakeFileStream: """Minimal stand-in for ``e2b.FileStreamReader``. Yields fixed-size chunks and tracks whether ``close()`` was invoked so tests can assert we release the connection on both success and abort. """ def __init__(self, data: bytes, *, chunk_size: int = 4096) -> None: self._data = bytes(data) self._chunk_size = max(1, int(chunk_size)) self._offset = 0 self.closed = False def __iter__(self): return self def __next__(self) -> bytes: if self.closed or self._offset >= len(self._data): raise StopIteration end = min(self._offset + self._chunk_size, len(self._data)) chunk = self._data[self._offset : end] self._offset = end return chunk def close(self) -> None: self.closed = True def __enter__(self) -> _FakeFileStream: return self def __exit__(self, *exc_info) -> None: self.close() class FakeFilesAPI: def __init__( self, store: dict[str, bytes] | None = None, *, stream_chunk_size: int = 4096, ) -> None: self.store = dict(store or {}) self.read_calls: list[tuple[str, str | None]] = [] self.write_calls: list[tuple[str, bytes]] = [] self.streams: list[_FakeFileStream] = [] self._stream_chunk_size = stream_chunk_size def read(self, path: str, *, format: str | None = None): self.read_calls.append((path, format)) if path not in self.store: raise FileNotFoundError(path) data = self.store[path] if format == "bytes": return data if format == "stream": stream = _FakeFileStream(data, chunk_size=self._stream_chunk_size) self.streams.append(stream) return stream try: return data.decode("utf-8") except UnicodeDecodeError: return data def write(self, path: str, content: bytes) -> None: self.write_calls.append((path, content)) self.store[path] = content class FakeClient: """Lightweight ``e2b.Sandbox`` substitute used by the provider tests.""" def __init__( self, sandbox_id: str = "fake-sb-1", *, commands: FakeCommandsAPI | None = None, files: FakeFilesAPI | None = None, ) -> None: self.sandbox_id = sandbox_id self.commands = commands or FakeCommandsAPI() self.files = files or FakeFilesAPI() self.timeouts_set: list[int] = [] self.killed = False self.closed = False def set_timeout(self, seconds: int) -> None: self.timeouts_set.append(int(seconds)) def kill(self) -> None: self.killed = True def close(self) -> None: self.closed = True class FakeSandboxClass: """Stand-in for ``e2b_code_interpreter.Sandbox`` (the class itself).""" def __init__(self) -> None: self.create_calls: list[dict[str, Any]] = [] self.connect_calls: list[tuple[str, dict[str, Any]]] = [] self.list_calls: list[dict[str, Any]] = [] self.create_factory = lambda **kw: FakeClient(sandbox_id=f"created-{len(self.create_calls)}") self.connect_factory = lambda sid, **kw: FakeClient(sandbox_id=sid) self.list_return: Any = [] def create(self, **kwargs: Any) -> FakeClient: self.create_calls.append(kwargs) return self.create_factory(**kwargs) def connect(self, sandbox_id: str, **kwargs: Any) -> FakeClient: self.connect_calls.append((sandbox_id, kwargs)) return self.connect_factory(sandbox_id, **kwargs) def list(self, **kwargs: Any) -> Any: self.list_calls.append(kwargs) return self.list_return def _make_provider(*, replicas: int = 3, idle_timeout: int = 1800) -> Any: """Build a ``E2BSandboxProvider`` instance bypassing ``__init__``.""" mod = importlib.import_module("deerflow.community.e2b_sandbox.e2b_sandbox_provider") provider = mod.E2BSandboxProvider.__new__(mod.E2BSandboxProvider) provider._lock = threading.Lock() provider._sandboxes = {} provider._thread_sandboxes = {} provider._thread_locks = {} provider._warm_pool = OrderedDict() provider._shutdown_called = False provider._config = { "api_key": "test-key", "template": "code-interpreter-v1", "domain": None, "home_dir": "/home/user", "idle_timeout": idle_timeout, "replicas": replicas, "mounts": [], "environment": {}, } return provider def _install_fake_sdk(monkeypatch, provider) -> FakeSandboxClass: fake_cls = FakeSandboxClass() monkeypatch.setattr(provider, "_get_sandbox_cls", lambda: fake_cls) return fake_cls def _make_sandbox(client: FakeClient, *, sandbox_id: str | None = None) -> Any: mod = importlib.import_module("deerflow.community.e2b_sandbox.e2b_sandbox") return mod.E2BSandbox( id=sandbox_id or client.sandbox_id, client=client, home_dir="/home/user", ) def test_thread_key_returns_user_thread_tuple(): p = _make_provider() assert p._thread_key("t1", "u1") == ("u1", "t1") def test_stable_seed_is_deterministic_and_user_scoped(): p = _make_provider() s_a = p._stable_seed("t1", "u1") s_b = p._stable_seed("t1", "u1") s_other_user = p._stable_seed("t1", "u2") s_other_thread = p._stable_seed("t2", "u1") assert s_a == s_b assert s_a != s_other_user assert s_a != s_other_thread def test_is_sandbox_gone_error_matches_known_signatures(): mod = importlib.import_module("deerflow.community.e2b_sandbox.e2b_sandbox") f = mod._is_sandbox_gone_error assert f(RuntimeError("Paused sandbox abcdef not found")) assert f(Exception("The sandbox was not found: due to timeout")) assert f(Exception("sandbox not found")) # Unrelated errors must not flip the dead flag. assert not f(Exception("Connection reset by peer")) assert not f(ValueError("invalid path")) def test_execute_command_marks_dead_on_sandbox_gone_error(): client = FakeClient(commands=FakeCommandsAPI([FakeCommandsAPI.GONE])) sb = _make_sandbox(client) out = sb.execute_command("echo hi") assert "Error: " in out and "sandbox was not found" in out assert sb.is_dead is True out2 = sb.execute_command("echo again") assert "reaped" in out2.lower() assert client.commands.calls == ["echo hi"] def test_execute_command_returns_stdout_on_success(): client = FakeClient(commands=FakeCommandsAPI([SimpleNamespace(stdout="hello\n", stderr="", exit_code=0)])) sb = _make_sandbox(client) assert sb.execute_command("printf hello").rstrip() == "hello" assert sb.is_dead is False def test_execute_command_does_not_mark_dead_on_unrelated_error(): def boom(_cmd: str, **kwargs) -> Any: raise RuntimeError("Connection reset by peer") client = FakeClient(commands=FakeCommandsAPI([boom])) sb = _make_sandbox(client) out = sb.execute_command("echo hi") assert "Error" in out assert sb.is_dead is False def test_execute_command_forwards_env_and_timeout_to_commands_run(): """execute_command(env=..., timeout=...) routes env as ``envs`` and the timeout through to ``commands.run`` so request-scoped secrets (#3861) reach the e2b subprocess without entering the command string. Regression for the signature mismatch that broke bash for every e2b user.""" commands = MagicMock() commands.run.return_value = SimpleNamespace(stdout="ok\n", stderr="", exit_code=0) client = FakeClient(commands=commands) sb = _make_sandbox(client) out = sb.execute_command("echo $TOK", env={"TOK": "secret-v"}, timeout=120) assert out.rstrip() == "ok" args, kwargs = commands.run.call_args assert args == ("echo $TOK",) assert kwargs["envs"] == {"TOK": "secret-v"} assert kwargs["timeout"] == 120 # The secret must not be smuggled into the command string. assert "secret-v" not in args[0] def test_execute_command_env_none_passes_no_envs_kwarg(): """env=None is fully backward-compatible — ``commands.run`` is called with no ``envs``/``timeout`` kwargs, so existing (non-secret) callers are unaffected.""" commands = MagicMock() commands.run.return_value = SimpleNamespace(stdout="ok\n", stderr="", exit_code=0) client = FakeClient(commands=commands) sb = _make_sandbox(client) sb.execute_command("echo hi") _, kwargs = commands.run.call_args assert "envs" not in kwargs assert "timeout" not in kwargs def test_execute_command_forwards_env_as_envs(): """Per-call ``env`` reaches the e2b SDK as ``envs`` so secrets like ``GITHUB_TOKEN`` are scoped to a single command without mutating shared state. Mirrors the local/AIO sandboxes' overlay contract. """ client = FakeClient(commands=FakeCommandsAPI([SimpleNamespace(stdout="ok", stderr="", exit_code=0)])) sb = _make_sandbox(client) sb.execute_command("gh pr create", env={"GH_TOKEN": "tok-123"}) assert client.commands.envs == [{"GH_TOKEN": "tok-123"}] def test_execute_command_rejects_invalid_env_key(): client = FakeClient(commands=FakeCommandsAPI([])) sb = _make_sandbox(client) with pytest.raises(ValueError, match="extra_env key"): sb.execute_command("echo hi", env={"X;rm -rf /;Y": "v"}) # The SDK was never reached — validation happens before commands.run. assert client.commands.calls == [] def test_ping_returns_false_when_sandbox_gone(): client = FakeClient(commands=FakeCommandsAPI([FakeCommandsAPI.GONE])) sb = _make_sandbox(client) assert sb.ping() is False assert sb.is_dead is True def test_ping_returns_true_on_unknown_error(): def boom(_cmd: str) -> Any: raise RuntimeError("upstream timeout") client = FakeClient(commands=FakeCommandsAPI([boom])) sb = _make_sandbox(client) assert sb.ping() is True assert sb.is_dead is False def test_client_alive_true_for_healthy_client(): p = _make_provider() client = FakeClient() assert p._client_alive(client) is True assert client.commands.calls == ["true"] def test_client_alive_false_when_sandbox_gone(): p = _make_provider() client = FakeClient(commands=FakeCommandsAPI([FakeCommandsAPI.GONE])) assert p._client_alive(client) is False def test_client_alive_treats_unknown_errors_as_alive(): p = _make_provider() def boom(_cmd: str) -> Any: raise RuntimeError("flaky network") client = FakeClient(commands=FakeCommandsAPI([boom])) assert p._client_alive(client) is True def test_safe_close_client_swallows_close_failures(): p = _make_provider() class BadCloseClient: def close(self) -> None: raise RuntimeError("boom") p._safe_close_client(BadCloseClient()) p._safe_close_client(None) def test_kill_and_close_invokes_kill_and_close_in_order(): p = _make_provider() client = FakeClient() sb = _make_sandbox(client) p._kill_and_close(sb) assert client.killed is True def test_kill_and_close_swallows_kill_exceptions(): p = _make_provider() client = FakeClient() sb = _make_sandbox(client) def explode(): raise RuntimeError("already gone") client.kill = explode p._kill_and_close(sb) def test_reuse_in_process_sandbox_returns_cached_id_on_healthy_reuse(): p = _make_provider() client = FakeClient() sb = _make_sandbox(client, sandbox_id="sb-1") p._sandboxes["sb-1"] = sb p._thread_sandboxes[("u1", "t1")] = "sb-1" sid = p._reuse_in_process_sandbox("t1", user_id="u1") assert sid == "sb-1" assert client.timeouts_set, "expected set_timeout to be called on reuse" def test_reuse_in_process_sandbox_evicts_dead_sandbox(): p = _make_provider() client = FakeClient(commands=FakeCommandsAPI([FakeCommandsAPI.GONE])) sb = _make_sandbox(client, sandbox_id="sb-dead") sb._dead = True p._sandboxes["sb-dead"] = sb p._thread_sandboxes[("u1", "t1")] = "sb-dead" sid = p._reuse_in_process_sandbox("t1", user_id="u1") assert sid is None assert "sb-dead" not in p._sandboxes assert ("u1", "t1") not in p._thread_sandboxes def test_reuse_in_process_sandbox_evicts_when_ping_fails(): p = _make_provider() client = FakeClient(commands=FakeCommandsAPI([FakeCommandsAPI.GONE])) sb = _make_sandbox(client, sandbox_id="sb-stale") p._sandboxes["sb-stale"] = sb p._thread_sandboxes[("u1", "t1")] = "sb-stale" sid = p._reuse_in_process_sandbox("t1", user_id="u1") assert sid is None assert sb.is_dead is True assert "sb-stale" not in p._sandboxes def test_reuse_in_process_sandbox_cleans_dangling_mapping(): p = _make_provider() p._thread_sandboxes[("u1", "t1")] = "ghost" sid = p._reuse_in_process_sandbox("t1", user_id="u1") assert sid is None assert ("u1", "t1") not in p._thread_sandboxes def test_reuse_in_process_sandbox_returns_none_when_no_mapping(): p = _make_provider() assert p._reuse_in_process_sandbox("t-x", user_id="u-x") is None def test_reclaim_warm_pool_sandbox_happy_path(monkeypatch): p = _make_provider() fake_cls = _install_fake_sdk(monkeypatch, p) seed = p._stable_seed("t1", "u1") p._warm_pool["sb-warm"] = (seed, 12345.0) sid = p._reclaim_warm_pool_sandbox("t1", user_id="u1") assert sid == "sb-warm" assert "sb-warm" in p._sandboxes assert p._thread_sandboxes[("u1", "t1")] == "sb-warm" assert "sb-warm" not in p._warm_pool assert [c[0] for c in fake_cls.connect_calls] == ["sb-warm"] def test_reclaim_warm_pool_sandbox_drops_dead_entry(monkeypatch): p = _make_provider() fake_cls = _install_fake_sdk(monkeypatch, p) fake_cls.connect_factory = lambda sid, **kw: FakeClient(sandbox_id=sid, commands=FakeCommandsAPI([FakeCommandsAPI.GONE])) seed = p._stable_seed("t1", "u1") p._warm_pool["sb-zombie"] = (seed, 12345.0) sid = p._reclaim_warm_pool_sandbox("t1", user_id="u1") assert sid is None assert "sb-zombie" not in p._sandboxes assert "sb-zombie" not in p._warm_pool def test_reclaim_warm_pool_sandbox_handles_reconnect_exception(monkeypatch): p = _make_provider() fake_cls = _install_fake_sdk(monkeypatch, p) def boom(sid, **kw): raise RuntimeError("404 Not Found") fake_cls.connect_factory = boom seed = p._stable_seed("t1", "u1") p._warm_pool["sb-broken"] = (seed, 12345.0) sid = p._reclaim_warm_pool_sandbox("t1", user_id="u1") assert sid is None assert "sb-broken" not in p._warm_pool def test_reclaim_warm_pool_sandbox_returns_none_on_seed_mismatch(monkeypatch): p = _make_provider() _install_fake_sdk(monkeypatch, p) p._warm_pool["sb-other"] = ("some-other-seed", 12345.0) assert p._reclaim_warm_pool_sandbox("t1", user_id="u1") is None # The unrelated entry must remain untouched. assert "sb-other" in p._warm_pool class _FakePaginator: """Mirror of e2b SDK's ``SandboxPaginator``: items via ``next_items``.""" def __init__(self, pages: list[list[Any]]) -> None: self._pages = list(pages) self.has_next = bool(self._pages) self.calls = 0 def next_items(self) -> list[Any]: self.calls += 1 if not self._pages: self.has_next = False return [] page = self._pages.pop(0) self.has_next = bool(self._pages) return page def _info(sandbox_id: str, user_id: str, thread_id: str): return SimpleNamespace( sandbox_id=sandbox_id, metadata={ "deer_flow_provider": "e2b_sandbox_provider", "deer_flow_user": user_id, "deer_flow_thread": thread_id, }, ) def test_discover_remote_sandbox_walks_paginator(monkeypatch): p = _make_provider() fake_cls = _install_fake_sdk(monkeypatch, p) fake_cls.list_return = _FakePaginator( [ [_info("sb-other", "u-x", "t-x")], [_info("sb-match", "u1", "t1")], ] ) sid = p._discover_remote_sandbox("t1", user_id="u1") assert sid == "sb-match" assert p._thread_sandboxes[("u1", "t1")] == "sb-match" def test_discover_remote_sandbox_accepts_legacy_list(monkeypatch): p = _make_provider() fake_cls = _install_fake_sdk(monkeypatch, p) fake_cls.list_return = [_info("sb-legacy", "u1", "t1")] sid = p._discover_remote_sandbox("t1", user_id="u1") assert sid == "sb-legacy" def test_discover_remote_sandbox_skips_dead_candidate(monkeypatch): p = _make_provider() fake_cls = _install_fake_sdk(monkeypatch, p) fake_cls.list_return = [_info("sb-dead", "u1", "t1")] fake_cls.connect_factory = lambda sid, **kw: FakeClient(sandbox_id=sid, commands=FakeCommandsAPI([FakeCommandsAPI.GONE])) assert p._discover_remote_sandbox("t1", user_id="u1") is None assert ("u1", "t1") not in p._thread_sandboxes def test_discover_remote_sandbox_returns_none_when_list_raises(monkeypatch): p = _make_provider() fake_cls = _install_fake_sdk(monkeypatch, p) def boom(**kw): raise RuntimeError("API unreachable") fake_cls.list = boom assert p._discover_remote_sandbox("t1", user_id="u1") is None def test_bootstrap_sandbox_paths_emits_expected_script(): p = _make_provider() client = FakeClient() p._bootstrap_sandbox_paths(client) assert len(client.commands.calls) == 1 script = client.commands.calls[0] assert "ln -sfn" in script assert "/mnt/user-data" in script assert "/mnt/acp-workspace" in script assert "BOOTSTRAP_OK" in script for sub in ("workspace", "uploads", "outputs", "acp-workspace"): assert f"/home/user/{sub}" in script def test_bootstrap_sandbox_paths_swallows_command_failure(): p = _make_provider() def boom(_cmd: str) -> Any: raise RuntimeError("sudo not allowed") client = FakeClient(commands=FakeCommandsAPI([boom])) p._bootstrap_sandbox_paths(client) def test_release_unknown_sandbox_id_is_noop(): p = _make_provider() p.release("nonexistent") assert p._warm_pool == OrderedDict() def test_release_dead_sandbox_skips_warm_pool(monkeypatch): p = _make_provider() client = FakeClient() sb = _make_sandbox(client, sandbox_id="sb-dead") sb._dead = True p._sandboxes["sb-dead"] = sb p._thread_sandboxes[("u1", "t1")] = "sb-dead" p.release("sb-dead") assert "sb-dead" not in p._warm_pool, "dead sandbox must not be parked" assert "sb-dead" not in p._sandboxes assert ("u1", "t1") not in p._thread_sandboxes assert client.killed is True, "release of dead sandbox must kill the remote VM" def test_release_healthy_sandbox_parks_in_warm_pool(monkeypatch, tmp_path): p = _make_provider() _setup_paths(monkeypatch, tmp_path) cmds = FakeCommandsAPI([SimpleNamespace(stdout="", stderr="", exit_code=0)]) client = FakeClient(commands=cmds) sb = _make_sandbox(client, sandbox_id="sb-warm-1") p._sandboxes["sb-warm-1"] = sb p._thread_sandboxes[("u1", "t1")] = "sb-warm-1" p.release("sb-warm-1") assert "sb-warm-1" in p._warm_pool seed_in_pool, _ts = p._warm_pool["sb-warm-1"] assert seed_in_pool == p._stable_seed("t1", "u1") assert client.killed is False assert client.timeouts_set def test_release_skips_warm_pool_when_sync_reveals_dead_vm(monkeypatch, tmp_path): p = _make_provider() _setup_paths(monkeypatch, tmp_path) client = FakeClient(commands=FakeCommandsAPI([FakeCommandsAPI.GONE])) sb = _make_sandbox(client, sandbox_id="sb-died-during-sync") p._sandboxes["sb-died-during-sync"] = sb p._thread_sandboxes[("u1", "t1")] = "sb-died-during-sync" p.release("sb-died-during-sync") assert sb.is_dead is True assert "sb-died-during-sync" not in p._warm_pool assert client.killed is True def _setup_paths(monkeypatch, tmp_path): paths_mod = importlib.import_module("deerflow.config.paths") monkeypatch.setattr(paths_mod, "get_paths", lambda: Paths(base_dir=tmp_path), raising=False) def test_sync_outputs_to_host_writes_new_files(monkeypatch, tmp_path): p = _make_provider() _setup_paths(monkeypatch, tmp_path) listing = "13\t/home/user/outputs/random.pdf\x00" files = FakeFilesAPI(store={"/home/user/outputs/random.pdf": b"%PDF-1.4hello"}) cmds = FakeCommandsAPI([SimpleNamespace(stdout=listing, stderr="", exit_code=0)]) client = FakeClient(commands=cmds, files=files) sb = _make_sandbox(client, sandbox_id="sb-sync-1") p._sync_outputs_to_host(sb, thread_id="t1", user_id="u1") expected = Paths(base_dir=tmp_path).thread_dir("t1", user_id="u1") / "user-data" / "outputs" / "random.pdf" assert expected.exists() assert expected.read_bytes() == b"%PDF-1.4hello" def test_sync_outputs_to_host_skips_unchanged_files(monkeypatch, tmp_path): p = _make_provider() _setup_paths(monkeypatch, tmp_path) out_dir = Paths(base_dir=tmp_path).thread_dir("t1", user_id="u1") / "user-data" / "outputs" out_dir.mkdir(parents=True, exist_ok=True) target = out_dir / "random.pdf" target.write_bytes(b"%PDF-1.4hello") listing = "13\t/home/user/outputs/random.pdf\x00" files = FakeFilesAPI(store={"/home/user/outputs/random.pdf": b"DIFFERENT-SAME-LEN"}) cmds = FakeCommandsAPI([SimpleNamespace(stdout=listing, stderr="", exit_code=0)]) client = FakeClient(commands=cmds, files=files) sb = _make_sandbox(client, sandbox_id="sb-sync-2") p._sync_outputs_to_host(sb, thread_id="t1", user_id="u1") assert files.read_calls == [], "size match should skip the download round-trip" assert target.read_bytes() == b"%PDF-1.4hello" def test_sync_outputs_to_host_marks_dead_on_sandbox_gone(monkeypatch, tmp_path): p = _make_provider() _setup_paths(monkeypatch, tmp_path) cmds = FakeCommandsAPI([FakeCommandsAPI.GONE]) client = FakeClient(commands=cmds) sb = _make_sandbox(client, sandbox_id="sb-sync-dead") p._sync_outputs_to_host(sb, thread_id="t1", user_id="u1") assert sb.is_dead is True def test_sync_outputs_to_host_uses_virtual_path_for_download(monkeypatch, tmp_path): """`download_file` requires paths under ``/mnt/user-data``; the sync helper must translate the physical /home/user/... back to the virtual prefix before calling it.""" p = _make_provider() _setup_paths(monkeypatch, tmp_path) listing = "5\t/home/user/outputs/sub/x.txt\x00" files = FakeFilesAPI(store={"/home/user/outputs/sub/x.txt": b"hello"}) cmds = FakeCommandsAPI([SimpleNamespace(stdout=listing, stderr="", exit_code=0)]) client = FakeClient(commands=cmds, files=files) sb = _make_sandbox(client, sandbox_id="sb-sync-3") p._sync_outputs_to_host(sb, thread_id="t1", user_id="u1") read_paths = [r[0] for r in files.read_calls] assert "/home/user/outputs/sub/x.txt" in read_paths def test_sync_outputs_to_host_is_noop_when_client_closed(): p = _make_provider() sb = _make_sandbox(FakeClient(), sandbox_id="sb-x") sb.close() p._sync_outputs_to_host(sb, thread_id="t1", user_id="u1") def test_download_file_uses_streaming_read_and_returns_full_bytes(): payload = b"A" * (128 * 1024) # 128 KiB — well below the cap. files = FakeFilesAPI(store={"/home/user/outputs/small.bin": payload}) client = FakeClient(files=files) sb = _make_sandbox(client, sandbox_id="sb-stream-1") data = sb.download_file("/mnt/user-data/outputs/small.bin") assert data == payload formats_used = [fmt for _p, fmt in files.read_calls] assert "stream" in formats_used, f"expected download_file to invoke read(format='stream'), got {formats_used!r}" assert files.streams, "download_file must actually consume a stream" assert files.streams[-1].closed, "stream must be closed after successful read" def test_download_file_streaming_raises_efbig_before_full_buffering(): import errno as _errno from deerflow.community.e2b_sandbox import e2b_sandbox as e2b_sb_mod cap = e2b_sb_mod._MAX_DOWNLOAD_SIZE class _OversizeStream: def __init__(self) -> None: self.bytes_yielded = 0 self.closed = False self._chunk = b"X" * (1024 * 1024) # 1 MiB per chunk def __iter__(self): return self def __next__(self) -> bytes: if self.closed: raise StopIteration # Yield up to ``cap + a bit`` — the caller must abort before # actually buffering all of that in memory. if self.bytes_yielded > cap + 4 * len(self._chunk): raise StopIteration self.bytes_yielded += len(self._chunk) return self._chunk def close(self) -> None: self.closed = True stream = _OversizeStream() class _StubFilesAPI: def __init__(self) -> None: self.calls: list[tuple[str, str | None]] = [] def read(self, path: str, *, format: str | None = None): self.calls.append((path, format)) assert format == "stream", "provider must request a streamed download" return stream files = _StubFilesAPI() client = FakeClient(files=files) # type: ignore[arg-type] sb = _make_sandbox(client, sandbox_id="sb-stream-oversize") try: sb.download_file("/mnt/user-data/outputs/huge.bin") except OSError as exc: assert exc.errno == _errno.EFBIG, f"expected EFBIG, got errno={exc.errno!r} ({exc})" else: # pragma: no cover - defensive raise AssertionError("download_file must raise OSError(EFBIG) on oversize stream") assert stream.closed is True, "stream must be closed on abort so the pooled connection is released" assert stream.bytes_yielded <= cap + 2 * 1024 * 1024, f"aborted too late: yielded={stream.bytes_yielded} vs cap={cap}" def test_download_file_falls_back_to_buffered_read_for_legacy_sdk(): class _LegacyFilesAPI: def __init__(self, data: bytes) -> None: self._data = data self.calls: list[tuple[str, str | None]] = [] def read(self, path: str, *, format: str | None = None): self.calls.append((path, format)) if format == "stream": raise TypeError("format='stream' unsupported") if format == "bytes": return self._data return self._data.decode("utf-8", errors="replace") files = _LegacyFilesAPI(b"legacy-payload") client = FakeClient(files=files) # type: ignore[arg-type] sb = _make_sandbox(client, sandbox_id="sb-legacy") data = sb.download_file("/mnt/user-data/outputs/legacy.bin") assert data == b"legacy-payload" formats_used = [fmt for _p, fmt in files.calls] assert formats_used == ["stream", "bytes"], f"expected stream then bytes fallback, got {formats_used!r}" def test_sync_outputs_to_host_skips_oversize_files(monkeypatch, tmp_path): from deerflow.community.e2b_sandbox import e2b_sandbox as e2b_sb_mod p = _make_provider() _setup_paths(monkeypatch, tmp_path) oversize = e2b_sb_mod._MAX_DOWNLOAD_SIZE + 1 listing = f"{oversize}\t/home/user/outputs/huge.bin\x00" files = FakeFilesAPI() # no store entry: any read attempt would raise cmds = FakeCommandsAPI([SimpleNamespace(stdout=listing, stderr="", exit_code=0)]) client = FakeClient(commands=cmds, files=files) sb = _make_sandbox(client, sandbox_id="sb-sync-oversize") p._sync_outputs_to_host(sb, thread_id="t1", user_id="u1") assert files.read_calls == [], "oversize files must be skipped without invoking download_file" host_target = Paths(base_dir=tmp_path).thread_dir("t1", user_id="u1") / "user-data" / "outputs" / "huge.bin" assert not host_target.exists(), "no oversize artefact must be written to host"