feat(client): add proxy_domain + timeout kwargs, fix default proxy host
- WrennClient/AsyncWrennClient accept proxy_domain= and timeout= kwargs - WRENN_PROXY_DOMAIN env var supported - Default proxy host: app.wrenn.dev -> wrenn.dev (was port-id.app.wrenn.dev) - Custom base_url preserves host verbatim (with port) - Default timeout: httpx.Timeout(30.0, connect=10.0) - _build_proxy_url/_build_http_proxy_url take optional proxy_domain - code_runner proxy + WS URL builders thread proxy_domain through
This commit is contained in:
@ -45,6 +45,16 @@ class TestBuildHttpProxyUrl:
|
||||
url = _build_http_proxy_url("https://api.example.com:9443", "sb-1", 80)
|
||||
assert url == "https://80-sb-1.api.example.com:9443"
|
||||
|
||||
def test_proxy_domain_override_http(self):
|
||||
url = _build_http_proxy_url(
|
||||
"https://app.wrenn.dev/api", "cl-abc", 8080, "wrenn.dev"
|
||||
)
|
||||
assert url == "https://8080-cl-abc.wrenn.dev"
|
||||
|
||||
def test_proxy_domain_override_ws(self):
|
||||
url = _build_proxy_url("https://app.wrenn.dev/api", "cl-abc", 8888, "wrenn.dev")
|
||||
assert url == "wss://8888-cl-abc.wrenn.dev"
|
||||
|
||||
|
||||
class TestCapsuleCreate:
|
||||
@respx.mock
|
||||
@ -222,7 +232,7 @@ class TestGetUrlPublic:
|
||||
202, json={"id": "cl-99", "status": "starting"}
|
||||
)
|
||||
cap = Capsule(api_key=API_KEY, base_url=BASE)
|
||||
assert cap.get_url(8080) == "https://8080-cl-99.app.wrenn.dev"
|
||||
assert cap.get_url(8080) == "https://8080-cl-99.wrenn.dev"
|
||||
|
||||
@respx.mock
|
||||
def test_sync_get_url_localhost(self):
|
||||
@ -242,7 +252,7 @@ class TestGetUrlPublic:
|
||||
202, json={"id": "cl-async", "status": "starting"}
|
||||
)
|
||||
cap = await AsyncCapsule.create(api_key=API_KEY, base_url=BASE)
|
||||
assert cap.get_url(5000) == "https://5000-cl-async.app.wrenn.dev"
|
||||
assert cap.get_url(5000) == "https://5000-cl-async.wrenn.dev"
|
||||
await cap._client.aclose()
|
||||
|
||||
|
||||
|
||||
@ -261,3 +261,39 @@ class TestAsyncClient:
|
||||
)
|
||||
with pytest.raises(WrennNotFoundError):
|
||||
await async_client.capsules.get("nope")
|
||||
|
||||
|
||||
class TestClientResolution:
|
||||
def test_default_base_url_strips_app_subdomain(self):
|
||||
with WrennClient(api_key="wrn_test1234567890abcdef12345678") as c:
|
||||
assert c._proxy_domain == "wrenn.dev"
|
||||
|
||||
def test_custom_base_url_preserves_host(self):
|
||||
with WrennClient(
|
||||
api_key="wrn_test1234567890abcdef12345678",
|
||||
base_url="http://localhost:8080/api",
|
||||
) as c:
|
||||
assert c._proxy_domain == "localhost:8080"
|
||||
|
||||
def test_explicit_proxy_domain_wins(self):
|
||||
with WrennClient(
|
||||
api_key="wrn_test1234567890abcdef12345678",
|
||||
base_url="https://app.wrenn.dev/api",
|
||||
proxy_domain="custom.example.com",
|
||||
) as c:
|
||||
assert c._proxy_domain == "custom.example.com"
|
||||
|
||||
def test_env_proxy_domain(self, monkeypatch):
|
||||
monkeypatch.setenv("WRENN_PROXY_DOMAIN", "env.example.com")
|
||||
with WrennClient(api_key="wrn_test1234567890abcdef12345678") as c:
|
||||
assert c._proxy_domain == "env.example.com"
|
||||
|
||||
def test_default_timeout(self):
|
||||
with WrennClient(api_key="wrn_test1234567890abcdef12345678") as c:
|
||||
t = c._http.timeout
|
||||
assert t.connect == 10.0
|
||||
assert t.read == 30.0
|
||||
|
||||
def test_timeout_float_override(self):
|
||||
with WrennClient(api_key="wrn_test1234567890abcdef12345678", timeout=5.0) as c:
|
||||
assert c._http.timeout.connect == 5.0
|
||||
|
||||
@ -263,7 +263,7 @@ class TestEnsureKernel:
|
||||
@respx.mock
|
||||
def test_creates_kernel_with_wrenn_name_when_none_exist(self):
|
||||
c = _make_capsule()
|
||||
proxy_base = "https://8888-sb-1.app.wrenn.dev"
|
||||
proxy_base = "https://8888-sb-1.wrenn.dev"
|
||||
list_route = respx.get(f"{proxy_base}/api/kernels").respond(200, json=[])
|
||||
create_route = respx.post(f"{proxy_base}/api/kernels").respond(
|
||||
201, json={"id": "k-new", "name": "wrenn"}
|
||||
@ -279,7 +279,7 @@ class TestEnsureKernel:
|
||||
@respx.mock
|
||||
def test_reuses_existing_wrenn_kernel(self):
|
||||
c = _make_capsule()
|
||||
proxy_base = "https://8888-sb-1.app.wrenn.dev"
|
||||
proxy_base = "https://8888-sb-1.wrenn.dev"
|
||||
respx.get(f"{proxy_base}/api/kernels").respond(
|
||||
200,
|
||||
json=[
|
||||
@ -295,7 +295,7 @@ class TestEnsureKernel:
|
||||
@respx.mock
|
||||
def test_creates_when_only_other_kernels_exist(self):
|
||||
c = _make_capsule()
|
||||
proxy_base = "https://8888-sb-1.app.wrenn.dev"
|
||||
proxy_base = "https://8888-sb-1.wrenn.dev"
|
||||
respx.get(f"{proxy_base}/api/kernels").respond(
|
||||
200, json=[{"id": "k-other", "name": "python3"}]
|
||||
)
|
||||
@ -308,7 +308,7 @@ class TestEnsureKernel:
|
||||
@respx.mock
|
||||
def test_caches_kernel_id(self):
|
||||
c = _make_capsule()
|
||||
proxy_base = "https://8888-sb-1.app.wrenn.dev"
|
||||
proxy_base = "https://8888-sb-1.wrenn.dev"
|
||||
route = respx.get(f"{proxy_base}/api/kernels").respond(
|
||||
200, json=[{"id": "k-1", "name": "wrenn"}]
|
||||
)
|
||||
@ -322,7 +322,7 @@ class TestEnsureKernel:
|
||||
202, json={"id": "sb-1", "status": "starting"}
|
||||
)
|
||||
c = Capsule(kernel="python3", api_key=API_KEY, base_url=BASE)
|
||||
proxy_base = "https://8888-sb-1.app.wrenn.dev"
|
||||
proxy_base = "https://8888-sb-1.wrenn.dev"
|
||||
respx.get(f"{proxy_base}/api/kernels").respond(200, json=[])
|
||||
create = respx.post(f"{proxy_base}/api/kernels").respond(
|
||||
201, json={"id": "k-py", "name": "python3"}
|
||||
@ -334,7 +334,7 @@ class TestEnsureKernel:
|
||||
@respx.mock
|
||||
def test_retries_on_5xx_then_succeeds(self):
|
||||
c = _make_capsule()
|
||||
proxy_base = "https://8888-sb-1.app.wrenn.dev"
|
||||
proxy_base = "https://8888-sb-1.wrenn.dev"
|
||||
responses = [
|
||||
httpx.Response(503),
|
||||
httpx.Response(200, json=[{"id": "k-1", "name": "wrenn"}]),
|
||||
@ -347,7 +347,7 @@ class TestEnsureKernel:
|
||||
@respx.mock
|
||||
def test_raises_on_4xx(self):
|
||||
c = _make_capsule()
|
||||
proxy_base = "https://8888-sb-1.app.wrenn.dev"
|
||||
proxy_base = "https://8888-sb-1.wrenn.dev"
|
||||
respx.get(f"{proxy_base}/api/kernels").respond(401)
|
||||
with pytest.raises(httpx.HTTPStatusError):
|
||||
c._ensure_kernel(jupyter_timeout=2)
|
||||
@ -355,7 +355,7 @@ class TestEnsureKernel:
|
||||
@respx.mock
|
||||
def test_timeout_raises(self):
|
||||
c = _make_capsule()
|
||||
proxy_base = "https://8888-sb-1.app.wrenn.dev"
|
||||
proxy_base = "https://8888-sb-1.wrenn.dev"
|
||||
respx.get(f"{proxy_base}/api/kernels").respond(503)
|
||||
with patch("time.sleep"):
|
||||
with pytest.raises(TimeoutError):
|
||||
@ -813,7 +813,7 @@ class TestAsyncEnsureKernel:
|
||||
@respx.mock
|
||||
async def test_async_creates_kernel_when_none_exist(self):
|
||||
c = _make_async_capsule()
|
||||
proxy_base = "https://8888-sb-1.app.wrenn.dev"
|
||||
proxy_base = "https://8888-sb-1.wrenn.dev"
|
||||
list_route = respx.get(f"{proxy_base}/api/kernels").respond(200, json=[])
|
||||
create_route = respx.post(f"{proxy_base}/api/kernels").respond(
|
||||
201, json={"id": "k-new", "name": "wrenn"}
|
||||
@ -829,7 +829,7 @@ class TestAsyncEnsureKernel:
|
||||
@respx.mock
|
||||
async def test_async_reuses_existing_wrenn_kernel(self):
|
||||
c = _make_async_capsule()
|
||||
proxy_base = "https://8888-sb-1.app.wrenn.dev"
|
||||
proxy_base = "https://8888-sb-1.wrenn.dev"
|
||||
respx.get(f"{proxy_base}/api/kernels").respond(
|
||||
200,
|
||||
json=[
|
||||
@ -847,7 +847,7 @@ class TestAsyncEnsureKernel:
|
||||
@respx.mock
|
||||
async def test_async_retries_on_5xx_then_succeeds(self):
|
||||
c = _make_async_capsule()
|
||||
proxy_base = "https://8888-sb-1.app.wrenn.dev"
|
||||
proxy_base = "https://8888-sb-1.wrenn.dev"
|
||||
responses = [
|
||||
httpx.Response(503),
|
||||
httpx.Response(200, json=[{"id": "k-1", "name": "wrenn"}]),
|
||||
@ -867,7 +867,7 @@ class TestAsyncEnsureKernel:
|
||||
@respx.mock
|
||||
async def test_async_raises_on_4xx(self):
|
||||
c = _make_async_capsule()
|
||||
proxy_base = "https://8888-sb-1.app.wrenn.dev"
|
||||
proxy_base = "https://8888-sb-1.wrenn.dev"
|
||||
respx.get(f"{proxy_base}/api/kernels").respond(401)
|
||||
with pytest.raises(httpx.HTTPStatusError):
|
||||
await c._ensure_kernel(jupyter_timeout=2)
|
||||
@ -877,7 +877,7 @@ class TestAsyncEnsureKernel:
|
||||
@respx.mock
|
||||
async def test_async_caches_kernel_id(self):
|
||||
c = _make_async_capsule()
|
||||
proxy_base = "https://8888-sb-1.app.wrenn.dev"
|
||||
proxy_base = "https://8888-sb-1.wrenn.dev"
|
||||
route = respx.get(f"{proxy_base}/api/kernels").respond(
|
||||
200, json=[{"id": "k-1", "name": "wrenn"}]
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user