Skip to content

Commit 5b2eadf

Browse files
committed
unit tests
1 parent 463e876 commit 5b2eadf

14 files changed

Lines changed: 408 additions & 434 deletions

tests/sdk/async_devbox/test_core.py

Lines changed: 24 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313

1414
from tests.sdk.conftest import MockDevboxView
1515
from runloop_api_client.sdk import AsyncDevbox
16-
from runloop_api_client._types import NotGiven
1716
from runloop_api_client.lib.polling import PollingConfig
1817
from runloop_api_client.sdk.async_devbox import (
1918
_AsyncFileInterface,
@@ -44,7 +43,7 @@ async def test_context_manager_enter_exit(self, mock_async_client: AsyncMock, de
4443
assert devbox.id == "dev_123"
4544

4645
call_kwargs = mock_async_client.devboxes.shutdown.call_args[1]
47-
assert isinstance(call_kwargs["timeout"], NotGiven)
46+
assert "timeout" not in call_kwargs
4847

4948
@pytest.mark.asyncio
5049
async def test_context_manager_exception_handling(self, mock_async_client: AsyncMock) -> None:
@@ -137,8 +136,7 @@ async def test_shutdown(self, mock_async_client: AsyncMock, devbox_view: MockDev
137136
@pytest.mark.asyncio
138137
async def test_suspend(self, mock_async_client: AsyncMock, devbox_view: MockDevboxView) -> None:
139138
"""Test suspend method."""
140-
mock_async_client.devboxes.suspend = AsyncMock(return_value=None)
141-
mock_async_client.devboxes.await_suspended = AsyncMock(return_value=devbox_view)
139+
mock_async_client.devboxes.suspend = AsyncMock(return_value=devbox_view)
142140
polling_config = PollingConfig(timeout_seconds=60.0)
143141

144142
devbox = AsyncDevbox(mock_async_client, "dev_123")
@@ -154,22 +152,18 @@ async def test_suspend(self, mock_async_client: AsyncMock, devbox_view: MockDevb
154152
assert result == devbox_view
155153
mock_async_client.devboxes.suspend.assert_called_once_with(
156154
"dev_123",
155+
polling_config=polling_config,
157156
extra_headers={"X-Custom": "value"},
158157
extra_query={"param": "value"},
159158
extra_body={"key": "value"},
160159
timeout=30.0,
161160
idempotency_key="key-123",
162161
)
163-
mock_async_client.devboxes.await_suspended.assert_called_once_with(
164-
"dev_123",
165-
polling_config=polling_config,
166-
)
167162

168163
@pytest.mark.asyncio
169164
async def test_resume(self, mock_async_client: AsyncMock, devbox_view: MockDevboxView) -> None:
170165
"""Test resume method."""
171-
mock_async_client.devboxes.resume = AsyncMock(return_value=None)
172-
mock_async_client.devboxes.await_running = AsyncMock(return_value=devbox_view)
166+
mock_async_client.devboxes.resume = AsyncMock(return_value=devbox_view)
173167
polling_config = PollingConfig(timeout_seconds=60.0)
174168

175169
devbox = AsyncDevbox(mock_async_client, "dev_123")
@@ -185,16 +179,13 @@ async def test_resume(self, mock_async_client: AsyncMock, devbox_view: MockDevbo
185179
assert result == devbox_view
186180
mock_async_client.devboxes.resume.assert_called_once_with(
187181
"dev_123",
182+
polling_config=polling_config,
188183
extra_headers={"X-Custom": "value"},
189184
extra_query={"param": "value"},
190185
extra_body={"key": "value"},
191186
timeout=30.0,
192187
idempotency_key="key-123",
193188
)
194-
mock_async_client.devboxes.await_running.assert_called_once_with(
195-
"dev_123",
196-
polling_config=polling_config,
197-
)
198189

199190
@pytest.mark.asyncio
200191
async def test_keep_alive(self, mock_async_client: AsyncMock) -> None:
@@ -240,7 +231,17 @@ async def test_snapshot_disk(self, mock_async_client: AsyncMock) -> None:
240231

241232
assert snapshot.id == "snap_123"
242233
mock_async_client.devboxes.snapshot_disk_async.assert_called_once()
234+
call_kwargs = mock_async_client.devboxes.snapshot_disk_async.call_args[1]
235+
assert "commit_message" not in call_kwargs
236+
assert call_kwargs["metadata"] == {"key": "value"}
237+
assert call_kwargs["name"] == "test-snapshot"
238+
assert call_kwargs["extra_headers"] == {"X-Custom": "value"}
239+
assert "polling_config" not in call_kwargs
240+
assert "timeout" not in call_kwargs
243241
mock_async_client.devboxes.disk_snapshots.await_completed.assert_called_once()
242+
call_kwargs2 = mock_async_client.devboxes.disk_snapshots.await_completed.call_args[1]
243+
assert call_kwargs2["polling_config"] == polling_config
244+
assert "timeout" not in call_kwargs2
244245

245246
@pytest.mark.asyncio
246247
async def test_snapshot_disk_async(self, mock_async_client: AsyncMock) -> None:
@@ -257,6 +258,13 @@ async def test_snapshot_disk_async(self, mock_async_client: AsyncMock) -> None:
257258

258259
assert snapshot.id == "snap_123"
259260
mock_async_client.devboxes.snapshot_disk_async.assert_called_once()
261+
call_kwargs = mock_async_client.devboxes.snapshot_disk_async.call_args[1]
262+
assert "commit_message" not in call_kwargs
263+
assert call_kwargs["metadata"] == {"key": "value"}
264+
assert call_kwargs["name"] == "test-snapshot"
265+
assert call_kwargs["extra_headers"] == {"X-Custom": "value"}
266+
assert "polling_config" not in call_kwargs
267+
assert "timeout" not in call_kwargs
260268

261269
@pytest.mark.asyncio
262270
async def test_close(self, mock_async_client: AsyncMock, devbox_view: MockDevboxView) -> None:
@@ -267,6 +275,8 @@ async def test_close(self, mock_async_client: AsyncMock, devbox_view: MockDevbox
267275
await devbox.close()
268276

269277
mock_async_client.devboxes.shutdown.assert_called_once()
278+
call_kwargs = mock_async_client.devboxes.shutdown.call_args[1]
279+
assert "timeout" not in call_kwargs
270280

271281
def test_cmd_property(self, mock_async_client: AsyncMock) -> None:
272282
"""Test cmd property returns AsyncCommandInterface."""

tests/sdk/async_devbox/test_interfaces.py

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414

1515
from tests.sdk.conftest import MockExecutionView
1616
from runloop_api_client.sdk import AsyncDevbox
17-
from runloop_api_client._types import Omit, NotGiven
1817

1918

2019
class TestAsyncCommandInterface:
@@ -25,17 +24,19 @@ async def test_exec_without_callbacks(
2524
self, mock_async_client: AsyncMock, execution_view: MockExecutionView
2625
) -> None:
2726
"""Test exec without streaming callbacks."""
28-
mock_async_client.devboxes.execute_and_await_completion = AsyncMock(return_value=execution_view)
27+
mock_async_client.devboxes.execute_async = AsyncMock(return_value=execution_view)
28+
mock_async_client.devboxes.executions.await_completed = AsyncMock(return_value=execution_view)
2929

3030
devbox = AsyncDevbox(mock_async_client, "dev_123")
31-
result = await devbox.cmd.exec("echo hello")
31+
result = await devbox.cmd.exec(command="echo hello")
3232

3333
assert result.exit_code == 0
34-
assert await result.stdout() == "output"
35-
call_kwargs = mock_async_client.devboxes.execute_and_await_completion.call_args[1]
34+
assert await result.stdout(num_lines=10) == "output"
35+
call_kwargs = mock_async_client.devboxes.execute_async.call_args[1]
3636
assert call_kwargs["command"] == "echo hello"
37-
assert isinstance(call_kwargs["shell_name"], Omit)
38-
assert isinstance(call_kwargs["timeout"], NotGiven)
37+
assert "polling_config" not in call_kwargs
38+
assert "timeout" not in call_kwargs
39+
mock_async_client.devboxes.executions.await_completed.assert_not_called()
3940

4041
@pytest.mark.asyncio
4142
async def test_exec_with_stdout_callback(self, mock_async_client: AsyncMock, mock_async_stream: AsyncMock) -> None:
@@ -61,7 +62,7 @@ async def test_exec_with_stdout_callback(self, mock_async_client: AsyncMock, moc
6162
stdout_calls: list[str] = []
6263

6364
devbox = AsyncDevbox(mock_async_client, "dev_123")
64-
result = await devbox.cmd.exec("echo hello", stdout=stdout_calls.append)
65+
result = await devbox.cmd.exec(command="echo hello", stdout=stdout_calls.append)
6566

6667
assert result.exit_code == 0
6768
mock_async_client.devboxes.execute_async.assert_called_once()
@@ -81,7 +82,7 @@ async def test_exec_async_returns_execution(
8182
mock_async_client.devboxes.executions.stream_stdout_updates = AsyncMock(return_value=mock_async_stream)
8283

8384
devbox = AsyncDevbox(mock_async_client, "dev_123")
84-
execution = await devbox.cmd.exec_async("long-running command")
85+
execution = await devbox.cmd.exec_async(command="long-running command")
8586

8687
assert execution.execution_id == "exec_123"
8788
assert execution.devbox_id == "dev_123"
@@ -97,7 +98,7 @@ async def test_read(self, mock_async_client: AsyncMock) -> None:
9798
mock_async_client.devboxes.read_file_contents = AsyncMock(return_value="file content")
9899

99100
devbox = AsyncDevbox(mock_async_client, "dev_123")
100-
result = await devbox.file.read("/path/to/file")
101+
result = await devbox.file.read(file_path="/path/to/file")
101102

102103
assert result == "file content"
103104
mock_async_client.devboxes.read_file_contents.assert_called_once()
@@ -109,7 +110,7 @@ async def test_write_string(self, mock_async_client: AsyncMock) -> None:
109110
mock_async_client.devboxes.write_file_contents = AsyncMock(return_value=execution_detail)
110111

111112
devbox = AsyncDevbox(mock_async_client, "dev_123")
112-
result = await devbox.file.write("/path/to/file", "content")
113+
result = await devbox.file.write(file_path="/path/to/file", contents="content")
113114

114115
assert result == execution_detail
115116
mock_async_client.devboxes.write_file_contents.assert_called_once()
@@ -121,7 +122,7 @@ async def test_write_bytes(self, mock_async_client: AsyncMock) -> None:
121122
mock_async_client.devboxes.write_file_contents = AsyncMock(return_value=execution_detail)
122123

123124
devbox = AsyncDevbox(mock_async_client, "dev_123")
124-
result = await devbox.file.write("/path/to/file", b"content")
125+
result = await devbox.file.write(file_path="/path/to/file", contents="content")
125126

126127
assert result == execution_detail
127128
mock_async_client.devboxes.write_file_contents.assert_called_once()
@@ -134,7 +135,7 @@ async def test_download(self, mock_async_client: AsyncMock) -> None:
134135
mock_async_client.devboxes.download_file = AsyncMock(return_value=mock_response)
135136

136137
devbox = AsyncDevbox(mock_async_client, "dev_123")
137-
result = await devbox.file.download("/path/to/file")
138+
result = await devbox.file.download(path="/path/to/file")
138139

139140
assert result == b"file content"
140141
mock_async_client.devboxes.download_file.assert_called_once()
@@ -150,7 +151,7 @@ async def test_upload(self, mock_async_client: AsyncMock, tmp_path: Path) -> Non
150151
temp_file = tmp_path / "test_file.txt"
151152
temp_file.write_text("test content")
152153

153-
result = await devbox.file.upload("/remote/path", temp_file)
154+
result = await devbox.file.upload(path="/remote/path", file=temp_file)
154155

155156
assert result == execution_detail
156157
mock_async_client.devboxes.upload_file.assert_called_once()

tests/sdk/devbox/test_core.py

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
MockDevboxView,
1616
)
1717
from runloop_api_client.sdk import Devbox
18-
from runloop_api_client._types import NotGiven, omit
18+
from runloop_api_client._types import omit
1919
from runloop_api_client.sdk.devbox import (
2020
_FileInterface,
2121
_CommandInterface,
@@ -45,7 +45,7 @@ def test_context_manager_enter_exit(self, mock_client: Mock, devbox_view: MockDe
4545
assert devbox.id == "dev_123"
4646

4747
call_kwargs = mock_client.devboxes.shutdown.call_args[1]
48-
assert isinstance(call_kwargs["timeout"], NotGiven)
48+
assert "timeout" not in call_kwargs
4949

5050
def test_context_manager_exception_handling(self, mock_client: Mock) -> None:
5151
"""Test context manager handles exceptions during shutdown."""
@@ -232,14 +232,15 @@ def test_snapshot_disk(self, mock_client: Mock) -> None:
232232

233233
assert snapshot.id == "snap_123"
234234
call_kwargs = mock_client.devboxes.snapshot_disk_async.call_args[1]
235-
assert call_kwargs["commit_message"] is omit or call_kwargs["commit_message"] is None
235+
assert "commit_message" not in call_kwargs or call_kwargs["commit_message"] in (omit, None)
236236
assert call_kwargs["metadata"] == {"key": "value"}
237237
assert call_kwargs["name"] == "test-snapshot"
238238
assert call_kwargs["extra_headers"] == {"X-Custom": "value"}
239-
assert isinstance(call_kwargs["timeout"], NotGiven)
239+
assert "polling_config" not in call_kwargs
240+
assert "timeout" not in call_kwargs
240241
call_kwargs2 = mock_client.devboxes.disk_snapshots.await_completed.call_args[1]
241242
assert call_kwargs2["polling_config"] == polling_config
242-
assert isinstance(call_kwargs2["timeout"], NotGiven)
243+
assert "timeout" not in call_kwargs2
243244

244245
def test_snapshot_disk_async(self, mock_client: Mock) -> None:
245246
"""Test snapshot_disk_async returns immediately."""
@@ -255,11 +256,12 @@ def test_snapshot_disk_async(self, mock_client: Mock) -> None:
255256

256257
assert snapshot.id == "snap_123"
257258
call_kwargs = mock_client.devboxes.snapshot_disk_async.call_args[1]
258-
assert call_kwargs["commit_message"] is omit or call_kwargs["commit_message"] is None
259+
assert "commit_message" not in call_kwargs or call_kwargs["commit_message"] in (omit, None)
259260
assert call_kwargs["metadata"] == {"key": "value"}
260261
assert call_kwargs["name"] == "test-snapshot"
261262
assert call_kwargs["extra_headers"] == {"X-Custom": "value"}
262-
assert isinstance(call_kwargs["timeout"], NotGiven)
263+
assert "polling_config" not in call_kwargs
264+
assert "timeout" not in call_kwargs
263265
# Verify async method does not wait for completion
264266
if hasattr(mock_client.devboxes.disk_snapshots, "await_completed"):
265267
assert not mock_client.devboxes.disk_snapshots.await_completed.called
@@ -272,7 +274,7 @@ def test_close(self, mock_client: Mock, devbox_view: MockDevboxView) -> None:
272274
devbox.close()
273275

274276
call_kwargs = mock_client.devboxes.shutdown.call_args[1]
275-
assert isinstance(call_kwargs["timeout"], NotGiven)
277+
assert "timeout" not in call_kwargs
276278

277279
def test_cmd_property(self, mock_client: Mock) -> None:
278280
"""Test cmd property returns CommandInterface."""

tests/sdk/devbox/test_edge_cases.py

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
import threading
1010
from types import SimpleNamespace
1111
from pathlib import Path
12-
from unittest.mock import Mock, patch
12+
from unittest.mock import Mock
1313

1414
import httpx
1515
import pytest
@@ -136,11 +136,13 @@ def test_path_handling(self, mock_client: Mock, tmp_path: Path) -> None:
136136
temp_file = tmp_path / "test_file.txt"
137137
temp_file.write_text("test")
138138

139-
with patch("httpx.put") as mock_put:
140-
mock_response = create_mock_httpx_response()
141-
mock_put.return_value = mock_response
139+
http_client = Mock()
140+
mock_response = create_mock_httpx_response()
141+
http_client.put.return_value = mock_response
142+
mock_client._client = http_client
142143

143-
obj = StorageObject(mock_client, "obj_123", "https://upload.example.com")
144-
obj.upload_content(temp_file) # Path object works
144+
obj = StorageObject(mock_client, "obj_123", "https://upload.example.com")
145+
obj.upload_content(temp_file.read_text())
146+
obj.upload_content(temp_file.read_bytes())
145147

146-
mock_put.assert_called_once()
148+
assert http_client.put.call_count == 2

0 commit comments

Comments
 (0)