@@ -1437,6 +1437,31 @@ def get_next_page(page: int) -> dict:
14371437 assert attempts ["count" ] == 3
14381438
14391439
1440+ def test_collect_paginated_results_retries_request_timeout_errors ():
1441+ attempts = {"count" : 0 }
1442+ collected = []
1443+
1444+ def get_next_page (page : int ) -> dict :
1445+ attempts ["count" ] += 1
1446+ if attempts ["count" ] < 3 :
1447+ raise HyperbrowserError ("request timeout" , status_code = 408 )
1448+ return {"current" : 1 , "total" : 1 , "items" : ["a" ]}
1449+
1450+ collect_paginated_results (
1451+ operation_name = "sync paginated request-timeout retries" ,
1452+ get_next_page = get_next_page ,
1453+ get_current_page_batch = lambda response : response ["current" ],
1454+ get_total_page_batches = lambda response : response ["total" ],
1455+ on_page_success = lambda response : collected .extend (response ["items" ]),
1456+ max_wait_seconds = 1.0 ,
1457+ max_attempts = 5 ,
1458+ retry_delay_seconds = 0.0001 ,
1459+ )
1460+
1461+ assert collected == ["a" ]
1462+ assert attempts ["count" ] == 3
1463+
1464+
14401465def test_collect_paginated_results_raises_when_page_batch_stagnates ():
14411466 with pytest .raises (HyperbrowserPollingError , match = "No pagination progress" ):
14421467 collect_paginated_results (
@@ -1709,6 +1734,34 @@ async def get_next_page(page: int) -> dict:
17091734 asyncio .run (run ())
17101735
17111736
1737+ def test_collect_paginated_results_async_retries_request_timeout_errors ():
1738+ async def run () -> None :
1739+ attempts = {"count" : 0 }
1740+ collected = []
1741+
1742+ async def get_next_page (page : int ) -> dict :
1743+ attempts ["count" ] += 1
1744+ if attempts ["count" ] < 3 :
1745+ raise HyperbrowserError ("request timeout" , status_code = 408 )
1746+ return {"current" : 1 , "total" : 1 , "items" : ["a" ]}
1747+
1748+ await collect_paginated_results_async (
1749+ operation_name = "async paginated request-timeout retries" ,
1750+ get_next_page = get_next_page ,
1751+ get_current_page_batch = lambda response : response ["current" ],
1752+ get_total_page_batches = lambda response : response ["total" ],
1753+ on_page_success = lambda response : collected .extend (response ["items" ]),
1754+ max_wait_seconds = 1.0 ,
1755+ max_attempts = 5 ,
1756+ retry_delay_seconds = 0.0001 ,
1757+ )
1758+
1759+ assert collected == ["a" ]
1760+ assert attempts ["count" ] == 3
1761+
1762+ asyncio .run (run ())
1763+
1764+
17121765def test_wait_for_job_result_returns_fetched_value ():
17131766 status_values = iter (["running" , "completed" ])
17141767
@@ -1775,6 +1828,31 @@ def fetch_result() -> dict:
17751828 assert fetch_attempts ["count" ] == 3
17761829
17771830
1831+ def test_wait_for_job_result_retries_request_timeout_fetch_errors ():
1832+ fetch_attempts = {"count" : 0 }
1833+
1834+ def fetch_result () -> dict :
1835+ fetch_attempts ["count" ] += 1
1836+ if fetch_attempts ["count" ] < 3 :
1837+ raise HyperbrowserError ("request timeout" , status_code = 408 )
1838+ return {"ok" : True }
1839+
1840+ result = wait_for_job_result (
1841+ operation_name = "sync wait helper request-timeout retries" ,
1842+ get_status = lambda : "completed" ,
1843+ is_terminal_status = lambda value : value == "completed" ,
1844+ fetch_result = fetch_result ,
1845+ poll_interval_seconds = 0.0001 ,
1846+ max_wait_seconds = 1.0 ,
1847+ max_status_failures = 2 ,
1848+ fetch_max_attempts = 5 ,
1849+ fetch_retry_delay_seconds = 0.0001 ,
1850+ )
1851+
1852+ assert result == {"ok" : True }
1853+ assert fetch_attempts ["count" ] == 3
1854+
1855+
17781856def test_wait_for_job_result_async_returns_fetched_value ():
17791857 async def run () -> None :
17801858 status_values = iter (["running" , "completed" ])
@@ -1850,6 +1928,34 @@ async def fetch_result() -> dict:
18501928 asyncio .run (run ())
18511929
18521930
1931+ def test_wait_for_job_result_async_retries_request_timeout_fetch_errors ():
1932+ async def run () -> None :
1933+ fetch_attempts = {"count" : 0 }
1934+
1935+ async def fetch_result () -> dict :
1936+ fetch_attempts ["count" ] += 1
1937+ if fetch_attempts ["count" ] < 3 :
1938+ raise HyperbrowserError ("request timeout" , status_code = 408 )
1939+ return {"ok" : True }
1940+
1941+ result = await wait_for_job_result_async (
1942+ operation_name = "async wait helper request-timeout retries" ,
1943+ get_status = lambda : asyncio .sleep (0 , result = "completed" ),
1944+ is_terminal_status = lambda value : value == "completed" ,
1945+ fetch_result = fetch_result ,
1946+ poll_interval_seconds = 0.0001 ,
1947+ max_wait_seconds = 1.0 ,
1948+ max_status_failures = 2 ,
1949+ fetch_max_attempts = 5 ,
1950+ fetch_retry_delay_seconds = 0.0001 ,
1951+ )
1952+
1953+ assert result == {"ok" : True }
1954+ assert fetch_attempts ["count" ] == 3
1955+
1956+ asyncio .run (run ())
1957+
1958+
18531959def test_wait_for_job_result_validates_configuration ():
18541960 with pytest .raises (HyperbrowserError , match = "max_attempts must be at least 1" ):
18551961 wait_for_job_result (
0 commit comments