|
7 | 7 | from __future__ import annotations |
8 | 8 |
|
9 | 9 | import json |
| 10 | +from typing import Any, cast |
10 | 11 |
|
11 | 12 | import httpx |
12 | 13 | import pytest |
13 | | -import respx |
| 14 | +from respx import MockRouter |
14 | 15 |
|
15 | 16 | from runloop_api_client import AsyncRunloop, Runloop |
16 | 17 |
|
17 | | -BASE = "http://localhost" |
18 | | -EXECUTE_PATTERN = f"{BASE}/v1/devboxes/dbx_test/execute" |
| 18 | +base_url = "http://127.0.0.1:4010" |
| 19 | +EXECUTE_PATH = "/v1/devboxes/dbx_test/execute" |
19 | 20 |
|
20 | 21 | STUB_RESPONSE = { |
21 | 22 | "execution_id": "exec_1", |
|
28 | 29 | } |
29 | 30 |
|
30 | 31 |
|
31 | | -class TestCommandIdUniqueness: |
32 | | - """Every call without an explicit command_id must produce a distinct UUID.""" |
| 32 | +class TestCommandIdGeneration: |
| 33 | + """command_id must be a fresh UUIDv7 per call when not explicitly provided.""" |
33 | 34 |
|
34 | | - @respx.mock |
35 | | - def test_sync_execute_generates_unique_ids(self) -> None: |
36 | | - route = respx.post(EXECUTE_PATTERN).mock( |
37 | | - return_value=httpx.Response(200, json=STUB_RESPONSE) |
38 | | - ) |
39 | | - client = Runloop(base_url=BASE, bearer_token="test") |
| 35 | + @pytest.mark.respx(base_url=base_url) |
| 36 | + def test_execute_generates_unique_command_ids(self, respx_mock: MockRouter) -> None: |
| 37 | + route = respx_mock.post(EXECUTE_PATH).mock(return_value=httpx.Response(200, json=STUB_RESPONSE)) |
| 38 | + client = Runloop(base_url=base_url, bearer_token="test") |
40 | 39 |
|
41 | 40 | for _ in range(5): |
42 | 41 | client.devboxes.execute(id="dbx_test", command="echo hi") |
43 | 42 |
|
44 | 43 | assert route.call_count == 5 |
45 | | - ids = [json.loads(call.request.content)["command_id"] for call in route.calls] |
46 | | - assert len(set(ids)) == 5, f"All command_ids should be unique, got: {ids}" |
| 44 | + ids = [json.loads(cast(bytes, call.request.content))["command_id"] for call in route.calls] |
| 45 | + assert len(set(ids)) == 5, f"command_ids should all be unique, got: {ids}" |
47 | 46 |
|
48 | | - @respx.mock |
49 | | - def test_sync_execute_respects_explicit_id(self) -> None: |
50 | | - route = respx.post(EXECUTE_PATTERN).mock( |
51 | | - return_value=httpx.Response(200, json=STUB_RESPONSE) |
52 | | - ) |
53 | | - client = Runloop(base_url=BASE, bearer_token="test") |
| 47 | + @pytest.mark.respx(base_url=base_url) |
| 48 | + def test_execute_preserves_explicit_command_id(self, respx_mock: MockRouter) -> None: |
| 49 | + route = respx_mock.post(EXECUTE_PATH).mock(return_value=httpx.Response(200, json=STUB_RESPONSE)) |
| 50 | + client = Runloop(base_url=base_url, bearer_token="test") |
54 | 51 |
|
55 | 52 | client.devboxes.execute(id="dbx_test", command="echo hi", command_id="my-custom-id") |
56 | 53 |
|
57 | | - body = json.loads(route.calls[0].request.content) |
| 54 | + body = json.loads(cast(bytes, route.calls[0].request.content)) |
58 | 55 | assert body["command_id"] == "my-custom-id" |
59 | 56 |
|
60 | | - @respx.mock |
| 57 | + |
| 58 | +class TestAsyncCommandIdGeneration: |
| 59 | + """Async variant: command_id must be a fresh UUIDv7 per call when not explicitly provided.""" |
| 60 | + |
| 61 | + @pytest.mark.respx(base_url=base_url) |
61 | 62 | @pytest.mark.asyncio |
62 | | - async def test_async_execute_generates_unique_ids(self) -> None: |
63 | | - route = respx.post(EXECUTE_PATTERN).mock( |
64 | | - return_value=httpx.Response(200, json=STUB_RESPONSE) |
65 | | - ) |
66 | | - client = AsyncRunloop(base_url=BASE, bearer_token="test") |
| 63 | + async def test_execute_generates_unique_command_ids(self, respx_mock: MockRouter) -> None: |
| 64 | + route = respx_mock.post(EXECUTE_PATH).mock(return_value=httpx.Response(200, json=STUB_RESPONSE)) |
| 65 | + client = AsyncRunloop(base_url=base_url, bearer_token="test") |
67 | 66 |
|
68 | 67 | for _ in range(5): |
69 | 68 | await client.devboxes.execute(id="dbx_test", command="echo hi") |
70 | 69 |
|
71 | 70 | assert route.call_count == 5 |
72 | | - ids = [json.loads(call.request.content)["command_id"] for call in route.calls] |
73 | | - assert len(set(ids)) == 5, f"All command_ids should be unique, got: {ids}" |
| 71 | + ids = [json.loads(cast(bytes, call.request.content))["command_id"] for call in route.calls] |
| 72 | + assert len(set(ids)) == 5, f"command_ids should all be unique, got: {ids}" |
74 | 73 |
|
75 | | - @respx.mock |
| 74 | + @pytest.mark.respx(base_url=base_url) |
76 | 75 | @pytest.mark.asyncio |
77 | | - async def test_async_execute_respects_explicit_id(self) -> None: |
78 | | - route = respx.post(EXECUTE_PATTERN).mock( |
79 | | - return_value=httpx.Response(200, json=STUB_RESPONSE) |
80 | | - ) |
81 | | - client = AsyncRunloop(base_url=BASE, bearer_token="test") |
| 76 | + async def test_execute_preserves_explicit_command_id(self, respx_mock: MockRouter) -> None: |
| 77 | + route = respx_mock.post(EXECUTE_PATH).mock(return_value=httpx.Response(200, json=STUB_RESPONSE)) |
| 78 | + client = AsyncRunloop(base_url=base_url, bearer_token="test") |
82 | 79 |
|
83 | 80 | await client.devboxes.execute(id="dbx_test", command="echo hi", command_id="my-custom-id") |
84 | 81 |
|
85 | | - body = json.loads(route.calls[0].request.content) |
| 82 | + body = json.loads(cast(bytes, route.calls[0].request.content)) |
86 | 83 | assert body["command_id"] == "my-custom-id" |
0 commit comments