fetchbasequery 0.2.1 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -1
- package/README.md +262 -36
- package/jsconfig.json +12 -0
- package/package.json +26 -6
- package/src/decorators/index.d.ts +10 -0
- package/src/decorators/index.js +1 -0
- package/src/decorators/json.js +107 -0
- package/src/fetchBaseQuery.js +101 -14
- package/src/index.d.ts +19 -0
- package/src/index.js +4 -0
- package/src/middlewares/index.d.ts +14 -0
- package/src/middlewares/index.js +3 -4
- package/src/middlewares/muteErrors.js +17 -0
- package/src/middlewares/retryOn429.js +37 -0
- package/src/middlewares/retryOnError.js +32 -0
- package/src/utils/sleep.js +17 -0
- package/tests/index.html +7 -0
- package/tests/index.js +546 -0
- package/src/baseQuery.js +0 -80
- package/src/helpers/abortable.js +0 -13
- package/src/helpers/encodeJson.js +0 -8
- package/src/helpers/mergeHeaders.js +0 -9
- package/src/helpers/pipe.js +0 -1
- package/src/helpers/purge.js +0 -16
- package/src/helpers/responseFromError.js +0 -5
- package/src/middlewares/accessToken.js +0 -62
- package/src/middlewares/commonReply.js +0 -23
- package/src/middlewares/encodeBody.js +0 -85
- package/src/middlewares/jsonReply.js +0 -35
- package/types/index.d.ts +0 -64
package/src/middlewares/index.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
export {
|
|
2
|
-
export {
|
|
3
|
-
export {
|
|
4
|
-
export { default as jsonReply } from './jsonReply.js'
|
|
1
|
+
export { muteErrors } from './muteErrors.js'
|
|
2
|
+
export { retryOnError } from './retryOnError.js'
|
|
3
|
+
export { retryOn429 } from './retryOn429.js'
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @typedef {import('../index.d.ts').Middleware} Middleware
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
*
|
|
7
|
+
* @returns {Middleware}
|
|
8
|
+
*/
|
|
9
|
+
export function muteErrors() {
|
|
10
|
+
return async (request, next) => {
|
|
11
|
+
try {
|
|
12
|
+
return await next(request)
|
|
13
|
+
} catch (e) {
|
|
14
|
+
return Response.error()
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { sleep } from '../utils/sleep.js'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @typedef {import('./index.d.ts').RetryOn429Options} RetryOn429Options
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @typedef {import('../index.d.ts').Middleware} Middleware
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
*
|
|
13
|
+
* @param {RetryOn429Options} [options = {}]
|
|
14
|
+
* @returns {Middleware}
|
|
15
|
+
*/
|
|
16
|
+
export function retryOn429({
|
|
17
|
+
maxNumAttempts = 3,
|
|
18
|
+
maxBackoffTime = Infinity,
|
|
19
|
+
backoffTime = (attempt, response) => {
|
|
20
|
+
const delayFromHeader = Number(response.headers.get('retry-after'))
|
|
21
|
+
return isNaN(delayFromHeader) ? attempt * 2000 : delayFromHeader * 1000
|
|
22
|
+
}
|
|
23
|
+
} = {}) {
|
|
24
|
+
return async (request, next) => {
|
|
25
|
+
let response
|
|
26
|
+
for (let attempt = 1; attempt <= maxNumAttempts && !request.signal.aborted; attempt ++) {
|
|
27
|
+
response = await next(request)
|
|
28
|
+
if (response.status !== 429)
|
|
29
|
+
break
|
|
30
|
+
const delay = backoffTime(attempt, response)
|
|
31
|
+
if (delay > maxBackoffTime)
|
|
32
|
+
break
|
|
33
|
+
await sleep(delay, request.signal)
|
|
34
|
+
}
|
|
35
|
+
return response || Response.error()
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { sleep } from '../utils/sleep.js'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @typedef {import('./index.d.ts').RetryOnErrorOptions} RetryOnErrorOptions
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @typedef {import('../index.d.ts').Middleware} Middleware
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
*
|
|
13
|
+
* @param {RetryOnErrorOptions} [options = {}]
|
|
14
|
+
* @returns {Middleware}
|
|
15
|
+
*/
|
|
16
|
+
export function retryOnError({
|
|
17
|
+
maxNumAttempts = 3,
|
|
18
|
+
backoffTime = attempt => attempt * 2000
|
|
19
|
+
} = {}) {
|
|
20
|
+
return async (request, next) => {
|
|
21
|
+
let lastError
|
|
22
|
+
for (let attempt = 1; attempt <= maxNumAttempts && !request.signal.aborted; attempt ++) {
|
|
23
|
+
try {
|
|
24
|
+
return await next(request)
|
|
25
|
+
} catch (e) {
|
|
26
|
+
lastError = e
|
|
27
|
+
await sleep(backoffTime(attempt), request.signal)
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
throw lastError
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @param {number} delay Delay in milliseconds
|
|
3
|
+
* @param {AbortSignal} [signal=undefined]
|
|
4
|
+
* @returns
|
|
5
|
+
*/
|
|
6
|
+
export function sleep(delay, signal = undefined) {
|
|
7
|
+
return new Promise((resolve, reject) => { // abortable sleep
|
|
8
|
+
const id = setTimeout(resolve, delay)
|
|
9
|
+
if (signal instanceof AbortSignal) {
|
|
10
|
+
const abortSignal = AbortSignal.any([ signal ])
|
|
11
|
+
abortSignal.onabort = () => {
|
|
12
|
+
clearInterval(id)
|
|
13
|
+
reject(abortSignal.reason)
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
})
|
|
17
|
+
}
|
package/tests/index.html
ADDED
package/tests/index.js
ADDED
|
@@ -0,0 +1,546 @@
|
|
|
1
|
+
import { fetchBaseQuery } from "../src/fetchBaseQuery.js"
|
|
2
|
+
import { muteErrors } from "../src/middlewares/muteErrors.js"
|
|
3
|
+
import { retryOnError } from "../src/middlewares/retryOnError.js"
|
|
4
|
+
import { retryOn429 } from "../src/middlewares/retryOn429.js"
|
|
5
|
+
import { json } from "../src/decorators/json.js"
|
|
6
|
+
import { sleep } from "../src/utils/sleep.js"
|
|
7
|
+
|
|
8
|
+
const createFakeFetch = () => {
|
|
9
|
+
|
|
10
|
+
const session = {
|
|
11
|
+
attempts: 0,
|
|
12
|
+
counter: 1
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const connectionError = () => { throw new Error('Failed to load resource: net::ERR_CONNECTION_TIMED_OUT') }
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
*
|
|
19
|
+
* @param {string | Request} resource
|
|
20
|
+
* @param {RequestInit} [options = {}]
|
|
21
|
+
* @returns {Promise<Response>}
|
|
22
|
+
*/
|
|
23
|
+
return async (resource, options = {}) => {
|
|
24
|
+
const request = new Request(resource, options)
|
|
25
|
+
const url = new URL(request.url)
|
|
26
|
+
if (url.origin !== 'https://test.local')
|
|
27
|
+
connectionError()
|
|
28
|
+
switch (url.pathname) {
|
|
29
|
+
case '/counter': {
|
|
30
|
+
switch (request.method.toLowerCase()) {
|
|
31
|
+
case 'get': {
|
|
32
|
+
return new Response(JSON.stringify({
|
|
33
|
+
success: true,
|
|
34
|
+
counter: session.counter
|
|
35
|
+
}), {
|
|
36
|
+
status: 200,
|
|
37
|
+
statusText: 'OK',
|
|
38
|
+
headers: {
|
|
39
|
+
'Content-Type': 'application/json; charset=utf-8'
|
|
40
|
+
}
|
|
41
|
+
})
|
|
42
|
+
}
|
|
43
|
+
case 'post': {
|
|
44
|
+
session.counter += 1
|
|
45
|
+
return new Response(JSON.stringify({
|
|
46
|
+
success: true,
|
|
47
|
+
counter: session.counter
|
|
48
|
+
}), {
|
|
49
|
+
status: 200,
|
|
50
|
+
statusText: 'OK',
|
|
51
|
+
headers: {
|
|
52
|
+
'Content-Type': 'application/json; charset=utf-8'
|
|
53
|
+
}
|
|
54
|
+
})
|
|
55
|
+
}
|
|
56
|
+
case 'put': {
|
|
57
|
+
const { delta } = await request.json()
|
|
58
|
+
session.counter += delta
|
|
59
|
+
return new Response(JSON.stringify({
|
|
60
|
+
success: true,
|
|
61
|
+
counter: session.counter
|
|
62
|
+
}), {
|
|
63
|
+
status: 200,
|
|
64
|
+
statusText: 'OK',
|
|
65
|
+
headers: {
|
|
66
|
+
'Content-Type': 'application/json; charset=utf-8'
|
|
67
|
+
}
|
|
68
|
+
})
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
} break
|
|
72
|
+
case '/long-task': {
|
|
73
|
+
switch (request.method.toLowerCase()) {
|
|
74
|
+
case 'post': {
|
|
75
|
+
await sleep(200)
|
|
76
|
+
return new Response(JSON.stringify({
|
|
77
|
+
success: true,
|
|
78
|
+
}), {
|
|
79
|
+
status: 200,
|
|
80
|
+
statusText: 'OK',
|
|
81
|
+
headers: {
|
|
82
|
+
'Content-Type': 'application/json; charset=utf-8'
|
|
83
|
+
}
|
|
84
|
+
})
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
} break
|
|
88
|
+
case '/very-long-task': {
|
|
89
|
+
switch (request.method.toLowerCase()) {
|
|
90
|
+
case 'post': {
|
|
91
|
+
await sleep(500, request.signal)
|
|
92
|
+
if (request.signal.aborted)
|
|
93
|
+
connectionError()
|
|
94
|
+
return new Response(JSON.stringify({
|
|
95
|
+
success: true,
|
|
96
|
+
}), {
|
|
97
|
+
status: 200,
|
|
98
|
+
statusText: 'OK',
|
|
99
|
+
headers: {
|
|
100
|
+
'Content-Type': 'application/json; charset=utf-8'
|
|
101
|
+
}
|
|
102
|
+
})
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
} break
|
|
106
|
+
case '/too-many-requests': {
|
|
107
|
+
switch (request.method.toLowerCase()) {
|
|
108
|
+
case 'get': {
|
|
109
|
+
const attempts = ++ session.attempts
|
|
110
|
+
if (attempts < 3) {
|
|
111
|
+
return new Response(null, {
|
|
112
|
+
status: 429,
|
|
113
|
+
statusText: 'Too Many Requests',
|
|
114
|
+
headers: {
|
|
115
|
+
'Retry-After': '1'
|
|
116
|
+
}
|
|
117
|
+
})
|
|
118
|
+
}
|
|
119
|
+
return new Response(JSON.stringify({
|
|
120
|
+
success: true,
|
|
121
|
+
attempts
|
|
122
|
+
}), {
|
|
123
|
+
status: 200,
|
|
124
|
+
statusText: 'OK',
|
|
125
|
+
headers: {
|
|
126
|
+
'Content-Type': 'application/json; charset=utf-8'
|
|
127
|
+
}
|
|
128
|
+
})
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
} break
|
|
132
|
+
case '/try-harder': {
|
|
133
|
+
switch (request.method.toLowerCase()) {
|
|
134
|
+
case 'get': {
|
|
135
|
+
const attempts = ++ session.attempts
|
|
136
|
+
if (attempts < 3)
|
|
137
|
+
connectionError()
|
|
138
|
+
return new Response(JSON.stringify({
|
|
139
|
+
success: true,
|
|
140
|
+
attempts
|
|
141
|
+
}), {
|
|
142
|
+
status: 200,
|
|
143
|
+
statusText: 'OK',
|
|
144
|
+
headers: {
|
|
145
|
+
'Content-Type': 'application/json; charset=utf-8'
|
|
146
|
+
}
|
|
147
|
+
})
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
} break
|
|
151
|
+
case '/echo-content-type': {
|
|
152
|
+
switch (request.method.toLowerCase()) {
|
|
153
|
+
case 'post': {
|
|
154
|
+
return new Response(JSON.stringify({
|
|
155
|
+
success: true,
|
|
156
|
+
contentType: request.headers.get('content-type'),
|
|
157
|
+
}), {
|
|
158
|
+
status: 200,
|
|
159
|
+
statusText: 'OK',
|
|
160
|
+
headers: {
|
|
161
|
+
'Content-Type': 'application/json; charset=utf-8'
|
|
162
|
+
}
|
|
163
|
+
})
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
} break
|
|
167
|
+
}
|
|
168
|
+
return new Response(null, {
|
|
169
|
+
status: 404,
|
|
170
|
+
statusText: 'Not Found'
|
|
171
|
+
})
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
async function test1() {
|
|
176
|
+
const query = fetchBaseQuery({
|
|
177
|
+
baseUrl: 'https://test.local',
|
|
178
|
+
fetch: createFakeFetch()
|
|
179
|
+
})
|
|
180
|
+
{
|
|
181
|
+
const response = await query('/counter')
|
|
182
|
+
if (response.status !== 200)
|
|
183
|
+
throw new Error('E01')
|
|
184
|
+
const data = await response.json()
|
|
185
|
+
if (data?.counter !== 1)
|
|
186
|
+
throw new Error('E02')
|
|
187
|
+
}
|
|
188
|
+
{
|
|
189
|
+
const response = await query('https://test.local/counter')
|
|
190
|
+
if (response.status !== 200)
|
|
191
|
+
throw new Error('E03')
|
|
192
|
+
const data = await response.json()
|
|
193
|
+
if (data?.counter !== 1)
|
|
194
|
+
throw new Error('E04')
|
|
195
|
+
}
|
|
196
|
+
{
|
|
197
|
+
try {
|
|
198
|
+
const response = await query('https://test.global/counter')
|
|
199
|
+
throw new Error('E05')
|
|
200
|
+
} catch (e) {
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
async function test2() {
|
|
206
|
+
const query = fetchBaseQuery({
|
|
207
|
+
baseUrl: 'https://test.local',
|
|
208
|
+
fetch: createFakeFetch()
|
|
209
|
+
})
|
|
210
|
+
{
|
|
211
|
+
const response = await query('/counter')
|
|
212
|
+
if (response.status !== 200)
|
|
213
|
+
throw new Error('E01')
|
|
214
|
+
const data = await response.json()
|
|
215
|
+
if (data?.counter !== 1)
|
|
216
|
+
throw new Error('E02')
|
|
217
|
+
}
|
|
218
|
+
{
|
|
219
|
+
const response = await query('/counter', { method: 'post' })
|
|
220
|
+
if (response.status !== 200)
|
|
221
|
+
throw new Error('E03')
|
|
222
|
+
const data = await response.json()
|
|
223
|
+
if (data?.counter !== 2)
|
|
224
|
+
throw new Error('E04')
|
|
225
|
+
}
|
|
226
|
+
{
|
|
227
|
+
const response = await query('/counter')
|
|
228
|
+
if (response.status !== 200)
|
|
229
|
+
throw new Error('E05')
|
|
230
|
+
const data = await response.json()
|
|
231
|
+
if (data?.counter !== 2)
|
|
232
|
+
throw new Error('E06')
|
|
233
|
+
}
|
|
234
|
+
{
|
|
235
|
+
const response = await query('/counter', { method: 'post' })
|
|
236
|
+
if (response.status !== 200)
|
|
237
|
+
throw new Error('E07')
|
|
238
|
+
const data = await response.json()
|
|
239
|
+
if (data?.counter !== 3)
|
|
240
|
+
throw new Error('E08')
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
async function test3() {
|
|
245
|
+
const query = fetchBaseQuery({
|
|
246
|
+
baseUrl: 'https://test.local',
|
|
247
|
+
fetch: createFakeFetch(),
|
|
248
|
+
middlewares: [
|
|
249
|
+
muteErrors()
|
|
250
|
+
]
|
|
251
|
+
})
|
|
252
|
+
{
|
|
253
|
+
const response = await query('https://test.global/counter')
|
|
254
|
+
if (response.status !== 0)
|
|
255
|
+
throw new Error('E01')
|
|
256
|
+
}
|
|
257
|
+
{
|
|
258
|
+
const response = await query('/not-existed-path')
|
|
259
|
+
if (response.status !== 404)
|
|
260
|
+
throw new Error('E02')
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
async function test4() {
|
|
265
|
+
const query = fetchBaseQuery({
|
|
266
|
+
baseUrl: 'https://test.local',
|
|
267
|
+
fetch: createFakeFetch(),
|
|
268
|
+
})
|
|
269
|
+
{
|
|
270
|
+
const response = await query('/echo-content-type', {
|
|
271
|
+
method: 'post',
|
|
272
|
+
body: new URLSearchParams({ 'foo': 'bar' })
|
|
273
|
+
})
|
|
274
|
+
if (response.status !== 200)
|
|
275
|
+
throw new Error('E01')
|
|
276
|
+
const data = await response.json()
|
|
277
|
+
if (!/application\/x-www-form-urlencoded(\;| |$)/.test(data.contentType))
|
|
278
|
+
throw new Error('E02')
|
|
279
|
+
}
|
|
280
|
+
{
|
|
281
|
+
const body = new FormData()
|
|
282
|
+
body.append('foo', 'bat')
|
|
283
|
+
const response = await query('/echo-content-type', {
|
|
284
|
+
method: 'post',
|
|
285
|
+
body
|
|
286
|
+
})
|
|
287
|
+
if (response.status !== 200)
|
|
288
|
+
throw new Error('E03')
|
|
289
|
+
const data = await response.json()
|
|
290
|
+
if (!/multipart\/form-data(\;| |$)/.test(data.contentType))
|
|
291
|
+
throw new Error('E04')
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
async function test5() {
|
|
296
|
+
const query = fetchBaseQuery({
|
|
297
|
+
baseUrl: 'https://test.local',
|
|
298
|
+
timeout: 300,
|
|
299
|
+
fetch: createFakeFetch(),
|
|
300
|
+
middlewares: [
|
|
301
|
+
muteErrors()
|
|
302
|
+
]
|
|
303
|
+
})
|
|
304
|
+
{
|
|
305
|
+
const response = await query('/long-task', { method: 'post' })
|
|
306
|
+
if (response.status !== 200)
|
|
307
|
+
throw new Error('E01')
|
|
308
|
+
}
|
|
309
|
+
{
|
|
310
|
+
const responsePromise = query('/very-long-task', { method: 'post' })
|
|
311
|
+
setTimeout(() => { responsePromise.abort() }, 100)
|
|
312
|
+
const response = await responsePromise
|
|
313
|
+
if (response.status !== 0)
|
|
314
|
+
throw new Error('E02')
|
|
315
|
+
}
|
|
316
|
+
{
|
|
317
|
+
const response = await query('/very-long-task', { method: 'post' })
|
|
318
|
+
if (response.status !== 0)
|
|
319
|
+
throw new Error('E03')
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
async function test6() {
|
|
324
|
+
{
|
|
325
|
+
const query = fetchBaseQuery({
|
|
326
|
+
baseUrl: 'https://test.local',
|
|
327
|
+
fetch: createFakeFetch(),
|
|
328
|
+
middlewares: [
|
|
329
|
+
retryOn429({
|
|
330
|
+
maxNumAttempts: 5
|
|
331
|
+
})
|
|
332
|
+
]
|
|
333
|
+
})
|
|
334
|
+
const response = await query('/too-many-requests')
|
|
335
|
+
if (response.status !== 200)
|
|
336
|
+
throw new Error('E01')
|
|
337
|
+
const data = await response.json()
|
|
338
|
+
if (data.attempts !== 3)
|
|
339
|
+
throw new Error('E02')
|
|
340
|
+
}
|
|
341
|
+
{
|
|
342
|
+
const query = fetchBaseQuery({
|
|
343
|
+
baseUrl: 'https://test.local',
|
|
344
|
+
fetch: createFakeFetch(),
|
|
345
|
+
middlewares: [
|
|
346
|
+
retryOn429({
|
|
347
|
+
maxNumAttempts: 2
|
|
348
|
+
})
|
|
349
|
+
]
|
|
350
|
+
})
|
|
351
|
+
const response = await query('/too-many-requests')
|
|
352
|
+
if (response.status !== 429)
|
|
353
|
+
throw new Error('E03')
|
|
354
|
+
}
|
|
355
|
+
{
|
|
356
|
+
const query = fetchBaseQuery({
|
|
357
|
+
baseUrl: 'https://test.local',
|
|
358
|
+
fetch: createFakeFetch(),
|
|
359
|
+
timeout: 300,
|
|
360
|
+
middlewares: [
|
|
361
|
+
muteErrors(),
|
|
362
|
+
retryOn429({
|
|
363
|
+
maxNumAttempts: 5
|
|
364
|
+
})
|
|
365
|
+
]
|
|
366
|
+
})
|
|
367
|
+
const response = await query('/too-many-requests')
|
|
368
|
+
if (response.status !== 0)
|
|
369
|
+
throw new Error('E04')
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
async function test7() {
|
|
374
|
+
{
|
|
375
|
+
const query = fetchBaseQuery({
|
|
376
|
+
baseUrl: 'https://test.local',
|
|
377
|
+
fetch: createFakeFetch(),
|
|
378
|
+
middlewares: [
|
|
379
|
+
retryOnError({
|
|
380
|
+
maxNumAttempts: 5,
|
|
381
|
+
backoffTime: attempt => 200 + (attempt * 0)
|
|
382
|
+
})
|
|
383
|
+
]
|
|
384
|
+
})
|
|
385
|
+
const response = await query('/try-harder')
|
|
386
|
+
if (response.status !== 200)
|
|
387
|
+
throw new Error('E01')
|
|
388
|
+
const data = await response.json()
|
|
389
|
+
if (data.attempts !== 3)
|
|
390
|
+
throw new Error('E02')
|
|
391
|
+
}
|
|
392
|
+
{
|
|
393
|
+
const query = fetchBaseQuery({
|
|
394
|
+
baseUrl: 'https://test.local',
|
|
395
|
+
fetch: createFakeFetch(),
|
|
396
|
+
middlewares: [
|
|
397
|
+
muteErrors(),
|
|
398
|
+
retryOnError({
|
|
399
|
+
maxNumAttempts: 2,
|
|
400
|
+
backoffTime: attempt => 200 + (attempt * 0)
|
|
401
|
+
})
|
|
402
|
+
]
|
|
403
|
+
})
|
|
404
|
+
const response = await query('/try-harder')
|
|
405
|
+
if (response.status !== 0)
|
|
406
|
+
throw new Error('E03')
|
|
407
|
+
}
|
|
408
|
+
{
|
|
409
|
+
const query = fetchBaseQuery({
|
|
410
|
+
baseUrl: 'https://test.local',
|
|
411
|
+
fetch: createFakeFetch(),
|
|
412
|
+
timeout: 300,
|
|
413
|
+
middlewares: [
|
|
414
|
+
muteErrors(),
|
|
415
|
+
retryOnError({
|
|
416
|
+
maxNumAttempts: 5,
|
|
417
|
+
backoffTime: attempt => 200 + (attempt * 0)
|
|
418
|
+
})
|
|
419
|
+
]
|
|
420
|
+
})
|
|
421
|
+
const response = await query('/try-harder')
|
|
422
|
+
if (response.status !== 0)
|
|
423
|
+
throw new Error('E04')
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
async function test8() {
|
|
428
|
+
{
|
|
429
|
+
const query = json(fetchBaseQuery({
|
|
430
|
+
baseUrl: 'https://test.local',
|
|
431
|
+
fetch: createFakeFetch(),
|
|
432
|
+
middlewares: [
|
|
433
|
+
retryOnError({
|
|
434
|
+
maxNumAttempts: 5,
|
|
435
|
+
backoffTime: attempt => 200 + (attempt * 0)
|
|
436
|
+
})
|
|
437
|
+
]
|
|
438
|
+
}))
|
|
439
|
+
const response = await query('/try-harder')
|
|
440
|
+
if (response.status !== 200)
|
|
441
|
+
throw new Error('E01')
|
|
442
|
+
const { data } = response
|
|
443
|
+
if (data.attempts !== 3)
|
|
444
|
+
throw new Error('E02')
|
|
445
|
+
}
|
|
446
|
+
{
|
|
447
|
+
const query = json(fetchBaseQuery({
|
|
448
|
+
baseUrl: 'https://test.local',
|
|
449
|
+
fetch: createFakeFetch(),
|
|
450
|
+
}))
|
|
451
|
+
{
|
|
452
|
+
const response = await query('/counter')
|
|
453
|
+
if (response.status !== 200)
|
|
454
|
+
throw new Error('E03')
|
|
455
|
+
if (response.data?.counter !== 1)
|
|
456
|
+
throw new Error('E04')
|
|
457
|
+
}
|
|
458
|
+
{
|
|
459
|
+
const response = await query('/counter', { method: 'post' })
|
|
460
|
+
if (response.status !== 200)
|
|
461
|
+
throw new Error('E05')
|
|
462
|
+
if (response.data?.counter !== 2)
|
|
463
|
+
throw new Error('E06')
|
|
464
|
+
}
|
|
465
|
+
{
|
|
466
|
+
const response = await query('/counter', { method: 'put', body: { delta: 3 } })
|
|
467
|
+
if (response.status !== 200)
|
|
468
|
+
throw new Error('E05')
|
|
469
|
+
if (response.data?.counter !== 5)
|
|
470
|
+
throw new Error('E06')
|
|
471
|
+
}
|
|
472
|
+
{
|
|
473
|
+
const response = await query('/echo-content-type', {
|
|
474
|
+
method: 'post',
|
|
475
|
+
body: { 'foo': 'bar' }
|
|
476
|
+
})
|
|
477
|
+
if (response.status !== 200)
|
|
478
|
+
throw new Error('E07')
|
|
479
|
+
const { data } = response
|
|
480
|
+
if (data.contentType !== 'application/json; charset=utf-8')
|
|
481
|
+
throw new Error('E08')
|
|
482
|
+
}
|
|
483
|
+
{
|
|
484
|
+
const response = await query('/echo-content-type', {
|
|
485
|
+
method: 'post',
|
|
486
|
+
headers: { 'Content-Type': 'application/json' },
|
|
487
|
+
body: { 'foo': 'bar' }
|
|
488
|
+
})
|
|
489
|
+
if (response.status !== 200)
|
|
490
|
+
throw new Error('E09')
|
|
491
|
+
const { data } = response
|
|
492
|
+
if (data.contentType !== 'application/json')
|
|
493
|
+
throw new Error('E10')
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
async function test9() {
|
|
499
|
+
const query = fetchBaseQuery({
|
|
500
|
+
fetch: createFakeFetch(),
|
|
501
|
+
})
|
|
502
|
+
const promise = query('https://test.local/very-long-task', { method: 'post' })
|
|
503
|
+
let error
|
|
504
|
+
promise.catch(err => {
|
|
505
|
+
error = err
|
|
506
|
+
})
|
|
507
|
+
const reason = 'Unmounting...'
|
|
508
|
+
try {
|
|
509
|
+
setTimeout(() => promise.abort(reason), 50)
|
|
510
|
+
await promise
|
|
511
|
+
} catch (e) {
|
|
512
|
+
if (e !== reason)
|
|
513
|
+
throw new Error('E01')
|
|
514
|
+
} finally {
|
|
515
|
+
if (error !== reason)
|
|
516
|
+
throw new Error('E02')
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
async function runTests() {
|
|
521
|
+
const allTests = [
|
|
522
|
+
test1,
|
|
523
|
+
test2,
|
|
524
|
+
test3,
|
|
525
|
+
test4,
|
|
526
|
+
test5,
|
|
527
|
+
test6,
|
|
528
|
+
test7,
|
|
529
|
+
test8,
|
|
530
|
+
test9,
|
|
531
|
+
]
|
|
532
|
+
for (const test of allTests) {
|
|
533
|
+
await test()
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
console.log('Running tests, wait...')
|
|
538
|
+
runTests()
|
|
539
|
+
.then(() => {
|
|
540
|
+
console.log('All tests done.')
|
|
541
|
+
globalThis?.process?.exit(0)
|
|
542
|
+
})
|
|
543
|
+
.catch(error => {
|
|
544
|
+
console.error('Error while testing:', error)
|
|
545
|
+
globalThis?.process?.exit(1)
|
|
546
|
+
})
|