@@ -491,7 +491,7 @@ def unsuccessful_jwt_response(box_datetime, status_code, error_description, incl
491491@pytest .mark .parametrize ('jwt_algorithm' , ('RS512' ,))
492492@pytest .mark .parametrize ('rsa_passphrase' , (None ,))
493493@pytest .mark .parametrize ('pass_private_key_by_path' , (False ,))
494- @pytest .mark .parametrize ('status_code' , (400 , 401 , 429 , 500 ))
494+ @pytest .mark .parametrize ('status_code' , (400 , 401 ))
495495@pytest .mark .parametrize ('error_description' , ('invalid box_sub_type claim' , 'invalid kid' , "check the 'exp' claim" ))
496496@pytest .mark .parametrize ('error_code' , ('invalid_grant' , 'bad_request' ))
497497@pytest .mark .parametrize ('include_date_header' , (True , False ))
@@ -512,8 +512,132 @@ def test_auth_retry_for_invalid_exp_claim(
512512 auth .authenticate_instance (enterprise_id )
513513 else :
514514 auth .authenticate_instance (enterprise_id )
515- expected_calls = [call (enterprise_id , 'enterprise' )]
515+ expected_calls = [call (enterprise_id , 'enterprise' , None )]
516516 if expect_auth_retry :
517517 expected_calls .append (call (enterprise_id , 'enterprise' , box_datetime .replace (microsecond = 0 , tzinfo = None )))
518518 assert len (mock_send_jwt .mock_calls ) == len (expected_calls )
519519 mock_send_jwt .assert_has_calls (expected_calls )
520+
521+
522+ @pytest .mark .parametrize ('jwt_algorithm' , ('RS512' ,))
523+ @pytest .mark .parametrize ('rsa_passphrase' , (None ,))
524+ @pytest .mark .parametrize ('pass_private_key_by_path' , (False ,))
525+ @pytest .mark .parametrize ('status_code' , (429 ,))
526+ @pytest .mark .parametrize ('error_description' , ('Request rate limit exceeded' ,))
527+ @pytest .mark .parametrize ('error_code' , ('rate_limit_exceeded' ,))
528+ @pytest .mark .parametrize ('include_date_header' , (False ,))
529+ def test_auth_retry_for_rate_limit_error (
530+ jwt_auth_init_mocks ,
531+ unsuccessful_jwt_response ,
532+ ):
533+ # pylint:disable=redefined-outer-name
534+ enterprise_id = 'fake_enterprise_id'
535+ with jwt_auth_init_mocks (assert_authed = False ) as params :
536+ auth = params [0 ]
537+ with patch .object (auth , '_construct_and_send_jwt_auth' ) as mock_send_jwt :
538+ side_effect = []
539+ expected_calls = []
540+ # Retries multiple times, but less than max retries. Then succeeds when it gets a token.
541+ for _ in range (API .MAX_RETRY_ATTEMPTS - 2 ):
542+ side_effect .append (BoxOAuthException (429 , network_response = unsuccessful_jwt_response ))
543+ expected_calls .append (call (enterprise_id , 'enterprise' , None ))
544+ side_effect .append ('jwt_token' )
545+ expected_calls .append (call (enterprise_id , 'enterprise' , None ))
546+ mock_send_jwt .side_effect = side_effect
547+
548+ auth .authenticate_instance (enterprise_id )
549+ assert len (mock_send_jwt .mock_calls ) == len (expected_calls )
550+ mock_send_jwt .assert_has_calls (expected_calls )
551+
552+
553+ @pytest .mark .parametrize ('jwt_algorithm' , ('RS512' ,))
554+ @pytest .mark .parametrize ('rsa_passphrase' , (None ,))
555+ @pytest .mark .parametrize ('pass_private_key_by_path' , (False ,))
556+ @pytest .mark .parametrize ('status_code' , (429 ,))
557+ @pytest .mark .parametrize ('error_description' , ('Request rate limit exceeded' ,))
558+ @pytest .mark .parametrize ('error_code' , ('rate_limit_exceeded' ,))
559+ @pytest .mark .parametrize ('include_date_header' , (False ,))
560+ def test_auth_max_retries_for_rate_limit_error (
561+ jwt_auth_init_mocks ,
562+ unsuccessful_jwt_response ,
563+ ):
564+ # pylint:disable=redefined-outer-name
565+ enterprise_id = 'fake_enterprise_id'
566+ with jwt_auth_init_mocks (assert_authed = False ) as params :
567+ auth = params [0 ]
568+ with patch .object (auth , '_construct_and_send_jwt_auth' ) as mock_send_jwt :
569+ side_effect = []
570+ expected_calls = []
571+ # Retries max number of times, then throws the error
572+ for _ in range (API .MAX_RETRY_ATTEMPTS + 1 ):
573+ side_effect .append (BoxOAuthException (429 , network_response = unsuccessful_jwt_response ))
574+ expected_calls .append (call (enterprise_id , 'enterprise' , None ))
575+ mock_send_jwt .side_effect = side_effect
576+
577+ with pytest .raises (BoxOAuthException ) as error :
578+ auth .authenticate_instance (enterprise_id )
579+ assert error .value .status == 429
580+ assert len (mock_send_jwt .mock_calls ) == len (expected_calls )
581+ mock_send_jwt .assert_has_calls (expected_calls )
582+
583+
584+ @pytest .mark .parametrize ('jwt_algorithm' , ('RS512' ,))
585+ @pytest .mark .parametrize ('rsa_passphrase' , (None ,))
586+ @pytest .mark .parametrize ('pass_private_key_by_path' , (False ,))
587+ @pytest .mark .parametrize ('status_code' , (500 ,))
588+ @pytest .mark .parametrize ('error_description' , ('Internal Server Error' ,))
589+ @pytest .mark .parametrize ('error_code' , ('internal_server_error' ,))
590+ @pytest .mark .parametrize ('include_date_header' , (False ,))
591+ def test_auth_retry_for_internal_server_error (
592+ jwt_auth_init_mocks ,
593+ unsuccessful_jwt_response ,
594+ ):
595+ # pylint:disable=redefined-outer-name
596+ enterprise_id = 'fake_enterprise_id'
597+ with jwt_auth_init_mocks (assert_authed = False ) as params :
598+ auth = params [0 ]
599+ with patch .object (auth , '_construct_and_send_jwt_auth' ) as mock_send_jwt :
600+ side_effect = []
601+ expected_calls = []
602+ # Retries multiple times, but less than max retries. Then succeeds when it gets a token.
603+ for _ in range (API .MAX_RETRY_ATTEMPTS - 2 ):
604+ side_effect .append (BoxOAuthException (500 , network_response = unsuccessful_jwt_response ))
605+ expected_calls .append (call (enterprise_id , 'enterprise' , None ))
606+ side_effect .append ('jwt_token' )
607+ expected_calls .append (call (enterprise_id , 'enterprise' , None ))
608+ mock_send_jwt .side_effect = side_effect
609+
610+ auth .authenticate_instance (enterprise_id )
611+ assert len (mock_send_jwt .mock_calls ) == len (expected_calls )
612+ mock_send_jwt .assert_has_calls (expected_calls )
613+
614+
615+ @pytest .mark .parametrize ('jwt_algorithm' , ('RS512' ,))
616+ @pytest .mark .parametrize ('rsa_passphrase' , (None ,))
617+ @pytest .mark .parametrize ('pass_private_key_by_path' , (False ,))
618+ @pytest .mark .parametrize ('status_code' , (500 ,))
619+ @pytest .mark .parametrize ('error_description' , ('Internal Server Error' ,))
620+ @pytest .mark .parametrize ('error_code' , ('internal_server_error' ,))
621+ @pytest .mark .parametrize ('include_date_header' , (False ,))
622+ def test_auth_max_retries_for_internal_server_error (
623+ jwt_auth_init_mocks ,
624+ unsuccessful_jwt_response ,
625+ ):
626+ # pylint:disable=redefined-outer-name
627+ enterprise_id = 'fake_enterprise_id'
628+ with jwt_auth_init_mocks (assert_authed = False ) as params :
629+ auth = params [0 ]
630+ with patch .object (auth , '_construct_and_send_jwt_auth' ) as mock_send_jwt :
631+ side_effect = []
632+ expected_calls = []
633+ # Retries max number of times, then throws the error
634+ for _ in range (API .MAX_RETRY_ATTEMPTS + 1 ):
635+ side_effect .append (BoxOAuthException (500 , network_response = unsuccessful_jwt_response ))
636+ expected_calls .append (call (enterprise_id , 'enterprise' , None ))
637+ mock_send_jwt .side_effect = side_effect
638+
639+ with pytest .raises (BoxOAuthException ) as error :
640+ auth .authenticate_instance (enterprise_id )
641+ assert error .value .status == 500
642+ assert len (mock_send_jwt .mock_calls ) == len (expected_calls )
643+ mock_send_jwt .assert_has_calls (expected_calls )
0 commit comments