|
| 1 | +import asyncio |
| 2 | + |
| 3 | +import pytest |
| 4 | + |
| 5 | +from hyperbrowser.models import ( |
| 6 | + SandboxListParams, |
| 7 | + SandboxMemorySnapshotParams, |
| 8 | + SandboxSnapshotListParams, |
| 9 | +) |
| 10 | + |
| 11 | +from tests.helpers.config import create_async_client, make_test_name |
| 12 | +from tests.helpers.sandbox import ( |
| 13 | + default_sandbox_params, |
| 14 | + stop_sandbox_if_running_async, |
| 15 | + wait_for_runtime_ready_async, |
| 16 | +) |
| 17 | + |
| 18 | +SANDBOX_PAGE_LIMIT = 50 |
| 19 | +SNAPSHOT_LIST_LIMIT = 200 |
| 20 | +LIST_POLL_DELAY_SECONDS = 0.5 |
| 21 | +LIST_POLL_TIMEOUT_SECONDS = 90 |
| 22 | + |
| 23 | + |
| 24 | +async def _wait_for_sandbox_in_list(client, sandbox_id: str): |
| 25 | + deadline = asyncio.get_running_loop().time() + LIST_POLL_TIMEOUT_SECONDS |
| 26 | + |
| 27 | + while asyncio.get_running_loop().time() < deadline: |
| 28 | + page = 1 |
| 29 | + |
| 30 | + while True: |
| 31 | + response = await client.sandboxes.list( |
| 32 | + SandboxListParams( |
| 33 | + status="active", |
| 34 | + page=page, |
| 35 | + limit=SANDBOX_PAGE_LIMIT, |
| 36 | + ) |
| 37 | + ) |
| 38 | + match = next( |
| 39 | + (entry for entry in response.sandboxes if entry.id == sandbox_id), |
| 40 | + None, |
| 41 | + ) |
| 42 | + if match is not None: |
| 43 | + return match |
| 44 | + |
| 45 | + has_more = page * response.per_page < response.total_count |
| 46 | + if not has_more or not response.sandboxes: |
| 47 | + break |
| 48 | + |
| 49 | + page += 1 |
| 50 | + |
| 51 | + await asyncio.sleep(LIST_POLL_DELAY_SECONDS) |
| 52 | + |
| 53 | + raise RuntimeError(f"sandbox {sandbox_id} did not appear in list()") |
| 54 | + |
| 55 | + |
| 56 | +async def _wait_for_created_snapshot(client, snapshot_id: str): |
| 57 | + deadline = asyncio.get_running_loop().time() + LIST_POLL_TIMEOUT_SECONDS |
| 58 | + |
| 59 | + while asyncio.get_running_loop().time() < deadline: |
| 60 | + response = await client.sandboxes.list_snapshots( |
| 61 | + SandboxSnapshotListParams(limit=SNAPSHOT_LIST_LIMIT) |
| 62 | + ) |
| 63 | + match = next( |
| 64 | + (entry for entry in response.snapshots if entry.id == snapshot_id), |
| 65 | + None, |
| 66 | + ) |
| 67 | + if match is not None and match.status == "created": |
| 68 | + return match |
| 69 | + |
| 70 | + await asyncio.sleep(LIST_POLL_DELAY_SECONDS) |
| 71 | + |
| 72 | + raise RuntimeError( |
| 73 | + f"snapshot {snapshot_id} did not appear as created in list_snapshots()" |
| 74 | + ) |
| 75 | + |
| 76 | + |
| 77 | +@pytest.mark.anyio |
| 78 | +async def test_async_sandbox_list_e2e(): |
| 79 | + client = create_async_client() |
| 80 | + sandbox = None |
| 81 | + memory_snapshot = None |
| 82 | + snapshot_name = make_test_name("py-async-list-snapshot") |
| 83 | + |
| 84 | + try: |
| 85 | + sandbox = await client.sandboxes.create(default_sandbox_params("py-async-list")) |
| 86 | + await wait_for_runtime_ready_async(sandbox) |
| 87 | + |
| 88 | + memory_snapshot = await sandbox.create_memory_snapshot( |
| 89 | + SandboxMemorySnapshotParams(snapshot_name=snapshot_name) |
| 90 | + ) |
| 91 | + |
| 92 | + listed_sandbox = await _wait_for_sandbox_in_list(client, sandbox.id) |
| 93 | + assert listed_sandbox.id == sandbox.id |
| 94 | + assert listed_sandbox.status == "active" |
| 95 | + assert listed_sandbox.region == sandbox.region |
| 96 | + assert listed_sandbox.runtime.transport == "regional_proxy" |
| 97 | + assert listed_sandbox.runtime.base_url == sandbox.runtime.base_url |
| 98 | + |
| 99 | + images = await client.sandboxes.list_images() |
| 100 | + listed_image = next( |
| 101 | + (entry for entry in images.images if entry.id == memory_snapshot.image_id), |
| 102 | + None, |
| 103 | + ) |
| 104 | + assert listed_image is not None |
| 105 | + assert listed_image.image_name == memory_snapshot.image_name |
| 106 | + assert listed_image.namespace == memory_snapshot.image_namespace |
| 107 | + assert isinstance(listed_image.uploaded, bool) |
| 108 | + |
| 109 | + listed_snapshot = await _wait_for_created_snapshot( |
| 110 | + client, memory_snapshot.snapshot_id |
| 111 | + ) |
| 112 | + assert listed_snapshot.id == memory_snapshot.snapshot_id |
| 113 | + assert listed_snapshot.snapshot_name == snapshot_name |
| 114 | + assert listed_snapshot.namespace == memory_snapshot.namespace |
| 115 | + assert listed_snapshot.image_id == memory_snapshot.image_id |
| 116 | + assert listed_snapshot.image_name == memory_snapshot.image_name |
| 117 | + assert listed_snapshot.image_namespace == memory_snapshot.image_namespace |
| 118 | + assert listed_snapshot.status == "created" |
| 119 | + |
| 120 | + created_snapshots = await client.sandboxes.list_snapshots( |
| 121 | + SandboxSnapshotListParams( |
| 122 | + status="created", |
| 123 | + limit=SNAPSHOT_LIST_LIMIT, |
| 124 | + ) |
| 125 | + ) |
| 126 | + assert any( |
| 127 | + entry.id == listed_snapshot.id for entry in created_snapshots.snapshots |
| 128 | + ) |
| 129 | + finally: |
| 130 | + await stop_sandbox_if_running_async(sandbox) |
| 131 | + await client.close() |
0 commit comments