From b201e831dffde9acfbdab8cd90c9b818a1296f07 Mon Sep 17 00:00:00 2001 From: Devin Date: Wed, 8 Jul 2026 13:49:24 -0700 Subject: [PATCH] patch the network updat --- .../client/managers/async_manager/sandbox.py | 6 +- .../client/managers/sync_manager/sandbox.py | 6 +- pyproject.toml | 2 +- tests/test_create_sandbox_params.py | 22 ++++++++ tests/test_sandbox_wire_contract.py | 55 +++++++++++++++++++ 5 files changed, 88 insertions(+), 3 deletions(-) diff --git a/hyperbrowser/client/managers/async_manager/sandbox.py b/hyperbrowser/client/managers/async_manager/sandbox.py index ded15446..f8b8bf2f 100644 --- a/hyperbrowser/client/managers/async_manager/sandbox.py +++ b/hyperbrowser/client/managers/async_manager/sandbox.py @@ -635,7 +635,11 @@ async def update_network( payload = await self._request( "PUT", f"/sandbox/{sandbox_id}/network", - data=policy.model_dump(exclude_none=True, by_alias=True), + data=policy.model_dump( + exclude_none=True, + exclude_unset=True, + by_alias=True, + ), ) return SandboxNetworkUpdateResult(**payload) diff --git a/hyperbrowser/client/managers/sync_manager/sandbox.py b/hyperbrowser/client/managers/sync_manager/sandbox.py index 6ad4f849..0b88e9fc 100644 --- a/hyperbrowser/client/managers/sync_manager/sandbox.py +++ b/hyperbrowser/client/managers/sync_manager/sandbox.py @@ -624,7 +624,11 @@ def update_network( payload = self._request( "PUT", f"/sandbox/{sandbox_id}/network", - data=policy.model_dump(exclude_none=True, by_alias=True), + data=policy.model_dump( + exclude_none=True, + exclude_unset=True, + by_alias=True, + ), ) return SandboxNetworkUpdateResult(**payload) diff --git a/pyproject.toml b/pyproject.toml index b28c9bed..d5c0b909 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "hyperbrowser" -version = "0.93.0" +version = "0.93.1" description = "Python SDK for hyperbrowser" authors = ["Nikhil Shahi "] license = "MIT" diff --git a/tests/test_create_sandbox_params.py b/tests/test_create_sandbox_params.py index d6f69051..293b0fd2 100644 --- a/tests/test_create_sandbox_params.py +++ b/tests/test_create_sandbox_params.py @@ -95,6 +95,28 @@ def test_sandbox_network_policy_serializes_update_payload(): } +def test_sandbox_network_policy_defaults_unset_lists_for_response_models(): + policy = SandboxNetworkPolicy(allow_internet_access=True) + + assert policy.model_dump(by_alias=True, exclude_none=True) == { + "allowInternetAccess": True, + "allowOut": [], + "denyOut": [], + } + + +def test_sandbox_network_policy_can_omit_unset_lists_for_patch_payload(): + policy = SandboxNetworkPolicy(allow_internet_access=True) + + assert policy.model_dump( + by_alias=True, + exclude_none=True, + exclude_unset=True, + ) == { + "allowInternetAccess": True, + } + + def test_sandbox_image_build_params_serialize_expected_wire_keys(): create_params = CreateSandboxImageBuildParams( image_name="custom_node", diff --git a/tests/test_sandbox_wire_contract.py b/tests/test_sandbox_wire_contract.py index e403a3f1..53814ea8 100644 --- a/tests/test_sandbox_wire_contract.py +++ b/tests/test_sandbox_wire_contract.py @@ -699,6 +699,12 @@ def test_sandbox_request_models_serialize_expected_wire_keys(): "allowOut": [], "denyOut": [], } + assert SandboxNetworkPolicy(allow_internet_access=True).model_dump( + by_alias=True, exclude_none=True + ) == {"allowInternetAccess": True, "allowOut": [], "denyOut": []} + assert SandboxNetworkPolicy(allow_internet_access=True).model_dump( + by_alias=True, exclude_none=True, exclude_unset=True + ) == {"allowInternetAccess": True} assert SandboxImageListParams( page=2, @@ -1074,6 +1080,30 @@ def test_sync_sandbox_control_manager_uses_expected_wire_keys(): assert unexpose_call["url"].endswith("/sandbox/sbx_123/unexpose") +def test_sync_sandbox_update_network_omits_only_unset_lists(): + client = FakeSyncClient() + manager = SandboxManager(client) + sandbox = manager.attach(SandboxDetail(**SANDBOX_DETAIL_PAYLOAD)) + + sandbox.update_network(SandboxNetworkPolicy(allow_internet_access=True)) + sandbox.update_network( + SandboxNetworkPolicy( + allow_internet_access=True, + allow_out=[], + deny_out=[], + ) + ) + + preserve_call = client.transport.client.calls[0] + clear_call = client.transport.client.calls[1] + assert preserve_call["json"] == {"allowInternetAccess": True} + assert clear_call["json"] == { + "allowInternetAccess": True, + "allowOut": [], + "denyOut": [], + } + + def test_snapshot_summary_allows_missing_compatibility_tag(): snapshot = SandboxSnapshotSummary(**SNAPSHOT_PAYLOAD_WITHOUT_COMPATIBILITY_TAG) @@ -1528,6 +1558,31 @@ async def test_async_sandbox_control_manager_uses_expected_wire_keys(): assert unexpose_call["json"] == {"port": 3000} +@pytest.mark.anyio +async def test_async_sandbox_update_network_omits_only_unset_lists(): + client = FakeAsyncClient() + manager = AsyncSandboxManager(client) + sandbox = manager.attach(SandboxDetail(**SANDBOX_DETAIL_PAYLOAD)) + + await sandbox.update_network(SandboxNetworkPolicy(allow_internet_access=True)) + await sandbox.update_network( + SandboxNetworkPolicy( + allow_internet_access=True, + allow_out=[], + deny_out=[], + ) + ) + + preserve_call = client.transport.client.calls[0] + clear_call = client.transport.client.calls[1] + assert preserve_call["json"] == {"allowInternetAccess": True} + assert clear_call["json"] == { + "allowInternetAccess": True, + "allowOut": [], + "denyOut": [], + } + + @pytest.mark.anyio async def test_async_sandbox_runtime_apis_use_expected_wire_keys(): transport = AsyncRecordingTransport()