@@ -323,3 +323,79 @@ def test_net_property(self, mock_async_client: AsyncMock) -> None:
323323 net = devbox .net
324324 assert isinstance (net , AsyncNetworkInterface )
325325 assert net ._devbox is devbox
326+
327+ @pytest .mark .asyncio
328+ async def test_get_tunnel_returns_tunnel_view (self , mock_async_client : AsyncMock ) -> None :
329+ """Test get_tunnel returns the tunnel from get_info."""
330+ tunnel_view = SimpleNamespace (
331+ tunnel_key = "abc123xyz" ,
332+ auth_mode = "open" ,
333+ create_time_ms = 1234567890000 ,
334+ )
335+ devbox_view_with_tunnel = SimpleNamespace (
336+ id = "dbx_123" ,
337+ status = "running" ,
338+ tunnel = tunnel_view ,
339+ )
340+ mock_async_client .devboxes .retrieve = AsyncMock (return_value = devbox_view_with_tunnel )
341+
342+ devbox = AsyncDevbox (mock_async_client , "dbx_123" )
343+ result = await devbox .get_tunnel ()
344+
345+ assert result is not None
346+ assert result == tunnel_view
347+ assert result .tunnel_key == "abc123xyz"
348+ mock_async_client .devboxes .retrieve .assert_called_once_with ("dbx_123" )
349+
350+ @pytest .mark .asyncio
351+ async def test_get_tunnel_returns_none_when_no_tunnel (self , mock_async_client : AsyncMock ) -> None :
352+ """Test get_tunnel returns None when no tunnel is enabled."""
353+ devbox_view_no_tunnel = SimpleNamespace (
354+ id = "dbx_123" ,
355+ status = "running" ,
356+ tunnel = None ,
357+ )
358+ mock_async_client .devboxes .retrieve = AsyncMock (return_value = devbox_view_no_tunnel )
359+
360+ devbox = AsyncDevbox (mock_async_client , "dbx_123" )
361+ result = await devbox .get_tunnel ()
362+
363+ assert result is None
364+ mock_async_client .devboxes .retrieve .assert_called_once_with ("dbx_123" )
365+
366+ @pytest .mark .asyncio
367+ async def test_get_tunnel_url_constructs_url (self , mock_async_client : AsyncMock ) -> None :
368+ """Test get_tunnel_url constructs the correct URL."""
369+ tunnel_view = SimpleNamespace (
370+ tunnel_key = "abc123xyz" ,
371+ auth_mode = "open" ,
372+ create_time_ms = 1234567890000 ,
373+ )
374+ devbox_view_with_tunnel = SimpleNamespace (
375+ id = "dbx_123" ,
376+ status = "running" ,
377+ tunnel = tunnel_view ,
378+ )
379+ mock_async_client .devboxes .retrieve = AsyncMock (return_value = devbox_view_with_tunnel )
380+
381+ devbox = AsyncDevbox (mock_async_client , "dbx_123" )
382+ result = await devbox .get_tunnel_url (8080 )
383+
384+ assert result == "https://8080-abc123xyz.tunnel.runloop.ai"
385+ mock_async_client .devboxes .retrieve .assert_called_once_with ("dbx_123" )
386+
387+ @pytest .mark .asyncio
388+ async def test_get_tunnel_url_returns_none_when_no_tunnel (self , mock_async_client : AsyncMock ) -> None :
389+ """Test get_tunnel_url returns None when no tunnel is enabled."""
390+ devbox_view_no_tunnel = SimpleNamespace (
391+ id = "dbx_123" ,
392+ status = "running" ,
393+ tunnel = None ,
394+ )
395+ mock_async_client .devboxes .retrieve = AsyncMock (return_value = devbox_view_no_tunnel )
396+
397+ devbox = AsyncDevbox (mock_async_client , "dbx_123" )
398+ result = await devbox .get_tunnel_url (8080 )
399+
400+ assert result is None
401+ mock_async_client .devboxes .retrieve .assert_called_once_with ("dbx_123" )
0 commit comments