@@ -896,6 +896,138 @@ func TestClient_DefaultBackoff(t *testing.T) {
896
896
}
897
897
}
898
898
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
+
899
1031
func TestClient_DefaultRetryPolicy_TLS (t * testing.T ) {
900
1032
ts := httptest .NewTLSServer (http .HandlerFunc (func (w http.ResponseWriter , r * http.Request ) {
901
1033
w .WriteHeader (200 )
0 commit comments