@@ -315,10 +315,11 @@ module VCAP::CloudController
315315 context 'when UAA is unavailable' do
316316 before do
317317 allow ( uaa_client ) . to receive ( :token_info ) . and_raise ( UaaUnavailable )
318+ allow ( subject ) . to receive ( :sleep ) { |n | Timecop . travel ( n ) }
318319 end
319320
320- it 'returns an empty hash ' do
321- expect ( uaa_client . users_for_ids ( [ userid_1 ] ) ) . to eq ( { } )
321+ it 'raises an exception ' do
322+ expect { uaa_client . users_for_ids ( [ userid_1 ] ) } . to raise_error ( UaaUnavailable )
322323 end
323324 end
324325
@@ -378,21 +379,23 @@ module VCAP::CloudController
378379
379380 context 'when the endpoint returns an error' do
380381 let ( :uaa_error ) { CF ::UAA ::UAAError . new ( 'some error' ) }
381- let ( :mock_logger ) { double ( :steno_logger , error : nil ) }
382+ let ( :mock_logger ) { double ( :steno_logger , error : nil , info : nil ) }
382383
383384 before do
384385 scim = instance_double ( CF ::UAA ::Scim )
385386 allow ( scim ) . to receive ( :query ) . and_raise ( uaa_error )
386387 allow ( uaa_client ) . to receive_messages ( scim : scim , logger : mock_logger )
388+ allow ( subject ) . to receive ( :sleep ) { |n | Timecop . travel ( n ) }
387389 end
388390
389- it 'returns an empty hash ' do
390- expect ( uaa_client . users_for_ids ( [ userid_1 ] ) ) . to eq ( { } )
391+ it 'raises an exception ' do
392+ expect { uaa_client . users_for_ids ( [ userid_1 ] ) } . to raise_error ( UaaUnavailable )
391393 end
392394
393- it 'logs the error' do
394- uaa_client . users_for_ids ( [ userid_1 ] )
395- expect ( mock_logger ) . to have_received ( :error ) . with ( "Failed to retrieve users from UAA: #{ uaa_error . inspect } " )
395+ it 'retries, raises an exception after 17 attempts' do
396+ expect { uaa_client . users_for_ids ( [ userid_1 ] ) } . to raise_error ( UaaUnavailable )
397+ expect ( uaa_client ) . to have_received ( :scim ) . exactly ( 17 ) . times
398+ expect ( uaa_client ) . to have_received ( :sleep ) . exactly ( 16 ) . times
396399 end
397400 end
398401
@@ -439,8 +442,9 @@ module VCAP::CloudController
439442 context 'when token is invalid or expired twice' do
440443 let ( :auth_header ) { 'bearer invalid' }
441444
442- it 'retries once and then returns no usernames' do
443- expect ( uaa_client . users_for_ids ( [ userid_1 , userid_2 ] ) ) . to eq ( { } )
445+ it 'fails immediately without retries' do
446+ expect { uaa_client . users_for_ids ( [ userid_1 , userid_2 ] ) } . to raise_error ( CF ::UAA ::InvalidToken )
447+ expect ( uaa_client ) . not_to receive ( :sleep )
444448 end
445449 end
446450 end
@@ -548,6 +552,7 @@ module VCAP::CloudController
548552 let ( :username1 ) { 'user1@example.com' }
549553 let ( :username2 ) { 'user2@example.com' }
550554 let ( :partial_username ) { 'user' }
555+ let ( :mock_logger ) { double ( :steno_logger , error : nil , info : nil ) }
551556
552557 context 'with usernames but no origins' do
553558 it 'returns the ids for the usernames' do
@@ -675,13 +680,14 @@ module VCAP::CloudController
675680
676681 context 'when UAA is unavailable' do
677682 before do
678- allow ( uaa_client ) . to receive ( :token_info ) . and_raise ( UaaUnavailable )
683+ allow ( uaa_client ) . to receive ( :query ) . and_raise ( UaaUnavailable )
684+ allow ( uaa_client ) . to receive ( :sleep ) { |n | Timecop . travel ( n ) }
679685 end
680686
681- it 'raises UaaUnavailable ' do
682- expect do
683- uaa_client . ids_for_usernames_and_origins ( [ username1 ] , nil )
684- end . to raise_error ( UaaUnavailable )
687+ it 'retries, raises an exception after 17 attempts ' do
688+ expect { uaa_client . ids_for_usernames_and_origins ( [ username1 ] , nil ) } . to raise_error ( UaaUnavailable )
689+ expect ( uaa_client ) . to have_received ( :query ) . exactly ( 17 ) . times
690+ expect ( uaa_client ) . to have_received ( :sleep ) . exactly ( 16 ) . times
685691 end
686692 end
687693
@@ -690,12 +696,13 @@ module VCAP::CloudController
690696 scim = double ( 'scim' )
691697 allow ( scim ) . to receive ( :query ) . and_raise ( CF ::UAA ::TargetError )
692698 allow ( uaa_client ) . to receive ( :scim ) . and_return ( scim )
699+ allow ( subject ) . to receive ( :sleep ) { |n | Timecop . travel ( n ) }
693700 end
694701
695- it 'raises UaaUnavailable ' do
696- expect do
697- uaa_client . ids_for_usernames_and_origins ( [ username1 ] , nil )
698- end . to raise_error ( UaaUnavailable )
702+ it 'retries, raises an exception after 17 attempts ' do
703+ expect { uaa_client . ids_for_usernames_and_origins ( [ username1 ] , nil ) } . to raise_error ( UaaUnavailable )
704+ expect ( uaa_client ) . to have_received ( :scim ) . exactly ( 17 ) . times
705+ expect ( uaa_client ) . to have_received ( :sleep ) . exactly ( 16 ) . times
699706 end
700707 end
701708
@@ -704,12 +711,13 @@ module VCAP::CloudController
704711 scim = double ( 'scim' )
705712 allow ( scim ) . to receive ( :query ) . and_raise ( CF ::UAA ::BadTarget )
706713 allow ( uaa_client ) . to receive ( :scim ) . and_return ( scim )
714+ allow ( subject ) . to receive ( :sleep ) { |n | Timecop . travel ( n ) }
707715 end
708716
709- it 'raises UaaUnavailable ' do
710- expect do
711- uaa_client . ids_for_usernames_and_origins ( [ username1 ] , nil )
712- end . to raise_error ( UaaUnavailable )
717+ it 'retries, raises an exception after 17 attempts ' do
718+ expect { uaa_client . ids_for_usernames_and_origins ( [ username1 ] , nil ) } . to raise_error ( UaaUnavailable )
719+ expect ( uaa_client ) . to have_received ( :scim ) . exactly ( 17 ) . times
720+ expect ( uaa_client ) . to have_received ( :sleep ) . exactly ( 16 ) . times
713721 end
714722 end
715723 end
@@ -814,5 +822,81 @@ module VCAP::CloudController
814822 end
815823 end
816824 end
825+
826+ describe '#with_request_error_handling' do
827+ before do
828+ allow ( subject ) . to receive ( :sleep ) { |n | Timecop . travel ( n ) }
829+ end
830+
831+ context 'when the block succeeds immediately' do
832+ it 'does not sleep or raise an exception' do
833+ expect { uaa_client . with_request_error_handling { } } . not_to raise_error
834+ end
835+ end
836+
837+ context 'when the block raises an exception' do
838+ let ( :successful_block ) do
839+ proc {
840+ @count ||= 0
841+ @count += 1
842+ @count == 2 ? true : raise ( UaaUnavailable )
843+ }
844+ end
845+
846+ it 'retries once and eventually succeeds' do
847+ expect { subject . with_request_error_handling ( &successful_block ) } . not_to raise_error
848+ end
849+
850+ it 'fails immediately if invalidToken exception has been thrown' do
851+ expect { subject . with_request_error_handling { raise CF ::UAA ::InvalidToken } } . to raise_error ( CF ::UAA ::InvalidToken )
852+ expect ( uaa_client ) . not_to receive ( :sleep )
853+ end
854+
855+ it 'retries and eventually raises an error when the block fails' do
856+ attempts = 0
857+
858+ expect do
859+ subject . with_request_error_handling do
860+ attempts += 1
861+ Timecop . travel ( Time . now . utc + 50 )
862+ raise UaaUnavailable
863+ end
864+ end . to raise_error ( UaaUnavailable )
865+ expect ( uaa_client ) . to have_received ( :sleep ) . exactly ( 1 ) . times
866+ expect ( attempts ) . to eq ( 2 )
867+ end
868+
869+ it 'stops retrying after 60 seconds and raises an exception' do
870+ start_time = Time . now . utc
871+
872+ Timecop . freeze do
873+ expect do
874+ subject . with_request_error_handling do
875+ Timecop . travel ( start_time + 61 )
876+ raise UaaUnavailable
877+ end
878+ end . to raise_error ( UaaUnavailable )
879+ end
880+ expect ( uaa_client ) . not_to receive ( :sleep )
881+ end
882+
883+ it 'raises an error after 17 attempts in approximately 1 minute when each yield call immediately' do
884+ attempts = 0
885+ start_time = Time . now . utc
886+
887+ expect do
888+ subject . with_request_error_handling do
889+ attempts += 1
890+ raise UaaUnavailable
891+ end
892+ end . to raise_error ( UaaUnavailable )
893+ end_time = Time . now . utc
894+ duration = end_time . to_f - start_time . to_f
895+ expect ( attempts ) . to be_within ( 1 ) . of ( 17 )
896+ expect ( duration ) . to be_within ( 1 ) . of ( 62 )
897+ expect ( uaa_client ) . to have_received ( :sleep ) . exactly ( 16 ) . times
898+ end
899+ end
900+ end
817901 end
818902end
0 commit comments