110 lines
3.7 KiB
Python
110 lines
3.7 KiB
Python
from __future__ import annotations
|
|
|
|
import importlib
|
|
from typing import Literal
|
|
|
|
import pytest
|
|
|
|
from agents.extensions.sandbox.cloudflare import CloudflareSandboxClientOptions
|
|
from agents.extensions.sandbox.daytona import DaytonaSandboxClientOptions
|
|
from agents.extensions.sandbox.e2b import E2BSandboxClientOptions
|
|
from agents.sandbox.config import DEFAULT_PYTHON_SANDBOX_IMAGE
|
|
from agents.sandbox.sandboxes import DockerSandboxClientOptions, UnixLocalSandboxClientOptions
|
|
from agents.sandbox.session import BaseSandboxClientOptions
|
|
|
|
|
|
def test_sandbox_client_options_parse_uses_registered_builtin_type() -> None:
|
|
parsed = BaseSandboxClientOptions.parse(
|
|
{
|
|
"type": "docker",
|
|
"image": DEFAULT_PYTHON_SANDBOX_IMAGE,
|
|
"exposed_ports": [8080],
|
|
}
|
|
)
|
|
|
|
assert parsed == DockerSandboxClientOptions(
|
|
image=DEFAULT_PYTHON_SANDBOX_IMAGE, exposed_ports=(8080,)
|
|
)
|
|
|
|
|
|
def test_sandbox_client_options_parse_passthrough_existing_instance() -> None:
|
|
options = UnixLocalSandboxClientOptions(exposed_ports=(8080,))
|
|
|
|
parsed = BaseSandboxClientOptions.parse(options)
|
|
|
|
assert parsed is options
|
|
|
|
|
|
def test_sandbox_client_options_exclude_unset_preserves_type_discriminator() -> None:
|
|
try:
|
|
modal_module = importlib.import_module("agents.extensions.sandbox.modal")
|
|
except ModuleNotFoundError:
|
|
pytest.skip("modal is not installed")
|
|
|
|
payload = modal_module.ModalSandboxClientOptions(app_name="sandbox-tests").model_dump(
|
|
exclude_unset=True
|
|
)
|
|
|
|
assert payload == {
|
|
"type": "modal",
|
|
"app_name": "sandbox-tests",
|
|
"sandbox_create_timeout_s": None,
|
|
"workspace_persistence": "tar",
|
|
"snapshot_filesystem_timeout_s": None,
|
|
"snapshot_filesystem_restore_timeout_s": None,
|
|
"exposed_ports": (),
|
|
"gpu": None,
|
|
"timeout": 300,
|
|
"use_sleep_cmd": True,
|
|
"image_builder_version": "2025.06",
|
|
"idle_timeout": None,
|
|
}
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"options",
|
|
[
|
|
DockerSandboxClientOptions(image=DEFAULT_PYTHON_SANDBOX_IMAGE, exposed_ports=(8080,)),
|
|
UnixLocalSandboxClientOptions(exposed_ports=(8080,)),
|
|
E2BSandboxClientOptions(sandbox_type="e2b", template="base"),
|
|
DaytonaSandboxClientOptions(image=DEFAULT_PYTHON_SANDBOX_IMAGE),
|
|
CloudflareSandboxClientOptions(worker_url="https://example.com"),
|
|
],
|
|
)
|
|
def test_sandbox_client_options_roundtrip_preserves_concrete_type(
|
|
options: BaseSandboxClientOptions,
|
|
) -> None:
|
|
payload = options.model_dump(mode="json")
|
|
|
|
restored = BaseSandboxClientOptions.parse(payload)
|
|
|
|
assert restored == options
|
|
assert type(restored) is type(options)
|
|
|
|
|
|
def test_sandbox_client_options_parse_rejects_unknown_type() -> None:
|
|
with pytest.raises(ValueError, match="unknown sandbox client options type `unknown`"):
|
|
BaseSandboxClientOptions.parse({"type": "unknown"})
|
|
|
|
|
|
def test_sandbox_client_options_parse_rejects_invalid_payload() -> None:
|
|
with pytest.raises(
|
|
TypeError,
|
|
match="sandbox client options payload must be a BaseSandboxClientOptions or object payload",
|
|
):
|
|
BaseSandboxClientOptions.parse("docker")
|
|
|
|
|
|
def test_duplicate_sandbox_client_options_type_registration_raises() -> None:
|
|
with pytest.raises(TypeError, match="already registered"):
|
|
|
|
class DuplicateDockerSandboxClientOptions(BaseSandboxClientOptions):
|
|
type: Literal["docker"] = "docker"
|
|
|
|
|
|
def test_sandbox_client_options_subclasses_require_type_discriminator_default() -> None:
|
|
with pytest.raises(TypeError, match="must define a non-empty string default for `type`"):
|
|
|
|
class MissingTypeSandboxClientOptions(BaseSandboxClientOptions):
|
|
pass
|