forked from microsoftgraph/msgraph-sdk-javascript
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathRetryHandler.ts
More file actions
239 lines (213 loc) · 7.92 KB
/
Copy pathRetryHandler.ts
File metadata and controls
239 lines (213 loc) · 7.92 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
/**
* -------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the MIT License.
* See License in the project root for license information.
* -------------------------------------------------------------------------------------------
*/
import { assert } from "chai";
import "isomorphic-fetch";
import { Context } from "../../src/IContext";
import { FetchOptions } from "../../src/IFetchOptions";
import { MiddlewareControl } from "../../src/middleware/MiddlewareControl";
import { RetryHandlerOptions, ShouldRetry } from "../../src/middleware/options/RetryHandlerOptions";
import { RetryHandler } from "../../src/middleware/RetryHandler";
import { DummyHTTPMessageHandler } from "../DummyHTTPMessageHandler";
describe("RetryHandler.ts", function() {
this.timeout(20 * 1000);
const retryHandler = new RetryHandler();
const retryHandlerOptions = new RetryHandlerOptions();
const tooManyRequestsResponseWithRetryAfterDelay = new Response("", {
status: 429,
statusText: "TooManyRequests",
headers: {
"Retry-After": "10",
},
});
const tooManyRequestsResponseWithRetyAfterDate = new Response("", {
status: 429,
statusText: "TooManyRequests",
headers: {
"Retry-After": new Date(Date.now() + 10000).toUTCString(),
},
});
const serviceUnavailableResponse = new Response("", {
status: 503,
statusText: "ServiceUnavailable",
});
const gatewayTimeoutResponse = new Response("", {
status: 504,
statusText: "GatewayTimeout",
});
const nonRetryResponse = new Response("", {
status: 200,
statusText: "OK",
});
/* tslint:disable: no-string-literal */
describe("constructor", () => {
it("Should set the option member with retryHanderOptions", () => {
const handler = new RetryHandler(retryHandlerOptions);
assert.isDefined(handler["options"]);
});
it("Should create retryHandler instance with default retryHandlerOptions", () => {
const handler = new RetryHandler();
assert.isDefined(handler["options"]);
});
});
describe("isRetry", () => {
it("Should return true for 429 response", () => {
assert.isTrue(retryHandler["isRetry"](tooManyRequestsResponseWithRetryAfterDelay));
});
it("Should return true for 503 response", () => {
assert.isTrue(retryHandler["isRetry"](serviceUnavailableResponse));
});
it("Should return true for 504 response", () => {
assert.isTrue(retryHandler["isRetry"](gatewayTimeoutResponse));
});
it("Should return false for non retry response", () => {
assert.isFalse(retryHandler["isRetry"](nonRetryResponse));
});
});
describe("isBuffered", () => {
const url = "dummy_url";
it("Should succeed for non post, patch, put requests", () => {
const options: FetchOptions = {
method: "GET",
};
assert.isTrue(retryHandler["isBuffered"](url, options));
});
it("Should succeed for post request with non stream request", () => {
const options: FetchOptions = {
method: "POST",
body: "test",
};
assert.isTrue(retryHandler["isBuffered"](url, options));
});
it("Should fail for stream request", () => {
const options: FetchOptions = {
method: "PUT",
headers: {
"Content-Type": "application/octet-stream",
},
};
assert.isFalse(retryHandler["isBuffered"](url, options));
});
});
describe("getDelay", () => {
it("Should return retry delay from the response header", () => {
const delay = retryHandler["getDelay"](tooManyRequestsResponseWithRetryAfterDelay, 1, 5);
assert.equal(delay, 10);
});
it("Should return retry delay from the response header mentioning delay time", () => {
const delay = retryHandler["getDelay"](tooManyRequestsResponseWithRetyAfterDate, 1, 5);
assert.isDefined(delay);
});
it("Should return delay without exponential backoff", () => {
const delay = retryHandler["getDelay"](gatewayTimeoutResponse, 1, 10);
assert.isAbove(delay, 10);
assert.isBelow(delay, 11);
});
it("Should return delay with exponential backoff", () => {
const delay = retryHandler["getDelay"](gatewayTimeoutResponse, 2, 10);
assert.isAbove(delay, 12);
assert.isBelow(delay, 13);
});
it("Should return max delay for if the calculated delay is more", () => {
const delay = retryHandler["getDelay"](gatewayTimeoutResponse, 10, 100);
assert.isAbove(delay, 180);
assert.isBelow(delay, 181);
});
});
describe("getExponentialBackOffTime", () => {
it("Should return 0 delay for 0th attempt i.e for a fresh request", () => {
const time = retryHandler["getExponentialBackOffTime"](0);
assert.equal(time, 0);
});
it("Should return attempt time", () => {
const time = retryHandler["getExponentialBackOffTime"](1);
assert.equal(time, 1);
});
});
describe("sleep", async () => {
it("Should run the sleep method for 1 second", async () => {
try {
await retryHandler["sleep"](1);
} catch (error) {
throw error;
}
});
});
describe("getOptions", () => {
it("Should return the options in the context object", () => {
const delay = 10;
const maxRetries = 8;
const shouldRetry: ShouldRetry = () => false;
const options = new RetryHandlerOptions(delay, maxRetries, shouldRetry);
const cxt: Context = {
request: "url",
middlewareControl: new MiddlewareControl([options]),
};
const o = retryHandler["getOptions"](cxt);
assert.equal(o.delay, delay);
assert.equal(o.maxRetries, maxRetries);
assert.equal(o.shouldRetry, shouldRetry);
});
it("Should return the default set of options in the middleware", () => {
const cxt: Context = {
request: "url",
};
const o = retryHandler["getOptions"](cxt);
assert.equal(o.delay, retryHandler["options"].delay);
assert.equal(o.maxRetries, retryHandler["options"].maxRetries);
assert.equal(o.shouldRetry, retryHandler["options"].shouldRetry);
});
});
describe("executeWithRetry", async () => {
const options = new RetryHandlerOptions();
const handler = new RetryHandler(options);
const dummyHTTPHandler = new DummyHTTPMessageHandler();
handler.setNext(dummyHTTPHandler);
const cxt: Context = {
request: "url",
options: {
method: "GET",
},
};
it("Should return non retried response incase of maxRetries busted out", async () => {
dummyHTTPHandler.setResponses([new Response(null, { status: 429 }), new Response("ok", { status: 200 })]);
await handler["executeWithRetry"](cxt, RetryHandlerOptions["MAX_MAX_RETRIES"], options);
assert.equal(cxt.response.status, 429);
});
it("Should return succeeded response for non retry response", async () => {
dummyHTTPHandler.setResponses([new Response("ok", { status: 200 })]);
await handler["executeWithRetry"](cxt, 0, options);
assert.equal(cxt.response.status, 200);
});
it("Should return non retried response for streaming request", async () => {
const c: Context = {
request: "url",
options: {
method: "POST",
headers: {
"Content-Type": "application/octet-stream",
},
},
};
dummyHTTPHandler.setResponses([new Response(null, { status: 429 }), new Response("ok", { status: 200 })]);
await handler["executeWithRetry"](c, 0, options);
assert.equal(c.response.status, 429);
});
it("Should successfully retry and return ok response", async () => {
const opts = new RetryHandlerOptions(1);
dummyHTTPHandler.setResponses([new Response(null, { status: 429 }), new Response(null, { status: 429 }), new Response("ok", { status: 200 })]);
await handler["executeWithRetry"](cxt, 0, options);
assert.equal(cxt.response.status, 200);
});
it("Should fail by exceeding max retries", async () => {
const opts = new RetryHandlerOptions(1, 2);
dummyHTTPHandler.setResponses([new Response(null, { status: 429 }), new Response(null, { status: 429 }), new Response(null, { status: 429 }), new Response("ok", { status: 200 })]);
await handler["executeWithRetry"](cxt, 0, opts);
assert.equal(cxt.response.status, 429);
});
});
/* tslint:enable: no-string-literal */
});