@@ -26,10 +26,85 @@ import (
26
26
"testing"
27
27
"time"
28
28
29
+ "github.com/Azure/go-autorest/autorest"
29
30
"github.com/Azure/go-autorest/autorest/mocks"
30
31
"github.com/stretchr/testify/assert"
31
32
)
32
33
34
+ func TestNewBackoff (t * testing.T ) {
35
+ expected := & Backoff {Duration : time .Second , Factor : 2 , Steps : 0 , Cap : 3 * time .Second , Jitter : 0.5 }
36
+ result := NewBackoff (time .Second , 2 , 0.5 , 0 , 3 * time .Second )
37
+ assert .Equal (t , expected , result )
38
+ }
39
+
40
+ func TestWithNonRetriableErrors (t * testing.T ) {
41
+ bo := & Backoff {Duration : time .Second , Factor : 2 , Steps : 0 , Cap : 3 * time .Second , Jitter : 0.5 }
42
+ errs := []string {"error1" , "error2" }
43
+ expected := bo
44
+ expected .NonRetriableErrors = errs
45
+ result := bo .WithNonRetriableErrors (errs )
46
+ assert .Equal (t , expected , result )
47
+ }
48
+
49
+ func TestWithRetriableHTTPStatusCodes (t * testing.T ) {
50
+ bo := & Backoff {Duration : time .Second , Factor : 2 , Steps : 0 , Cap : 3 * time .Second , Jitter : 0.5 }
51
+ httpStatusCodes := []int {http .StatusOK , http .StatusTooManyRequests }
52
+ expected := bo
53
+ expected .RetriableHTTPStatusCodes = httpStatusCodes
54
+ result := bo .WithRetriableHTTPStatusCodes (httpStatusCodes )
55
+ assert .Equal (t , expected , result )
56
+ }
57
+
58
+ func TestIsNonRetriableError (t * testing.T ) {
59
+ // false case
60
+ bo := & Backoff {Factor : 1.0 , Steps : 3 }
61
+ ret := bo .isNonRetriableError (nil )
62
+ assert .Equal (t , false , ret )
63
+
64
+ // true case
65
+ errs := []string {"error1" , "error2" }
66
+ bo2 := bo
67
+ bo2 .NonRetriableErrors = errs
68
+ rerr := & Error {
69
+ Retriable : false ,
70
+ HTTPStatusCode : 429 ,
71
+ RawError : fmt .Errorf ("error1" ),
72
+ }
73
+
74
+ ret = bo2 .isNonRetriableError (rerr )
75
+ assert .Equal (t , true , ret )
76
+ }
77
+
78
+ func TestJitterWithNegativeMaxFactor (t * testing.T ) {
79
+ // jitter := duration + time.Duration(rand.Float64()*maxFactor*float64(duration))
80
+ // If maxFactor is 0.0 or less than 0.0, a suggested default value will be chosen.
81
+ // rand.Float64() returns, as a float64, a pseudo-random number in [0.0,1.0).
82
+ duration := time .Duration (time .Second )
83
+ maxFactor := float64 (- 3.0 )
84
+ res := jitter (duration , maxFactor )
85
+ defaultMaxFactor := float64 (1.0 )
86
+ expected := jitter (duration , defaultMaxFactor )
87
+ assert .Equal (t , expected - res >= time .Duration (0.0 * float64 (duration )), true )
88
+ assert .Equal (t , expected - res < time .Duration (1.0 * float64 (duration )), true )
89
+ }
90
+
91
+ func TestDoExponentialBackoffRetry (t * testing.T ) {
92
+ client := mocks .NewSender ()
93
+ bo := & Backoff {Duration : time .Second , Factor : 2 , Steps : 0 , Cap : 3 * time .Second , Jitter : 0.5 }
94
+ sender := autorest .DecorateSender (
95
+ client ,
96
+ DoExponentialBackoffRetry (bo ),
97
+ )
98
+
99
+ req := & http.Request {
100
+ Method : "GET" ,
101
+ }
102
+
103
+ result , err := sender .Do (req )
104
+ assert .Nil (t , result )
105
+ assert .Nil (t , err )
106
+ }
107
+
33
108
func TestStep (t * testing.T ) {
34
109
tests := []struct {
35
110
initial * Backoff
@@ -97,6 +172,34 @@ func TestDoBackoffRetry(t *testing.T) {
97
172
assert .Equal (t , expectedErr .Error (), err )
98
173
assert .Equal (t , 3 , client .Attempts ())
99
174
175
+ // retries with 0 steps
176
+ respSteps0 , errSteps0 := doBackoffRetry (client , fakeRequest , & Backoff {Factor : 1.0 , Steps : 0 })
177
+ assert .Nil (t , respSteps0 )
178
+ assert .Nil (t , errSteps0 )
179
+
180
+ // backoff with NonRetriableErrors and RetriableHTTPStatusCodes
181
+ r = mocks .NewResponseWithStatus ("404 StatusNotFound" , http .StatusNotFound )
182
+ client = mocks .NewSender ()
183
+ client .AppendAndRepeatResponseWithDelay (r , time .Second , 1 )
184
+ client .AppendError (fmt .Errorf ("HTTP status code (404)" ))
185
+ bo := & Backoff {Factor : 1.0 , Steps : 3 }
186
+ bo .NonRetriableErrors = []string {"404 StatusNotFound" }
187
+ bo .RetriableHTTPStatusCodes = []int {http .StatusNotFound }
188
+ expectedResp := & http.Response {
189
+ Status : "200 OK" ,
190
+ StatusCode : 200 ,
191
+ Proto : "HTTP/1.0" ,
192
+ ProtoMajor : 1 ,
193
+ ProtoMinor : 0 ,
194
+ Body : mocks .NewBody ("" ),
195
+ Request : fakeRequest ,
196
+ }
197
+
198
+ resp , err = doBackoffRetry (client , fakeRequest , bo )
199
+ assert .Nil (t , err )
200
+ assert .Equal (t , 3 , client .Attempts ())
201
+ assert .Equal (t , expectedResp , resp )
202
+
100
203
// returns immediately on succeed
101
204
r = mocks .NewResponseWithStatus ("200 OK" , http .StatusOK )
102
205
client = mocks .NewSender ()
0 commit comments