-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathtest_provider.py
More file actions
159 lines (117 loc) · 5.25 KB
/
Copy pathtest_provider.py
File metadata and controls
159 lines (117 loc) · 5.25 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
from __future__ import annotations
from typing import TYPE_CHECKING, cast
from unittest.mock import MagicMock, patch
import e2b
import pytest
from langchain_e2b import E2BProvider, E2BSandbox
from langchain_e2b.provider import DEFAULT_SANDBOX_TIMEOUT
if TYPE_CHECKING:
from collections.abc import Callable
def _resolver(values: dict[str, str]) -> Callable[[str], str | None]:
return values.get
def _sandbox(sandbox_id: str = "sbx-test") -> e2b.Sandbox:
return cast("e2b.Sandbox", MagicMock(sandbox_id=sandbox_id))
def test_provider_resolves_api_key_from_environment(
monkeypatch: pytest.MonkeyPatch,
) -> None:
"""The provider can read `E2B_API_KEY` without an explicit constructor key."""
monkeypatch.setenv("E2B_API_KEY", "env-key")
sandbox = _sandbox()
with patch(
"langchain_e2b.provider.e2b.Sandbox.create",
return_value=sandbox,
) as create:
provider = E2BProvider()
backend = provider.get_or_create()
create.assert_called_once_with(
timeout=DEFAULT_SANDBOX_TIMEOUT,
api_key="env-key",
)
assert isinstance(backend, E2BSandbox)
assert backend.id == "sbx-test"
def test_provider_requires_api_key() -> None:
"""A missing API key should fail before any SDK call is attempted."""
with pytest.raises(ValueError, match="No E2B API key found"):
E2BProvider(resolve_env_var=_resolver({}))
def test_provider_creates_sandbox_with_default_options() -> None:
"""Creating a sandbox uses the package-owned lifecycle implementation."""
sandbox = _sandbox("sbx-new")
with patch(
"langchain_e2b.provider.e2b.Sandbox.create",
return_value=sandbox,
) as create:
provider = E2BProvider(api_key="fake-key", resolve_env_var=_resolver({}))
backend = provider.get_or_create(timeout=2)
create.assert_called_once_with(
timeout=DEFAULT_SANDBOX_TIMEOUT,
api_key="fake-key",
)
assert backend.id == "sbx-new"
def test_provider_creates_sandbox_with_template_and_timeout() -> None:
"""Template and sandbox lifetime are configured by E2B environment values."""
sandbox = _sandbox("sbx-template")
resolve_env_var = _resolver(
{
"E2B_TEMPLATE": "custom-template",
"E2B_SANDBOX_TIMEOUT": "7200",
}
)
with patch(
"langchain_e2b.provider.e2b.Sandbox.create",
return_value=sandbox,
) as create:
provider = E2BProvider(api_key="fake-key", resolve_env_var=resolve_env_var)
backend = provider.get_or_create()
create.assert_called_once_with(
template="custom-template",
timeout=7200,
api_key="fake-key",
)
assert backend.id == "sbx-template"
@pytest.mark.parametrize("value", ["not-a-number", "0", "-1"])
def test_provider_rejects_invalid_sandbox_timeout(value: str) -> None:
"""Timeout configuration should fail with a clear error."""
resolve_env_var = _resolver({"E2B_SANDBOX_TIMEOUT": value})
with pytest.raises(ValueError, match="E2B_SANDBOX_TIMEOUT"):
E2BProvider(api_key="fake-key", resolve_env_var=resolve_env_var)
def test_provider_connects_existing_sandbox() -> None:
"""Providing `sandbox_id` reconnects instead of creating a new sandbox."""
sandbox = _sandbox("sbx-existing")
with patch(
"langchain_e2b.provider.e2b.Sandbox.connect",
return_value=sandbox,
) as connect:
provider = E2BProvider(api_key="fake-key", resolve_env_var=_resolver({}))
backend = provider.get_or_create(sandbox_id="sbx-existing")
connect.assert_called_once_with(
"sbx-existing",
timeout=DEFAULT_SANDBOX_TIMEOUT,
api_key="fake-key",
)
assert backend.id == "sbx-existing"
def test_provider_translates_missing_sandbox_to_key_error() -> None:
"""Deep Agents adapters can map missing sandboxes without importing E2B SDK."""
with patch(
"langchain_e2b.provider.e2b.Sandbox.connect",
side_effect=e2b.SandboxNotFoundException("missing"),
):
provider = E2BProvider(api_key="fake-key", resolve_env_var=_resolver({}))
with pytest.raises(KeyError) as exc_info:
provider.get_or_create(sandbox_id="sbx-missing")
assert exc_info.value.args == ("sbx-missing",)
def test_provider_rejects_unsupported_get_options() -> None:
"""Unsupported lifecycle options fail in the package provider."""
provider = E2BProvider(api_key="fake-key", resolve_env_var=_resolver({}))
with pytest.raises(TypeError, match="Received unsupported arguments: metadata"):
provider.get_or_create(metadata={"purpose": "test"})
def test_provider_deletes_sandbox() -> None:
"""Delete delegates to the E2B SDK lifecycle operation."""
with patch("langchain_e2b.provider.e2b.Sandbox.kill") as kill:
provider = E2BProvider(api_key="fake-key", resolve_env_var=_resolver({}))
provider.delete(sandbox_id="sbx-delete")
kill.assert_called_once_with("sbx-delete", api_key="fake-key")
def test_provider_rejects_unsupported_delete_options() -> None:
"""Delete only accepts the sandbox ID."""
provider = E2BProvider(api_key="fake-key", resolve_env_var=_resolver({}))
with pytest.raises(TypeError, match="Received unsupported arguments: force"):
provider.delete(sandbox_id="sbx-delete", force=True)