@@ -2571,6 +2571,74 @@ def fetch_result() -> dict:
25712571 assert fetch_attempts ["count" ] == 0
25722572
25732573
2574+ def test_wait_for_job_result_does_not_retry_terminal_callback_errors ():
2575+ status_attempts = {"count" : 0 }
2576+ fetch_attempts = {"count" : 0 }
2577+
2578+ def get_status () -> str :
2579+ status_attempts ["count" ] += 1
2580+ return "completed"
2581+
2582+ def fetch_result () -> dict :
2583+ fetch_attempts ["count" ] += 1
2584+ return {"ok" : True }
2585+
2586+ with pytest .raises (HyperbrowserError , match = "is_terminal_status failed" ):
2587+ wait_for_job_result (
2588+ operation_name = "sync wait helper terminal callback exception" ,
2589+ get_status = get_status ,
2590+ is_terminal_status = lambda value : (_ for _ in ()).throw (ValueError ("boom" )),
2591+ fetch_result = fetch_result ,
2592+ poll_interval_seconds = 0.0001 ,
2593+ max_wait_seconds = 1.0 ,
2594+ max_status_failures = 5 ,
2595+ fetch_max_attempts = 5 ,
2596+ fetch_retry_delay_seconds = 0.0001 ,
2597+ )
2598+
2599+ assert status_attempts ["count" ] == 1
2600+ assert fetch_attempts ["count" ] == 0
2601+
2602+
2603+ def test_wait_for_job_result_cancels_awaitable_terminal_callback_results ():
2604+ status_attempts = {"count" : 0 }
2605+ fetch_attempts = {"count" : 0 }
2606+ loop = asyncio .new_event_loop ()
2607+ try :
2608+ callback_future = loop .create_future ()
2609+
2610+ def get_status () -> str :
2611+ status_attempts ["count" ] += 1
2612+ return "completed"
2613+
2614+ def fetch_result () -> dict :
2615+ fetch_attempts ["count" ] += 1
2616+ return {"ok" : True }
2617+
2618+ with pytest .raises (
2619+ HyperbrowserError ,
2620+ match = "is_terminal_status must return a non-awaitable result" ,
2621+ ):
2622+ wait_for_job_result (
2623+ operation_name = "sync wait helper terminal callback future" ,
2624+ get_status = get_status ,
2625+ is_terminal_status = lambda value : callback_future , # type: ignore[return-value]
2626+ fetch_result = fetch_result ,
2627+ poll_interval_seconds = 0.0001 ,
2628+ max_wait_seconds = 1.0 ,
2629+ max_status_failures = 5 ,
2630+ fetch_max_attempts = 5 ,
2631+ fetch_retry_delay_seconds = 0.0001 ,
2632+ )
2633+
2634+ assert callback_future .cancelled ()
2635+ finally :
2636+ loop .close ()
2637+
2638+ assert status_attempts ["count" ] == 1
2639+ assert fetch_attempts ["count" ] == 0
2640+
2641+
25742642def test_wait_for_job_result_retries_rate_limit_status_errors ():
25752643 status_attempts = {"count" : 0 }
25762644 fetch_attempts = {"count" : 0 }
@@ -2916,6 +2984,77 @@ async def fetch_result() -> dict:
29162984 asyncio .run (run ())
29172985
29182986
2987+ def test_wait_for_job_result_async_does_not_retry_terminal_callback_errors ():
2988+ async def run () -> None :
2989+ status_attempts = {"count" : 0 }
2990+ fetch_attempts = {"count" : 0 }
2991+
2992+ async def get_status () -> str :
2993+ status_attempts ["count" ] += 1
2994+ return "completed"
2995+
2996+ async def fetch_result () -> dict :
2997+ fetch_attempts ["count" ] += 1
2998+ return {"ok" : True }
2999+
3000+ with pytest .raises (HyperbrowserError , match = "is_terminal_status failed" ):
3001+ await wait_for_job_result_async (
3002+ operation_name = "async wait helper terminal callback exception" ,
3003+ get_status = get_status ,
3004+ is_terminal_status = lambda value : (_ for _ in ()).throw (
3005+ ValueError ("boom" )
3006+ ),
3007+ fetch_result = fetch_result ,
3008+ poll_interval_seconds = 0.0001 ,
3009+ max_wait_seconds = 1.0 ,
3010+ max_status_failures = 5 ,
3011+ fetch_max_attempts = 5 ,
3012+ fetch_retry_delay_seconds = 0.0001 ,
3013+ )
3014+
3015+ assert status_attempts ["count" ] == 1
3016+ assert fetch_attempts ["count" ] == 0
3017+
3018+ asyncio .run (run ())
3019+
3020+
3021+ def test_wait_for_job_result_async_cancels_awaitable_terminal_callback_results ():
3022+ async def run () -> None :
3023+ status_attempts = {"count" : 0 }
3024+ fetch_attempts = {"count" : 0 }
3025+ callback_future = asyncio .get_running_loop ().create_future ()
3026+
3027+ async def get_status () -> str :
3028+ status_attempts ["count" ] += 1
3029+ return "completed"
3030+
3031+ async def fetch_result () -> dict :
3032+ fetch_attempts ["count" ] += 1
3033+ return {"ok" : True }
3034+
3035+ with pytest .raises (
3036+ HyperbrowserError ,
3037+ match = "is_terminal_status must return a non-awaitable result" ,
3038+ ):
3039+ await wait_for_job_result_async (
3040+ operation_name = "async wait helper terminal callback future" ,
3041+ get_status = get_status ,
3042+ is_terminal_status = lambda value : callback_future , # type: ignore[return-value]
3043+ fetch_result = fetch_result ,
3044+ poll_interval_seconds = 0.0001 ,
3045+ max_wait_seconds = 1.0 ,
3046+ max_status_failures = 5 ,
3047+ fetch_max_attempts = 5 ,
3048+ fetch_retry_delay_seconds = 0.0001 ,
3049+ )
3050+
3051+ assert callback_future .cancelled ()
3052+ assert status_attempts ["count" ] == 1
3053+ assert fetch_attempts ["count" ] == 0
3054+
3055+ asyncio .run (run ())
3056+
3057+
29193058def test_wait_for_job_result_async_retries_rate_limit_status_errors ():
29203059 async def run () -> None :
29213060 status_attempts = {"count" : 0 }
0 commit comments