Skip to content

Commit ab433f3

Browse files
Reuse shared endpoint request helpers for computer actions
Co-authored-by: Shri Sukhani <shrisukhani@users.noreply.github.com>
1 parent ba7ea1c commit ab433f3

7 files changed

Lines changed: 160 additions & 50 deletions

CONTRIBUTING.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ This runs lint, format checks, compile checks, tests, and package build.
9494
- `tests/test_computer_action_operation_metadata_usage.py` (computer-action manager operation-metadata usage enforcement),
9595
- `tests/test_computer_action_payload_helper_usage.py` (computer-action payload helper usage enforcement),
9696
- `tests/test_computer_action_request_helper_usage.py` (computer-action manager request-helper usage enforcement),
97+
- `tests/test_computer_action_request_internal_reuse.py` (computer-action request-helper internal reuse of shared model request endpoint helpers),
9798
- `tests/test_contributing_architecture_guard_listing.py` (`CONTRIBUTING.md` architecture-guard inventory completeness enforcement),
9899
- `tests/test_core_type_helper_usage.py` (core transport/config/header/file/polling/session/error/parsing manager+tool module enforcement of shared plain-type helper usage),
99100
- `tests/test_default_serialization_helper_usage.py` (default optional-query serialization helper usage enforcement),

hyperbrowser/client/managers/computer_action_request_utils.py

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@
22

33
from hyperbrowser.models import ComputerActionResponse
44

5-
from .response_utils import parse_response_model
5+
from .model_request_utils import (
6+
post_model_request_to_endpoint,
7+
post_model_request_to_endpoint_async,
8+
)
69

710

811
def execute_computer_action_request(
@@ -12,12 +15,10 @@ def execute_computer_action_request(
1215
payload: dict[str, Any],
1316
operation_name: str,
1417
) -> ComputerActionResponse:
15-
response = client.transport.post(
16-
endpoint,
18+
return post_model_request_to_endpoint(
19+
client=client,
20+
endpoint=endpoint,
1721
data=payload,
18-
)
19-
return parse_response_model(
20-
response.data,
2122
model=ComputerActionResponse,
2223
operation_name=operation_name,
2324
)
@@ -30,12 +31,10 @@ async def execute_computer_action_request_async(
3031
payload: dict[str, Any],
3132
operation_name: str,
3233
) -> ComputerActionResponse:
33-
response = await client.transport.post(
34-
endpoint,
34+
return await post_model_request_to_endpoint_async(
35+
client=client,
36+
endpoint=endpoint,
3537
data=payload,
36-
)
37-
return parse_response_model(
38-
response.data,
3938
model=ComputerActionResponse,
4039
operation_name=operation_name,
4140
)

hyperbrowser/client/managers/model_request_utils.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,3 +167,41 @@ async def put_model_request_async(
167167
model=model,
168168
operation_name=operation_name,
169169
)
170+
171+
172+
def post_model_request_to_endpoint(
173+
*,
174+
client: Any,
175+
endpoint: str,
176+
data: Dict[str, Any],
177+
model: Type[T],
178+
operation_name: str,
179+
) -> T:
180+
response = client.transport.post(
181+
endpoint,
182+
data=data,
183+
)
184+
return parse_response_model(
185+
response.data,
186+
model=model,
187+
operation_name=operation_name,
188+
)
189+
190+
191+
async def post_model_request_to_endpoint_async(
192+
*,
193+
client: Any,
194+
endpoint: str,
195+
data: Dict[str, Any],
196+
model: Type[T],
197+
operation_name: str,
198+
) -> T:
199+
response = await client.transport.post(
200+
endpoint,
201+
data=data,
202+
)
203+
return parse_response_model(
204+
response.data,
205+
model=model,
206+
operation_name=operation_name,
207+
)

tests/test_architecture_marker_usage.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@
7878
"tests/test_computer_action_operation_metadata_usage.py",
7979
"tests/test_computer_action_payload_helper_usage.py",
8080
"tests/test_computer_action_request_helper_usage.py",
81+
"tests/test_computer_action_request_internal_reuse.py",
8182
"tests/test_schema_injection_helper_usage.py",
8283
"tests/test_session_operation_metadata_usage.py",
8384
"tests/test_session_route_constants_usage.py",
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
from pathlib import Path
2+
3+
import pytest
4+
5+
pytestmark = pytest.mark.architecture
6+
7+
8+
def test_computer_action_request_utils_reuse_model_request_endpoint_helpers():
9+
module_text = Path(
10+
"hyperbrowser/client/managers/computer_action_request_utils.py"
11+
).read_text(encoding="utf-8")
12+
assert "model_request_utils import" in module_text
13+
assert "post_model_request_to_endpoint(" in module_text
14+
assert "post_model_request_to_endpoint_async(" in module_text
15+
assert "client.transport.post(" not in module_text
16+
assert "parse_response_model(" not in module_text
Lines changed: 18 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,78 +1,57 @@
11
import asyncio
2-
from types import SimpleNamespace
32

43
import hyperbrowser.client.managers.computer_action_request_utils as request_utils
54

65

7-
def test_execute_computer_action_request_posts_and_parses_response():
6+
def test_execute_computer_action_request_delegates_to_endpoint_post_helper():
87
captured = {}
98

10-
class _SyncTransport:
11-
def post(self, endpoint, data=None):
12-
captured["endpoint"] = endpoint
13-
captured["data"] = data
14-
return SimpleNamespace(data={"success": True})
15-
16-
class _Client:
17-
transport = _SyncTransport()
18-
19-
def _fake_parse_response_model(data, **kwargs):
20-
captured["parse_data"] = data
21-
captured["parse_kwargs"] = kwargs
9+
def _fake_post_model_request_to_endpoint(**kwargs):
10+
captured.update(kwargs)
2211
return {"parsed": True}
2312

24-
original_parse = request_utils.parse_response_model
25-
request_utils.parse_response_model = _fake_parse_response_model
13+
original = request_utils.post_model_request_to_endpoint
14+
request_utils.post_model_request_to_endpoint = _fake_post_model_request_to_endpoint
2615
try:
2716
result = request_utils.execute_computer_action_request(
28-
client=_Client(),
17+
client=object(),
2918
endpoint="https://example.com/cua",
3019
payload={"action": {"type": "screenshot"}},
3120
operation_name="computer action",
3221
)
3322
finally:
34-
request_utils.parse_response_model = original_parse
23+
request_utils.post_model_request_to_endpoint = original
3524

3625
assert result == {"parsed": True}
3726
assert captured["endpoint"] == "https://example.com/cua"
3827
assert captured["data"] == {"action": {"type": "screenshot"}}
39-
assert captured["parse_data"] == {"success": True}
40-
assert captured["parse_kwargs"]["operation_name"] == "computer action"
28+
assert captured["operation_name"] == "computer action"
4129

4230

43-
def test_execute_computer_action_request_async_posts_and_parses_response():
31+
def test_execute_computer_action_request_async_delegates_to_endpoint_post_helper():
4432
captured = {}
4533

46-
class _AsyncTransport:
47-
async def post(self, endpoint, data=None):
48-
captured["endpoint"] = endpoint
49-
captured["data"] = data
50-
return SimpleNamespace(data={"success": True})
51-
52-
class _Client:
53-
transport = _AsyncTransport()
54-
55-
def _fake_parse_response_model(data, **kwargs):
56-
captured["parse_data"] = data
57-
captured["parse_kwargs"] = kwargs
34+
async def _fake_post_model_request_to_endpoint_async(**kwargs):
35+
captured.update(kwargs)
5836
return {"parsed": True}
5937

60-
original_parse = request_utils.parse_response_model
61-
request_utils.parse_response_model = _fake_parse_response_model
38+
original = request_utils.post_model_request_to_endpoint_async
39+
request_utils.post_model_request_to_endpoint_async = (
40+
_fake_post_model_request_to_endpoint_async
41+
)
6242
try:
6343
result = asyncio.run(
6444
request_utils.execute_computer_action_request_async(
65-
client=_Client(),
45+
client=object(),
6646
endpoint="https://example.com/cua",
6747
payload={"action": {"type": "screenshot"}},
6848
operation_name="computer action",
6949
)
7050
)
7151
finally:
72-
request_utils.parse_response_model = original_parse
52+
request_utils.post_model_request_to_endpoint_async = original
7353

7454
assert result == {"parsed": True}
7555
assert captured["endpoint"] == "https://example.com/cua"
7656
assert captured["data"] == {"action": {"type": "screenshot"}}
77-
assert captured["parse_data"] == {"success": True}
78-
assert captured["parse_kwargs"]["operation_name"] == "computer action"
57+
assert captured["operation_name"] == "computer action"

tests/test_model_request_utils.py

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -334,3 +334,79 @@ def _fake_parse_response_model(data, **kwargs):
334334
assert captured["url"] == "https://api.example.test/resource/6"
335335
assert captured["parse_data"] == {"success": True}
336336
assert captured["parse_kwargs"]["operation_name"] == "delete resource"
337+
338+
339+
def test_post_model_request_to_endpoint_posts_and_parses_response():
340+
captured = {}
341+
342+
class _SyncTransport:
343+
def post(self, endpoint, data):
344+
captured["endpoint"] = endpoint
345+
captured["data"] = data
346+
return SimpleNamespace(data={"id": "endpoint-resource"})
347+
348+
class _Client:
349+
transport = _SyncTransport()
350+
351+
def _fake_parse_response_model(data, **kwargs):
352+
captured["parse_data"] = data
353+
captured["parse_kwargs"] = kwargs
354+
return {"parsed": True}
355+
356+
original_parse = model_request_utils.parse_response_model
357+
model_request_utils.parse_response_model = _fake_parse_response_model
358+
try:
359+
result = model_request_utils.post_model_request_to_endpoint(
360+
client=_Client(),
361+
endpoint="https://api.example.test/resource",
362+
data={"name": "value"},
363+
model=object,
364+
operation_name="endpoint create",
365+
)
366+
finally:
367+
model_request_utils.parse_response_model = original_parse
368+
369+
assert result == {"parsed": True}
370+
assert captured["endpoint"] == "https://api.example.test/resource"
371+
assert captured["data"] == {"name": "value"}
372+
assert captured["parse_data"] == {"id": "endpoint-resource"}
373+
assert captured["parse_kwargs"]["operation_name"] == "endpoint create"
374+
375+
376+
def test_post_model_request_to_endpoint_async_posts_and_parses_response():
377+
captured = {}
378+
379+
class _AsyncTransport:
380+
async def post(self, endpoint, data):
381+
captured["endpoint"] = endpoint
382+
captured["data"] = data
383+
return SimpleNamespace(data={"id": "endpoint-resource-async"})
384+
385+
class _Client:
386+
transport = _AsyncTransport()
387+
388+
def _fake_parse_response_model(data, **kwargs):
389+
captured["parse_data"] = data
390+
captured["parse_kwargs"] = kwargs
391+
return {"parsed": True}
392+
393+
original_parse = model_request_utils.parse_response_model
394+
model_request_utils.parse_response_model = _fake_parse_response_model
395+
try:
396+
result = asyncio.run(
397+
model_request_utils.post_model_request_to_endpoint_async(
398+
client=_Client(),
399+
endpoint="https://api.example.test/resource",
400+
data={"name": "value"},
401+
model=object,
402+
operation_name="endpoint create",
403+
)
404+
)
405+
finally:
406+
model_request_utils.parse_response_model = original_parse
407+
408+
assert result == {"parsed": True}
409+
assert captured["endpoint"] == "https://api.example.test/resource"
410+
assert captured["data"] == {"name": "value"}
411+
assert captured["parse_data"] == {"id": "endpoint-resource-async"}
412+
assert captured["parse_kwargs"]["operation_name"] == "endpoint create"

0 commit comments

Comments
 (0)