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