@@ -251,7 +251,7 @@ func testClientDo(t *testing.T, body interface{}) {
251251	}
252252
253253	if  resp .StatusCode  !=  200  {
254- 		t .Fatalf ("exected  200, got: %d" , resp .StatusCode )
254+ 		t .Fatalf ("expected  200, got: %d" , resp .StatusCode )
255255	}
256256
257257	if  retryCount  <  0  {
@@ -896,6 +896,156 @@ 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  :=  & http.Response {
904+ 			StatusCode : retriableStatusCode ,
905+ 			Header : http.Header {
906+ 				"Content-Type" : []string {"application/json" },
907+ 				"Retry-After" :  []string {"42" },
908+ 			},
909+ 		}
910+ 		backoff  :=  ExponentialJitterBackoff (retryWaitMin , retryWaitMax , 3 , response )
911+ 		expectedBackoff  :=  42  *  time .Second 
912+ 
913+ 		if  backoff  !=  expectedBackoff  {
914+ 			t .Fatalf ("expected default backoff from Retry-After header (%s), got %s" , expectedBackoff , backoff )
915+ 		}
916+ 	})
917+ 
918+ 	invalidRetryAfterHeaderCases  :=  []struct  {
919+ 		name          string 
920+ 		makeResponse  func () * http.Response 
921+ 	}{
922+ 		{
923+ 			name : "with empty first value of Retry-After header in response" ,
924+ 			makeResponse : func () * http.Response  {
925+ 				return  & http.Response {
926+ 					StatusCode : retriableStatusCode ,
927+ 					Header : http.Header {
928+ 						"Content-Type" : []string {"application/json" },
929+ 						"Retry-After" :  []string {"" },
930+ 					},
931+ 				}
932+ 			},
933+ 		},
934+ 		{
935+ 			name : "without Retry-After header in response" ,
936+ 			makeResponse : func () * http.Response  {
937+ 				return  & http.Response {
938+ 					StatusCode : retriableStatusCode ,
939+ 					Header :     http.Header {"Content-Type" : []string {"application/json" }},
940+ 				}
941+ 			},
942+ 		},
943+ 		{
944+ 			name : "with nil response" ,
945+ 			makeResponse : func () * http.Response  {
946+ 				return  nil 
947+ 			},
948+ 		},
949+ 	}
950+ 
951+ 	for  _ , irahc  :=  range  invalidRetryAfterHeaderCases  {
952+ 		t .Run (irahc .name , func (t  * testing.T ) {
953+ 			attemptNumCases  :=  []struct  {
954+ 				name                          string 
955+ 				attemptNum                    int 
956+ 				expectedBackoffWithoutJitter  time.Duration 
957+ 			}{
958+ 				{
959+ 					name :                         "with first attempt" ,
960+ 					attemptNum :                   0 ,
961+ 					expectedBackoffWithoutJitter : retryWaitMin ,
962+ 				},
963+ 				{
964+ 					name :                         "with low attempt number" ,
965+ 					attemptNum :                   3 ,
966+ 					expectedBackoffWithoutJitter : 16  *  time .Second ,
967+ 				},
968+ 				{
969+ 					name :                         "with high attempt number" ,
970+ 					attemptNum :                   10 ,
971+ 					expectedBackoffWithoutJitter : retryWaitMax ,
972+ 				},
973+ 			}
974+ 
975+ 			for  _ , anc  :=  range  attemptNumCases  {
976+ 				t .Run (anc .name , func (t  * testing.T ) {
977+ 					backoff  :=  ExponentialJitterBackoff (defaultRetryWaitMin , defaultRetryWaitMax , anc .attemptNum , irahc .makeResponse ())
978+ 					expectedJitterDelta  :=  float64 (anc .expectedBackoffWithoutJitter ) *  0.25 
979+ 					expectedMinTime  :=  anc .expectedBackoffWithoutJitter  -  time .Duration (expectedJitterDelta )
980+ 					expectedMaxTime  :=  anc .expectedBackoffWithoutJitter  +  time .Duration (expectedJitterDelta )
981+ 					expectedBackoffLowerLimit  :=  max (expectedMinTime , retryWaitMin )
982+ 					expectedBackoffUpperLimit  :=  min (expectedMaxTime , retryWaitMax )
983+ 
984+ 					t .Run ("returns exponential backoff with jitter, clamped within min and max limits" , func (t  * testing.T ) {
985+ 						if  backoff  <  expectedBackoffLowerLimit  ||  backoff  >  expectedBackoffUpperLimit  {
986+ 							t .Fatalf ("expected backoff to be within range [%s, %s], got %s" , expectedBackoffLowerLimit , expectedBackoffUpperLimit , backoff )
987+ 						}
988+ 					})
989+ 				})
990+ 			}
991+ 		})
992+ 	}
993+ }
994+ 
995+ func  Test_clampDuration (t  * testing.T ) {
996+ 	const  (
997+ 		minDuration  time.Duration  =  500  *  time .Millisecond 
998+ 		maxDuration  time.Duration  =  10  *  time .Minute 
999+ 	)
1000+ 
1001+ 	testCases  :=  []struct  {
1002+ 		name                     string 
1003+ 		errorMessage             string 
1004+ 		duration                 time.Duration 
1005+ 		expectedClampedDuration  time.Duration 
1006+ 	}{
1007+ 		{
1008+ 			name :                    "with duration below min value" ,
1009+ 			errorMessage :            "should return the min value" ,
1010+ 			duration :                60  *  time .Microsecond ,
1011+ 			expectedClampedDuration : minDuration ,
1012+ 		},
1013+ 		{
1014+ 			name :                    "with duration equal to min value" ,
1015+ 			errorMessage :            "should return the min value" ,
1016+ 			duration :                minDuration ,
1017+ 			expectedClampedDuration : minDuration ,
1018+ 		},
1019+ 		{
1020+ 			name :                    "with duration strictly within min and max range" ,
1021+ 			errorMessage :            "should return the given value" ,
1022+ 			duration :                45  *  time .Second ,
1023+ 			expectedClampedDuration : 45  *  time .Second ,
1024+ 		},
1025+ 		{
1026+ 			name :                    "with duration equal to max value" ,
1027+ 			errorMessage :            "should return the max value" ,
1028+ 			duration :                maxDuration ,
1029+ 			expectedClampedDuration : maxDuration ,
1030+ 		},
1031+ 		{
1032+ 			name :                    "with duration above max value" ,
1033+ 			errorMessage :            "should return the max value" ,
1034+ 			duration :                2  *  time .Hour ,
1035+ 			expectedClampedDuration : maxDuration ,
1036+ 		},
1037+ 	}
1038+ 
1039+ 	for  _ , tc  :=  range  testCases  {
1040+ 		t .Run (tc .name , func (t  * testing.T ) {
1041+ 			duration  :=  clampDuration (tc .duration , minDuration , maxDuration )
1042+ 			if  duration  !=  tc .expectedClampedDuration  {
1043+ 				t .Fatalf ("expected duration %s, got %s" , expectedBackoff , backoff )
1044+ 			}
1045+ 		})
1046+ 	}
1047+ }
1048+ 
8991049func  TestClient_DefaultRetryPolicy_TLS (t  * testing.T ) {
9001050	ts  :=  httptest .NewTLSServer (http .HandlerFunc (func (w  http.ResponseWriter , r  * http.Request ) {
9011051		w .WriteHeader (200 )
0 commit comments