Skip to content

Commit 2634c64

Browse files
authored
fix(devbox): get_tunnel_url now derives tunnel domain from base url (#771)
1 parent 68afe5a commit 2634c64

5 files changed

Lines changed: 54 additions & 3 deletions

File tree

src/runloop_api_client/sdk/async_devbox.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,9 @@ async def get_tunnel_url(
161161
tunnel_view = await self.get_tunnel(**options)
162162
if tunnel_view is None:
163163
return None
164-
return f"https://{port}-{tunnel_view.tunnel_key}.tunnel.runloop.ai"
164+
api_host = self._client.base_url.host
165+
base_domain = api_host[4:] if api_host.startswith("api.") else api_host
166+
return f"https://{port}-{tunnel_view.tunnel_key}.tunnel.{base_domain}"
165167

166168
async def logs(
167169
self,

src/runloop_api_client/sdk/devbox.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,9 @@ def get_tunnel_url(
160160
tunnel_view = self.get_tunnel(**options)
161161
if tunnel_view is None:
162162
return None
163-
return f"https://{port}-{tunnel_view.tunnel_key}.tunnel.runloop.ai"
163+
api_host = self._client.base_url.host
164+
base_domain = api_host[4:] if api_host.startswith("api.") else api_host
165+
return f"https://{port}-{tunnel_view.tunnel_key}.tunnel.{base_domain}"
164166

165167
def logs(
166168
self,

tests/sdk/async_devbox/test_core.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from types import SimpleNamespace
1010
from unittest.mock import AsyncMock
1111

12+
import httpx
1213
import pytest
1314

1415
from tests.sdk.conftest import MockDevboxView
@@ -377,13 +378,36 @@ async def test_get_tunnel_url_constructs_url(self, mock_async_client: AsyncMock)
377378
tunnel=tunnel_view,
378379
)
379380
mock_async_client.devboxes.retrieve = AsyncMock(return_value=devbox_view_with_tunnel)
381+
mock_async_client.base_url = httpx.URL("https://api.runloop.ai")
380382

381383
devbox = AsyncDevbox(mock_async_client, "dbx_123")
382384
result = await devbox.get_tunnel_url(8080)
383385

384386
assert result == "https://8080-abc123xyz.tunnel.runloop.ai"
385387
mock_async_client.devboxes.retrieve.assert_called_once_with("dbx_123")
386388

389+
@pytest.mark.asyncio
390+
async def test_get_tunnel_url_derives_domain_from_base_url(self, mock_async_client: AsyncMock) -> None:
391+
"""Test get_tunnel_url derives tunnel domain from client base_url."""
392+
tunnel_view = SimpleNamespace(
393+
tunnel_key="abc123xyz",
394+
auth_mode="open",
395+
create_time_ms=1234567890000,
396+
)
397+
devbox_view_with_tunnel = SimpleNamespace(
398+
id="dbx_123",
399+
status="running",
400+
tunnel=tunnel_view,
401+
)
402+
mock_async_client.devboxes.retrieve = AsyncMock(return_value=devbox_view_with_tunnel)
403+
devbox = AsyncDevbox(mock_async_client, "dbx_123")
404+
405+
mock_async_client.base_url = httpx.URL("https://api.runloop.pro")
406+
assert await devbox.get_tunnel_url(8080) == "https://8080-abc123xyz.tunnel.runloop.pro"
407+
408+
mock_async_client.base_url = httpx.URL("http://127.0.0.1:8080")
409+
assert await devbox.get_tunnel_url(8080) == "https://8080-abc123xyz.tunnel.127.0.0.1"
410+
387411
@pytest.mark.asyncio
388412
async def test_get_tunnel_url_returns_none_when_no_tunnel(self, mock_async_client: AsyncMock) -> None:
389413
"""Test get_tunnel_url returns None when no tunnel is enabled."""

tests/sdk/devbox/test_core.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from types import SimpleNamespace
1010
from unittest.mock import Mock
1111

12+
import httpx
1213
import pytest
1314

1415
from tests.sdk.conftest import (
@@ -373,13 +374,35 @@ def test_get_tunnel_url_constructs_url(self, mock_client: Mock) -> None:
373374
tunnel=tunnel_view,
374375
)
375376
mock_client.devboxes.retrieve.return_value = devbox_view_with_tunnel
377+
mock_client.base_url = httpx.URL("https://api.runloop.ai")
376378

377379
devbox = Devbox(mock_client, "dbx_123")
378380
result = devbox.get_tunnel_url(8080)
379381

380382
assert result == "https://8080-abc123xyz.tunnel.runloop.ai"
381383
mock_client.devboxes.retrieve.assert_called_once_with("dbx_123")
382384

385+
def test_get_tunnel_url_derives_domain_from_base_url(self, mock_client: Mock) -> None:
386+
"""Test get_tunnel_url derives tunnel domain from client base_url."""
387+
tunnel_view = SimpleNamespace(
388+
tunnel_key="abc123xyz",
389+
auth_mode="open",
390+
create_time_ms=1234567890000,
391+
)
392+
devbox_view_with_tunnel = SimpleNamespace(
393+
id="dbx_123",
394+
status="running",
395+
tunnel=tunnel_view,
396+
)
397+
mock_client.devboxes.retrieve.return_value = devbox_view_with_tunnel
398+
devbox = Devbox(mock_client, "dbx_123")
399+
400+
mock_client.base_url = httpx.URL("https://api.runloop.pro")
401+
assert devbox.get_tunnel_url(8080) == "https://8080-abc123xyz.tunnel.runloop.pro"
402+
403+
mock_client.base_url = httpx.URL("http://127.0.0.1:8080")
404+
assert devbox.get_tunnel_url(8080) == "https://8080-abc123xyz.tunnel.127.0.0.1"
405+
383406
def test_get_tunnel_url_returns_none_when_no_tunnel(self, mock_client: Mock) -> None:
384407
"""Test get_tunnel_url returns None when no tunnel is enabled."""
385408
devbox_view_no_tunnel = SimpleNamespace(

uv.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)