Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 9 additions & 18 deletions deploy/install/test_sandbox_setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,15 @@


class SandboxSetupTests(unittest.TestCase):
def test_e2b_endpoint_pair(self):
for api_url, domain in ((None, None), ("https://sandbox-test.sandbase.ai", "sandbox-test.sandbase.ai")):
self.assertTrue(sandbox_setup.e2b_endpoint(api_url, domain))
for api_url, domain in (("https://sandbox-test.sandbase.ai", None),
("http://sandbox-test.sandbase.ai", "sandbox-test.sandbase.ai"),
("https://sandbox-test.sandbase.ai/path", "sandbox-test.sandbase.ai"),
("https://localhost", "sandbox-test.sandbase.ai"),
("https://127.0.0.1", "sandbox-test.sandbase.ai"),
("https://unrelated.example", "sandbox-test.sandbase.ai"),
("https://good.example", "-bad.example")):
self.assertFalse(sandbox_setup.e2b_endpoint(api_url, domain))
def test_shared_e2b_selectors(self):
fixture = Path(__file__).resolve().parents[2] / "services/core/internal/sandbox/e2b/testdata/configuration-selectors.json"
cases = json.loads(fixture.read_text())
for case in cases["endpoints"]:
with self.subTest(endpoint=case["name"]):
self.assertEqual(sandbox_setup.e2b_endpoint(case["api_url"], case["domain"]), case["valid"])
for case in cases["templates"]:
with self.subTest(template=case["name"]):
self.assertEqual(sandbox_setup.e2b_template(case["value"]), case["valid"])

def setUp(self):
base = Path.home() / ".oac/tests/sandbox-setup"
Expand Down Expand Up @@ -70,13 +68,6 @@ def send(req):
self.assertEqual(writes, [dict(selection, expected_generation=generation)])
self.assertNotIn("expected_generation", selection)

def test_e2b_template_is_an_exact_build(self):
build = "0f6c1e8e-7d3a-4b8e-9a51-2b7f7f0c9d11"
for value in ("base:" + build, "my_template-2:" + build):
self.assertTrue(sandbox_setup.e2b_template(value), value)
for value in ("base", "base:", ":" + build, "base:" + build.upper(), "base:" + build.replace("-", ""),
"base:00000000-0000-0000-0000-000000000000", "ba.se:" + build, "x" * 129 + ":" + build):
self.assertFalse(sandbox_setup.e2b_template(value), value)


if __name__ == "__main__":
Expand Down
2 changes: 2 additions & 0 deletions docs/sandbox-provider.md
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,8 @@ An administrator [Session archive](../contracts/agents-api/admin-api.md#session-

## Validate the integration

E2B template and endpoint validators in the installer and Go adapter consume the shared [selector fixtures](../services/core/internal/sandbox/e2b/testdata/configuration-selectors.json). Extend these cases with any validation change so both entry points accept the same selectors.

Run `make check-sandbox-provider-contract` while developing. It runs the shared [`contracttest`](../services/core/internal/sandbox/contracttest) suite through real adapter boundaries with controlled native failures, plus the adapter and node transport tests; `make check` includes the same packages. Call the public failure runner with native-side fixtures instead of a fake `SandboxProvider`, and keep tests for foreign ownership, unknown mutation results, cancellation, no automatic replay, failed cleanup and reference-bound settlement.

Node tests separately cover disconnect and reconnect fencing and cleanup after a lost Create response. Helper protocols and the [sandbox node protocol](../contracts/agents-api/node-generation-protocol.md) require an exact version match; direct in-process interfaces have no separate wire version.
Expand Down
73 changes: 46 additions & 27 deletions services/core/internal/sandbox/e2b/endpoint_test.go
Original file line number Diff line number Diff line change
@@ -1,37 +1,56 @@
package e2b

import (
"strings"
"encoding/json"
"os"
"testing"
)

func TestNormalizeEndpoint(t *testing.T) {
for _, tc := range []struct {
apiURL, domain string
valid bool
}{
{"", "", true},
{"https://sandbox-test.sandbase.ai", "sandbox-test.sandbase.ai", true},
{"https://api.e2b.app", "e2b.app", true},
{"https://api.sandbox-test.sandbase.ai", "sandbox-test.sandbase.ai", true},
{"https://unrelated.example", "sandbox-test.sandbase.ai", false},
{"https://sandbox-test.sandbase.ai", "", false},
{"", "sandbox-test.sandbase.ai", false},
{"http://sandbox-test.sandbase.ai", "sandbox-test.sandbase.ai", false},
{"https://sandbox-test.sandbase.ai/path", "sandbox-test.sandbase.ai", false},
{"https://sandbox-test.sandbase.ai:443", "sandbox-test.sandbase.ai", false},
{"https://127.0.0.1", "sandbox-test.sandbase.ai", false},
{"https://localhost", "sandbox-test.sandbase.ai", false},
{"https://-bad.example", "sandbox-test.sandbase.ai", false},
{"https://good.example", "bad..example", false},
{"https://" + strings.Repeat("a.", 125) + "example", "good.example", false},
} {
apiURL, domain, err := NormalizeEndpoint(tc.apiURL, tc.domain)
if (err == nil) != tc.valid {
t.Errorf("NormalizeEndpoint(%q, %q): unexpected error %v", tc.apiURL, tc.domain, err)
// The installer consumes this same fixture; native selectors are owned here.
func TestSharedConfigurationSelectors(t *testing.T) {
raw, err := os.ReadFile("testdata/configuration-selectors.json")
if err != nil {
t.Fatal(err)
}
var cases struct {
Endpoints []struct {
Name string
APIURL string `json:"api_url"`
Domain string
Valid bool
}
if tc.apiURL == "" && tc.domain == "" && (apiURL != OfficialAPIURL || domain != OfficialDomain) {
t.Errorf("official defaults: %q %q", apiURL, domain)
Templates []struct {
Name string
Value string
Valid bool
}
}
if err := json.Unmarshal(raw, &cases); err != nil {
t.Fatal(err)
}
for _, tc := range cases.Endpoints {
t.Run("endpoint/"+tc.Name, func(t *testing.T) {
apiURL, domain, err := NormalizeEndpoint(tc.APIURL, tc.Domain)
if (err == nil) != tc.Valid {
t.Fatalf("valid=%v: %v", tc.Valid, err)
}
if tc.Valid {
expectedURL, expectedDomain := tc.APIURL, tc.Domain
if expectedURL == "" && expectedDomain == "" {
expectedURL, expectedDomain = OfficialAPIURL, OfficialDomain
}
if apiURL != expectedURL || domain != expectedDomain {
t.Fatalf("unexpected normalized selectors: %q %q", apiURL, domain)
}
}
})
}
for _, tc := range cases.Templates {
t.Run("template/"+tc.Name, func(t *testing.T) {
err := ValidateConfiguration(&DeploymentConfiguration{APIKey: "fixture-key", Template: tc.Value})
if (err == nil) != tc.Valid {
t.Fatalf("valid=%v: %v", tc.Valid, err)
}
})
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,250 @@
{
"endpoints": [
{
"name": "official",
"api_url": null,
"domain": null,
"valid": true
},
{
"name": "explicit_official",
"api_url": "https://api.e2b.app",
"domain": "e2b.app",
"valid": true
},
{
"name": "custom_root",
"api_url": "https://sand.example",
"domain": "sand.example",
"valid": true
},
{
"name": "custom_subdomain",
"api_url": "https://api.sand.example",
"domain": "sand.example",
"valid": true
},
{
"name": "one_missing_url",
"api_url": null,
"domain": "sand.example",
"valid": false
},
{
"name": "one_missing_domain",
"api_url": "https://api.sand.example",
"domain": null,
"valid": false
},
{
"name": "http",
"api_url": "http://api.sand.example",
"domain": "sand.example",
"valid": false
},
{
"name": "path",
"api_url": "https://api.sand.example/",
"domain": "sand.example",
"valid": false
},
{
"name": "query",
"api_url": "https://api.sand.example?x=1",
"domain": "sand.example",
"valid": false
},
{
"name": "empty_query",
"api_url": "https://api.sand.example?",
"domain": "sand.example",
"valid": false
},
{
"name": "fragment",
"api_url": "https://api.sand.example#x",
"domain": "sand.example",
"valid": false
},
{
"name": "userinfo",
"api_url": "https://user@api.sand.example",
"domain": "sand.example",
"valid": false
},
{
"name": "port",
"api_url": "https://api.sand.example:443",
"domain": "sand.example",
"valid": false
},
{
"name": "uppercase",
"api_url": "https://API.sand.example",
"domain": "sand.example",
"valid": false
},
{
"name": "foreign",
"api_url": "https://sand.example.evil.test",
"domain": "sand.example",
"valid": false
},
{
"name": "leading_space",
"api_url": " https://api.sand.example",
"domain": "sand.example",
"valid": false
},
{
"name": "newline",
"api_url": "https://api.sand.example\n",
"domain": "sand.example",
"valid": false
},
{
"name": "localhost",
"api_url": "https://localhost",
"domain": "localhost",
"valid": false
},
{
"name": "local_suffix",
"api_url": "https://sand.local",
"domain": "sand.local",
"valid": false
},
{
"name": "localhost_suffix",
"api_url": "https://sand.localhost",
"domain": "sand.localhost",
"valid": false
},
{
"name": "ipv4",
"api_url": "https://127.0.0.1",
"domain": "127.0.0.1",
"valid": false
},
{
"name": "ipv6",
"api_url": "https://::1",
"domain": "::1",
"valid": false
},
{
"name": "empty_label",
"api_url": "https://sand..example",
"domain": "sand..example",
"valid": false
},
{
"name": "leading_dash",
"api_url": "https://-sand.example",
"domain": "-sand.example",
"valid": false
},
{
"name": "trailing_dash",
"api_url": "https://sand-.example",
"domain": "sand-.example",
"valid": false
},
{
"name": "underscore",
"api_url": "https://sand_box.example",
"domain": "sand_box.example",
"valid": false
},
{
"name": "trailing_dot",
"api_url": "https://sand.example.",
"domain": "sand.example.",
"valid": false
},
{
"name": "label_length",
"api_url": "https://xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.example",
"domain": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.example",
"valid": false
},
{
"name": "domain_length",
"api_url": "https://a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.example",
"domain": "a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.example",
"valid": false
}
],
"templates": [
{
"name": "simple",
"value": "base:0f6c1e8e-7d3a-4b8e-9a51-2b7f7f0c9d11",
"valid": true
},
{
"name": "characters",
"value": "My_template-2:0f6c1e8e-7d3a-4b8e-9a51-2b7f7f0c9d11",
"valid": true
},
{
"name": "maximum_length",
"value": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx:0f6c1e8e-7d3a-4b8e-9a51-2b7f7f0c9d11",
"valid": true
},
{
"name": "unpinned",
"value": "base",
"valid": false
},
{
"name": "no_build",
"value": "base:",
"valid": false
},
{
"name": "empty_template",
"value": ":0f6c1e8e-7d3a-4b8e-9a51-2b7f7f0c9d11",
"valid": false
},
{
"name": "uppercase_uuid",
"value": "base:0F6C1E8E-7D3A-4B8E-9A51-2B7F7F0C9D11",
"valid": false
},
{
"name": "compact_uuid",
"value": "base:0f6c1e8e7d3a4b8e9a512b7f7f0c9d11",
"valid": false
},
{
"name": "zero_uuid",
"value": "base:00000000-0000-0000-0000-000000000000",
"valid": false
},
{
"name": "dot",
"value": "ba.se:0f6c1e8e-7d3a-4b8e-9a51-2b7f7f0c9d11",
"valid": false
},
{
"name": "too_long",
"value": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx:0f6c1e8e-7d3a-4b8e-9a51-2b7f7f0c9d11",
"valid": false
},
{
"name": "extra_colon",
"value": "base:0f6c1e8e-7d3a-4b8e-9a51-2b7f7f0c9d11:extra",
"valid": false
},
{
"name": "space",
"value": "base :0f6c1e8e-7d3a-4b8e-9a51-2b7f7f0c9d11",
"valid": false
},
{
"name": "unicode",
"value": "\u6a21\u677f:0f6c1e8e-7d3a-4b8e-9a51-2b7f7f0c9d11",
"valid": false
}
]
}