Skip to content

Commit 91fc21f

Browse files
Validate pagination page-batch metadata bounds
Co-authored-by: Shri Sukhani <shrisukhani@users.noreply.github.com>
1 parent d8994ef commit 91fc21f

File tree

2 files changed

+76
-1
lines changed

2 files changed

+76
-1
lines changed

hyperbrowser/client/polling.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,38 @@ def _validate_max_wait_seconds(max_wait_seconds: Optional[float]) -> None:
6262
raise HyperbrowserError("max_wait_seconds must be non-negative")
6363

6464

65+
def _validate_page_batch_values(
66+
*,
67+
operation_name: str,
68+
current_page_batch: int,
69+
total_page_batches: int,
70+
) -> None:
71+
if isinstance(current_page_batch, bool) or not isinstance(current_page_batch, int):
72+
raise HyperbrowserPollingError(
73+
f"Invalid current page batch for {operation_name}: expected integer"
74+
)
75+
if isinstance(total_page_batches, bool) or not isinstance(total_page_batches, int):
76+
raise HyperbrowserPollingError(
77+
f"Invalid total page batches for {operation_name}: expected integer"
78+
)
79+
if total_page_batches < 0:
80+
raise HyperbrowserPollingError(
81+
f"Invalid total page batches for {operation_name}: must be non-negative"
82+
)
83+
if current_page_batch < 0:
84+
raise HyperbrowserPollingError(
85+
f"Invalid current page batch for {operation_name}: must be non-negative"
86+
)
87+
if total_page_batches > 0 and current_page_batch < 1:
88+
raise HyperbrowserPollingError(
89+
f"Invalid current page batch for {operation_name}: must be at least 1 when total batches are positive"
90+
)
91+
if current_page_batch > total_page_batches:
92+
raise HyperbrowserPollingError(
93+
f"Invalid page batch state for {operation_name}: current page batch {current_page_batch} exceeds total page batches {total_page_batches}"
94+
)
95+
96+
6597
def has_exceeded_max_wait(start_time: float, max_wait_seconds: Optional[float]) -> bool:
6698
return (
6799
max_wait_seconds is not None
@@ -241,6 +273,11 @@ def collect_paginated_results(
241273
on_page_success(page_response)
242274
current_page_batch = get_current_page_batch(page_response)
243275
total_page_batches = get_total_page_batches(page_response)
276+
_validate_page_batch_values(
277+
operation_name=operation_name,
278+
current_page_batch=current_page_batch,
279+
total_page_batches=total_page_batches,
280+
)
244281
failures = 0
245282
first_check = False
246283
if (
@@ -303,6 +340,11 @@ async def collect_paginated_results_async(
303340
on_page_success(page_response)
304341
current_page_batch = get_current_page_batch(page_response)
305342
total_page_batches = get_total_page_batches(page_response)
343+
_validate_page_batch_values(
344+
operation_name=operation_name,
345+
current_page_batch=current_page_batch,
346+
total_page_batches=total_page_batches,
347+
)
306348
failures = 0
307349
first_check = False
308350
if (

tests/test_polling.py

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,20 @@ def test_collect_paginated_results_raises_when_page_batch_stagnates():
303303
)
304304

305305

306+
def test_collect_paginated_results_raises_on_invalid_page_batch_values():
307+
with pytest.raises(HyperbrowserPollingError, match="Invalid page batch state"):
308+
collect_paginated_results(
309+
operation_name="sync paginated invalid batches",
310+
get_next_page=lambda page: {"current": 3, "total": 2, "items": []},
311+
get_current_page_batch=lambda response: response["current"],
312+
get_total_page_batches=lambda response: response["total"],
313+
on_page_success=lambda response: None,
314+
max_wait_seconds=1.0,
315+
max_attempts=2,
316+
retry_delay_seconds=0.0001,
317+
)
318+
319+
306320
def test_collect_paginated_results_async_times_out():
307321
async def run() -> None:
308322
with pytest.raises(
@@ -312,7 +326,7 @@ async def run() -> None:
312326
await collect_paginated_results_async(
313327
operation_name="async paginated timeout",
314328
get_next_page=lambda page: asyncio.sleep(
315-
0, result={"current": 0, "total": 1, "items": []}
329+
0, result={"current": 1, "total": 2, "items": []}
316330
),
317331
get_current_page_batch=lambda response: response["current"],
318332
get_total_page_batches=lambda response: response["total"],
@@ -344,6 +358,25 @@ async def run() -> None:
344358
asyncio.run(run())
345359

346360

361+
def test_collect_paginated_results_async_raises_on_invalid_page_batch_values():
362+
async def run() -> None:
363+
with pytest.raises(HyperbrowserPollingError, match="Invalid page batch state"):
364+
await collect_paginated_results_async(
365+
operation_name="async paginated invalid batches",
366+
get_next_page=lambda page: asyncio.sleep(
367+
0, result={"current": 3, "total": 2, "items": []}
368+
),
369+
get_current_page_batch=lambda response: response["current"],
370+
get_total_page_batches=lambda response: response["total"],
371+
on_page_success=lambda response: None,
372+
max_wait_seconds=1.0,
373+
max_attempts=2,
374+
retry_delay_seconds=0.0001,
375+
)
376+
377+
asyncio.run(run())
378+
379+
347380
def test_wait_for_job_result_returns_fetched_value():
348381
status_values = iter(["running", "completed"])
349382

0 commit comments

Comments
 (0)