@@ -896,6 +896,138 @@ func TestClient_DefaultBackoff(t *testing.T) {
896896 }
897897}
898898
899+ func TestClient_ExponentialJitterBackoff (t * testing.T ) {
900+ const retriableStatusCode int = http .StatusServiceUnavailable
901+
902+ t .Run ("with non-empty first value of Retry-After header in response" , func (t * testing.T ) {
903+ response := testharness .FakeHTTPResponse (retriableStatusCode , nil )
904+ response .Header .Add ("Retry-After" , "42" )
905+ backoff := ExponentialJitterBackoff (retryWaitMin , retryWaitMax , 3 , response )
906+
907+ t .Run ("returns default backoff" , func (t * testing.T ) {
908+ assert .Equal (t , 42 * time .Second , backoff )
909+ })
910+ })
911+
912+ invalidRetryAfterHeaderCases := []struct {
913+ name string
914+ makeResponse func () * http.Response
915+ }{
916+ {
917+ name : "with empty first value of Retry-After header in response" ,
918+ makeResponse : func () * http.Response {
919+ response := testharness .FakeHTTPResponse (retriableStatusCode , nil )
920+ response .Header .Set ("Retry-After" , "" )
921+ return response
922+ },
923+ },
924+ {
925+ name : "without Retry-After header in response" ,
926+ makeResponse : func () * http.Response {
927+ return testharness .FakeHTTPResponse (retriableStatusCode , nil )
928+ },
929+ },
930+ {
931+ name : "with nil response" ,
932+ makeResponse : func () * http.Response {
933+ return nil
934+ },
935+ },
936+ }
937+
938+ for _ , irahc := range invalidRetryAfterHeaderCases {
939+ t .Run (irahc .name , func (t * testing.T ) {
940+ attemptNumCases := []struct {
941+ name string
942+ attemptNum int
943+ expectedBackoffWithoutJitter time.Duration
944+ }{
945+ {
946+ name : "with first attempt" ,
947+ attemptNum : 0 ,
948+ expectedBackoffWithoutJitter : retryWaitMin ,
949+ },
950+ {
951+ name : "with low attempt number" ,
952+ attemptNum : 3 ,
953+ expectedBackoffWithoutJitter : 16 * time .Second ,
954+ },
955+ {
956+ name : "with high attempt number" ,
957+ attemptNum : 10 ,
958+ expectedBackoffWithoutJitter : retryWaitMax ,
959+ },
960+ }
961+
962+ for _ , anc := range attemptNumCases {
963+ t .Run (anc .name , func (t * testing.T ) {
964+ backoff := ExponentialJitterBackoff (defaultRetryWaitMin , defaultRetryWaitMax , anc .attemptNum , irahc .makeResponse ())
965+ expectedJitterDelta := float64 (anc .expectedBackoffWithoutJitter ) * 0.25
966+ expectedMinTime := anc .expectedBackoffWithoutJitter - time .Duration (expectedJitterDelta )
967+ expectedMaxTime := anc .expectedBackoffWithoutJitter + time .Duration (expectedJitterDelta )
968+
969+ t .Run ("returns exponential backoff with jitter, clamped within min and max limits" , func (t * testing.T ) {
970+ assert .GreaterOrEqual (t , backoff , max (expectedMinTime , retryWaitMin ))
971+ assert .LessOrEqual (t , backoff , min (expectedMaxTime , retryWaitMax ))
972+ })
973+ })
974+ }
975+ })
976+ }
977+ }
978+
979+ func Test_clampDuration (t * testing.T ) {
980+ const (
981+ minDuration time.Duration = 500 * time .Millisecond
982+ maxDuration time.Duration = 10 * time .Minute
983+ )
984+
985+ testCases := []struct {
986+ name string
987+ errorMessage string
988+ duration time.Duration
989+ expectedClampedDuration time.Duration
990+ }{
991+ {
992+ name : "with duration below min value" ,
993+ errorMessage : "should return the min value" ,
994+ duration : 60 * time .Microsecond ,
995+ expectedClampedDuration : minDuration ,
996+ },
997+ {
998+ name : "with duration equal to min value" ,
999+ errorMessage : "should return the min value" ,
1000+ duration : minDuration ,
1001+ expectedClampedDuration : minDuration ,
1002+ },
1003+ {
1004+ name : "with duration strictly within min and max range" ,
1005+ errorMessage : "should return the given value" ,
1006+ duration : 45 * time .Second ,
1007+ expectedClampedDuration : 45 * time .Second ,
1008+ },
1009+ {
1010+ name : "with duration equal to max value" ,
1011+ errorMessage : "should return the max value" ,
1012+ duration : maxDuration ,
1013+ expectedClampedDuration : maxDuration ,
1014+ },
1015+ {
1016+ name : "with duration above max value" ,
1017+ errorMessage : "should return the max value" ,
1018+ duration : 2 * time .Hour ,
1019+ expectedClampedDuration : maxDuration ,
1020+ },
1021+ }
1022+
1023+ for _ , tc := range testCases {
1024+ t .Run (tc .name , func (t * testing.T ) {
1025+ duration := clampDuration (tc .duration , minDuration , maxDuration )
1026+ assert .Equal (t , tc .expectedClampedDuration , duration , tc .errorMessage )
1027+ })
1028+ }
1029+ }
1030+
8991031func TestClient_DefaultRetryPolicy_TLS (t * testing.T ) {
9001032 ts := httptest .NewTLSServer (http .HandlerFunc (func (w http.ResponseWriter , r * http.Request ) {
9011033 w .WriteHeader (200 )
0 commit comments