@@ -202,6 +202,67 @@ def test_on_check_not_called_when_immediately_ready(self, mock_sleep):
202202 callback .assert_not_called ()
203203
204204
205+ class TestJobJoinBackoff :
206+ @patch ("python_anticaptcha.base.time.sleep" )
207+ def test_backoff_sleep_schedule (self , mock_sleep ):
208+ client = MagicMock ()
209+ # processing 6 times then ready — enough to hit the 10s cap
210+ client .getTaskResult .side_effect = [
211+ {"status" : "processing" },
212+ {"status" : "processing" },
213+ {"status" : "processing" },
214+ {"status" : "processing" },
215+ {"status" : "processing" },
216+ {"status" : "processing" },
217+ {"status" : "ready" , "solution" : {}},
218+ ]
219+ job = Job (client , task_id = 1 )
220+ job .join (backoff = True )
221+ sleep_values = [call .args [0 ] for call in mock_sleep .call_args_list ]
222+ assert sleep_values == [1 , 2 , 4 , 8 , 10 , 10 ]
223+
224+ @patch ("python_anticaptcha.base.time.sleep" )
225+ def test_backoff_false_uses_fixed_interval (self , mock_sleep ):
226+ client = MagicMock ()
227+ client .getTaskResult .side_effect = [
228+ {"status" : "processing" },
229+ {"status" : "processing" },
230+ {"status" : "processing" },
231+ {"status" : "ready" , "solution" : {}},
232+ ]
233+ job = Job (client , task_id = 1 )
234+ job .join (backoff = False )
235+ sleep_values = [call .args [0 ] for call in mock_sleep .call_args_list ]
236+ assert sleep_values == [SLEEP_EVERY_CHECK_FINISHED ] * 3
237+
238+ @patch ("python_anticaptcha.base.time.sleep" )
239+ def test_backoff_timeout_still_works (self , mock_sleep ):
240+ client = MagicMock ()
241+ client .getTaskResult .return_value = {"status" : "processing" }
242+ job = Job (client , task_id = 1 )
243+ with pytest .raises (AnticaptchaException ) as exc_info :
244+ job .join (maximum_time = 5 , backoff = True )
245+ assert "exceeded" in str (exc_info .value ).lower ()
246+
247+ @patch ("python_anticaptcha.base.time.sleep" )
248+ def test_backoff_with_on_check (self , mock_sleep ):
249+ client = MagicMock ()
250+ client .getTaskResult .side_effect = [
251+ {"status" : "processing" },
252+ {"status" : "processing" },
253+ {"status" : "processing" },
254+ {"status" : "ready" , "solution" : {}},
255+ ]
256+ job = Job (client , task_id = 1 )
257+ callback = MagicMock ()
258+ job .join (backoff = True , on_check = callback )
259+ assert callback .call_count == 3
260+ # Elapsed times: 1, 1+2=3, 3+4=7
261+ callback .assert_any_call (1 , "processing" )
262+ callback .assert_any_call (3 , "processing" )
263+ callback .assert_any_call (7 , "processing" )
264+
265+
205266class TestContextManager :
206267 def test_enter_returns_self (self ):
207268 client = AnticaptchaClient ("key123" )
0 commit comments