diff --git a/tests/tools/test_terminal_sandbox_builder_dispatch.py b/tests/tools/test_terminal_sandbox_builder_dispatch.py new file mode 100644 index 0000000000..5a84eaf5e5 --- /dev/null +++ b/tests/tools/test_terminal_sandbox_builder_dispatch.py @@ -0,0 +1,68 @@ +"""Regression coverage for sandbox backend builder dispatch (#112715).""" + +import pytest + +from tools import terminal_tool_backends as backends + + +@pytest.mark.parametrize( + ("env_type", "expects_image"), + [ + ("singularity", True), + ("daytona", True), + ("vercel_sandbox", False), + ], +) +def test_create_environment_dispatches_each_sandbox_builder_once( + monkeypatch, env_type, expects_image +): + """The bound backend name must not collide with the generic dispatcher kwarg.""" + received = {} + + class FakeEnvironment: + def __init__(self, **kwargs): + received.update(kwargs) + + monkeypatch.setitem( + backends._SANDBOX_ROWS, + env_type, + (lambda: FakeEnvironment, expects_image, lambda _cc, _kwargs: {}), + ) + + environment = backends._create_environment( + env_type=env_type, + image="test-image", + cwd="/workspace", + timeout=42, + container_config={}, + task_id="test-task", + ) + + assert isinstance(environment, FakeEnvironment) + assert received["cwd"] == "/workspace" + assert received["timeout"] == 42 + assert received["task_id"] == "test-task" + assert ("image" in received) is expects_image + + +def test_create_environment_keeps_env_type_for_plugin_backends(monkeypatch): + """Unknown backends still receive their name for plugin provider lookup.""" + received = {} + + def build_plugin_env(**kwargs): + received.update(kwargs) + return object() + + monkeypatch.setattr(backends, "_build_plugin_env", build_plugin_env) + + backends._create_environment( + env_type="plugin-sandbox", + image="test-image", + cwd="/workspace", + timeout=42, + container_config={}, + task_id="test-task", + ) + + assert received["env_type"] == "plugin-sandbox" + assert received["image"] == "test-image" diff --git a/tools/terminal_tool_backends.py b/tools/terminal_tool_backends.py index 8ce7bfd76c..7bcb060449 100644 --- a/tools/terminal_tool_backends.py +++ b/tools/terminal_tool_backends.py @@ -240,9 +240,12 @@ def _create_environment(env_type: str, image: str, cwd: str, timeout: int, for local/ssh/vercel; ``container_config`` carries the container_*/docker_* resource keys; ``host_cwd`` is the host dir bound into Docker when cwd mounting is enabled. ``probe_only`` asks ssh for a throwaway connection with no remote setup/sync (the prompt-time probe). Unknown types fall through to plugin backends.""" - builder = _ENV_BUILDERS.get(env_type, _build_plugin_env) - return builder(env_type=env_type, image=image, cwd=cwd, timeout=timeout, cc=container_config or {}, - task_id=task_id, ssh_config=ssh_config, host_cwd=host_cwd, probe_only=probe_only) + builder = _ENV_BUILDERS.get(env_type) + kwargs = dict(image=image, cwd=cwd, timeout=timeout, cc=container_config or {}, task_id=task_id, + ssh_config=ssh_config, host_cwd=host_cwd, probe_only=probe_only) + if builder is not None: + return builder(**kwargs) + return _build_plugin_env(env_type=env_type, **kwargs) # --- Requirement checkers: one generic path driven by _BACKEND_SPECS; optional fields, checked in order: