1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package bigquery
16
17 import (
18 "errors"
19 "io"
20 "net/http"
21 "net/url"
22 "testing"
23
24 "golang.org/x/xerrors"
25 "google.golang.org/api/googleapi"
26 )
27
28 func TestRetryableErrors(t *testing.T) {
29 testCases := []struct {
30 description string
31 in error
32 useJobRetryReasons bool
33 wantRetry bool
34 }{
35 {
36 description: "nil error",
37 in: nil,
38 wantRetry: false,
39 },
40 {
41 description: "http stream closed",
42 in: errors.New("http2: stream closed"),
43 wantRetry: true,
44 },
45 {
46 description: "io ErrUnexpectedEOF",
47 in: io.ErrUnexpectedEOF,
48 wantRetry: true,
49 },
50 {
51 description: "unavailable",
52 in: &googleapi.Error{
53 Code: http.StatusServiceUnavailable,
54 Message: "foo",
55 },
56 wantRetry: true,
57 },
58 {
59 description: "url connection error",
60 in: &url.Error{Op: "blah", URL: "blah", Err: errors.New("connection refused")},
61 wantRetry: true,
62 },
63 {
64 description: "url other error",
65 in: &url.Error{Op: "blah", URL: "blah", Err: errors.New("blah")},
66 wantRetry: false,
67 },
68 {
69 description: "wrapped retryable",
70 in: xerrors.Errorf("test of wrapped retryable: %w", &googleapi.Error{
71 Code: http.StatusServiceUnavailable,
72 Message: "foo",
73 Errors: []googleapi.ErrorItem{
74 {Reason: "backendError", Message: "foo"},
75 },
76 }),
77 wantRetry: true,
78 },
79 {
80 description: "wrapped non-retryable",
81 in: xerrors.Errorf("test of wrapped retryable: %w", errors.New("blah")),
82 wantRetry: false,
83 },
84 {
85
86 description: "internal error",
87 in: &googleapi.Error{
88 Code: http.StatusInternalServerError,
89 },
90 wantRetry: true,
91 },
92 {
93 description: "internal w/backend reason",
94 in: &googleapi.Error{
95 Code: http.StatusServiceUnavailable,
96 Message: "foo",
97 Errors: []googleapi.ErrorItem{
98 {Reason: "backendError", Message: "foo"},
99 },
100 },
101 wantRetry: true,
102 },
103 {
104 description: "internal w/rateLimitExceeded reason",
105 in: &googleapi.Error{
106 Code: http.StatusServiceUnavailable,
107 Message: "foo",
108 Errors: []googleapi.ErrorItem{
109 {Reason: "rateLimitExceeded", Message: "foo"},
110 },
111 },
112 wantRetry: true,
113 },
114 {
115 description: "bad gateway error",
116 in: &googleapi.Error{
117 Code: http.StatusBadGateway,
118 Message: "foo",
119 },
120 wantRetry: true,
121 },
122 {
123 description: "jobRateLimitExceeded default",
124 in: &googleapi.Error{
125 Code: http.StatusOK,
126 Message: "foo",
127 Errors: []googleapi.ErrorItem{
128 {Reason: "jobRateLimitExceeded", Message: "foo"},
129 },
130 },
131 wantRetry: false,
132 },
133 {
134 description: "jobRateLimitExceeded job",
135 in: &googleapi.Error{
136 Code: http.StatusOK,
137 Message: "foo",
138 Errors: []googleapi.ErrorItem{
139 {Reason: "jobRateLimitExceeded", Message: "foo"},
140 },
141 },
142 useJobRetryReasons: true,
143 wantRetry: true,
144 },
145 {
146 description: "structured internal error default",
147 in: &googleapi.Error{
148 Code: http.StatusOK,
149 Message: "foo",
150 Errors: []googleapi.ErrorItem{
151 {Reason: "internalError", Message: "foo"},
152 },
153 },
154 wantRetry: false,
155 },
156 {
157 description: "structured internal error default",
158 in: &googleapi.Error{
159 Code: http.StatusOK,
160 Message: "foo",
161 Errors: []googleapi.ErrorItem{
162 {Reason: "internalError", Message: "foo"},
163 },
164 },
165 useJobRetryReasons: true,
166 wantRetry: true,
167 },
168 }
169
170 for _, testcase := range testCases {
171 tc := testcase
172 t.Run(tc.description, func(t *testing.T) {
173 t.Parallel()
174 reasons := defaultRetryReasons
175 if tc.useJobRetryReasons {
176 reasons = jobRetryReasons
177 }
178 got := retryableError(tc.in, reasons)
179 if got != tc.wantRetry {
180 t.Errorf("case (%s) mismatch: got %t wantRetry %t", tc.description, got, tc.wantRetry)
181 }
182 })
183 }
184 }
185
View as plain text