|
6 | 6 | "net/http" |
7 | 7 | "net/url" |
8 | 8 | "testing" |
| 9 | + "time" |
9 | 10 |
|
10 | 11 | "github.com/pkg/errors" |
11 | 12 | "github.com/stretchr/testify/require" |
@@ -47,36 +48,76 @@ func TestSprintByteId(t *testing.T) { |
47 | 48 |
|
48 | 49 | func TestRetryPolicy(t *testing.T) { |
49 | 50 |
|
50 | | - t.Run("test handling url error", func(t *testing.T) { |
51 | | - urlErr := &url.Error{Err: errors.New("EOF")} |
| 51 | + t.Run("test handling error", func(t *testing.T) { |
52 | 52 | resp := &http.Response{} |
53 | 53 |
|
54 | | - urlErr.Err = x509.UnknownAuthorityError{} |
55 | | - b, err := RetryPolicy(context.Background(), resp, urlErr) |
56 | | - require.False(t, b) |
57 | | - require.Equal(t, urlErr, err) |
| 54 | + retryableErrors := []error{ |
| 55 | + &url.Error{Err: errors.New("EOF")}, |
| 56 | + &url.Error{Err: errors.New("other error")}, |
| 57 | + errors.New("non-url error")} |
58 | 58 |
|
| 59 | + nonRetryableErrors := []error{&url.Error{Err: x509.UnknownAuthorityError{}}} |
59 | 60 | errMsgs := []string{"stopped after 12 redirects", "unsupported protocol scheme", "certificate is not trusted"} |
60 | 61 | for _, msg := range errMsgs { |
61 | | - urlErr.Err = errors.New(msg) |
62 | | - b, err := RetryPolicy(context.Background(), resp, urlErr) |
| 62 | + nonRetryableErrors = append(nonRetryableErrors, &url.Error{Err: errors.New(msg)}) |
| 63 | + } |
| 64 | + |
| 65 | + // should never retry if context is cancelled/timed out, regardless of client method or error |
| 66 | + checkCtxCancelled := func(callingContext context.Context, err error) { |
| 67 | + cancelled, cancel := context.WithCancel(callingContext) |
| 68 | + cancel() |
| 69 | + timedOut, cancel2 := context.WithDeadline(callingContext, time.Now().Add(-10*time.Second)) |
| 70 | + defer cancel2() |
| 71 | + |
| 72 | + // should never retry if context is cancelled/timed out |
| 73 | + b, checkErr := RetryPolicy(cancelled, resp, err) |
| 74 | + require.False(t, b) |
| 75 | + require.Equal(t, cancelled.Err(), checkErr) |
| 76 | + b, checkErr = RetryPolicy(timedOut, resp, err) |
63 | 77 | require.False(t, b) |
64 | | - require.Equal(t, urlErr, err) |
| 78 | + require.Equal(t, timedOut.Err(), checkErr) |
65 | 79 | } |
66 | 80 |
|
67 | | - urlErr.Err = errors.New("other error") |
68 | | - b, err = RetryPolicy(context.Background(), resp, urlErr) |
69 | | - require.True(t, b) |
70 | | - require.Nil(t, err) |
| 81 | + for _, err := range retryableErrors { |
| 82 | + // Should retry only if client method is also retryable |
| 83 | + for cm := range _clientMethod_index { |
| 84 | + ctx := context.WithValue(context.Background(), ClientMethod, clientMethod(cm)) |
| 85 | + |
| 86 | + checkCtxCancelled(ctx, err) |
| 87 | + |
| 88 | + _, nonRetryableClientMethod := nonRetryableClientMethods[clientMethod(cm)] |
| 89 | + b, checkErr := RetryPolicy(ctx, resp, err) |
| 90 | + if nonRetryableClientMethod { |
| 91 | + require.False(t, b) |
| 92 | + require.Equal(t, err, checkErr) |
| 93 | + } else { |
| 94 | + require.True(t, b) |
| 95 | + require.Nil(t, checkErr) |
| 96 | + } |
| 97 | + } |
| 98 | + } |
71 | 99 |
|
72 | | - b, err = RetryPolicy(context.Background(), resp, errors.New("non-url error")) |
73 | | - require.True(t, b) |
74 | | - require.Nil(t, err) |
| 100 | + for _, err := range nonRetryableErrors { |
| 101 | + // Should non retry regardless of client method |
| 102 | + for cm := range _clientMethod_index { |
| 103 | + ctx := context.WithValue(context.Background(), ClientMethod, clientMethod(cm)) |
| 104 | + |
| 105 | + checkCtxCancelled(ctx, err) |
75 | 106 |
|
| 107 | + b, checkErr := RetryPolicy(ctx, resp, err) |
| 108 | + require.False(t, b) |
| 109 | + require.Equal(t, err, checkErr) |
| 110 | + } |
| 111 | + } |
76 | 112 | }) |
77 | 113 |
|
78 | 114 | t.Run("test handling status codes", func(t *testing.T) { |
79 | | - resp := &http.Response{} |
| 115 | + var resp *http.Response |
| 116 | + retry, err := RetryPolicy(context.Background(), resp, nil) |
| 117 | + require.False(t, retry) |
| 118 | + require.Nil(t, err) |
| 119 | + |
| 120 | + resp = &http.Response{} |
80 | 121 | // 429 nd 503 are always retryable |
81 | 122 | retryableCodes := []int{429, 503} |
82 | 123 | // maybe retryable codes: 0 and >= 500, but not 501 |
|
0 commit comments