graphql-http 1.11.0 → 1.12.0
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/README.md +2 -0
- package/lib/audits/common.d.mts +1 -0
- package/lib/audits/common.d.ts +1 -0
- package/lib/audits/server.js +75 -87
- package/lib/audits/server.mjs +76 -88
- package/lib/audits/utils.d.mts +34 -15
- package/lib/audits/utils.d.ts +34 -15
- package/lib/audits/utils.js +86 -58
- package/lib/audits/utils.mjs +84 -56
- package/lib/handler.js +24 -25
- package/lib/handler.mjs +25 -26
- package/lib/utils.d.mts +4 -1
- package/lib/utils.d.ts +4 -1
- package/lib/utils.js +9 -4
- package/lib/utils.mjs +7 -3
- package/package.json +16 -16
- package/umd/graphql-http.js +2 -2
- package/umd/graphql-http.min.js +1 -1
- package/umd/graphql-http.min.js.gz +0 -0
package/lib/audits/server.mjs
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* audit/server
|
|
4
4
|
*
|
|
5
5
|
*/
|
|
6
|
-
import {
|
|
6
|
+
import { ressert, audit, extendedTypeof, AuditError } from './utils.mjs';
|
|
7
7
|
/**
|
|
8
8
|
* List of server audits required to check GraphQL over HTTP spec conformance.
|
|
9
9
|
*
|
|
@@ -24,8 +24,10 @@ export function serverAudits(opts) {
|
|
|
24
24
|
},
|
|
25
25
|
body: JSON.stringify({ query: '{ __typename }' }),
|
|
26
26
|
});
|
|
27
|
-
|
|
28
|
-
|
|
27
|
+
ressert(res).status.toBe(200);
|
|
28
|
+
ressert(res)
|
|
29
|
+
.header('content-type')
|
|
30
|
+
.toContain('application/graphql-response+json');
|
|
29
31
|
}),
|
|
30
32
|
audit('MUST accept application/json and match the content-type', async () => {
|
|
31
33
|
const res = await fetchFn(await getUrl(opts.url), {
|
|
@@ -36,8 +38,8 @@ export function serverAudits(opts) {
|
|
|
36
38
|
},
|
|
37
39
|
body: JSON.stringify({ query: '{ __typename }' }),
|
|
38
40
|
});
|
|
39
|
-
|
|
40
|
-
|
|
41
|
+
ressert(res).status.toBe(200);
|
|
42
|
+
ressert(res).header('content-type').toContain('application/json');
|
|
41
43
|
}),
|
|
42
44
|
audit('SHOULD accept */* and use application/json for the content-type', async () => {
|
|
43
45
|
const res = await fetchFn(await getUrl(opts.url), {
|
|
@@ -48,15 +50,15 @@ export function serverAudits(opts) {
|
|
|
48
50
|
},
|
|
49
51
|
body: JSON.stringify({ query: '{ __typename }' }),
|
|
50
52
|
});
|
|
51
|
-
|
|
52
|
-
|
|
53
|
+
ressert(res).status.toBe(200);
|
|
54
|
+
ressert(res).header('content-type').toContain('application/json');
|
|
53
55
|
}),
|
|
54
56
|
audit('SHOULD assume application/json content-type when accept is missing', async () => {
|
|
55
57
|
const url = new URL(await getUrl(opts.url));
|
|
56
58
|
url.searchParams.set('query', '{ __typename }');
|
|
57
59
|
const res = await fetchFn(url.toString());
|
|
58
|
-
|
|
59
|
-
|
|
60
|
+
ressert(res).status.toBe(200);
|
|
61
|
+
ressert(res).header('content-type').toContain('application/json');
|
|
60
62
|
}),
|
|
61
63
|
audit('MUST use utf-8 encoding when responding', async () => {
|
|
62
64
|
const res = await fetchFn(await getUrl(opts.url), {
|
|
@@ -66,28 +68,16 @@ export function serverAudits(opts) {
|
|
|
66
68
|
},
|
|
67
69
|
body: JSON.stringify({ query: '{ __typename }' }),
|
|
68
70
|
});
|
|
69
|
-
|
|
70
|
-
// has charset set to utf-8
|
|
71
|
-
try {
|
|
72
|
-
assert('Content-Type header', res.headers.get('content-type')).toContain('charset=utf-8');
|
|
73
|
-
return;
|
|
74
|
-
}
|
|
75
|
-
catch (_a) {
|
|
76
|
-
// noop, continue
|
|
77
|
-
}
|
|
78
|
-
// has no charset specified
|
|
79
|
-
assert('Content-Type header', res.headers.get('content-type')).notToContain('charset');
|
|
80
|
-
// and the content is utf-8 encoded
|
|
71
|
+
ressert(res).status.toBe(200);
|
|
81
72
|
try {
|
|
82
73
|
const decoder = new TextDecoder('utf-8');
|
|
83
|
-
|
|
84
|
-
assert('UTF-8 decoded body', decoded).toBe('{"data":{"__typename":"Query"}}');
|
|
74
|
+
decoder.decode(await res.arrayBuffer());
|
|
85
75
|
}
|
|
86
|
-
catch (
|
|
87
|
-
throw '
|
|
76
|
+
catch (_a) {
|
|
77
|
+
throw new AuditError(res, 'Response body is not UTF-8 encoded');
|
|
88
78
|
}
|
|
89
79
|
}),
|
|
90
|
-
audit('MUST accept utf-8
|
|
80
|
+
audit('MUST accept utf-8 encoded request', async () => {
|
|
91
81
|
const res = await fetchFn(await getUrl(opts.url), {
|
|
92
82
|
method: 'POST',
|
|
93
83
|
headers: {
|
|
@@ -95,10 +85,9 @@ export function serverAudits(opts) {
|
|
|
95
85
|
},
|
|
96
86
|
body: JSON.stringify({ query: '{ __typename }' }),
|
|
97
87
|
});
|
|
98
|
-
|
|
99
|
-
assert('Content-Type header', res.headers.get('content-type')).toContain('utf-8');
|
|
88
|
+
ressert(res).status.toBe(200);
|
|
100
89
|
}),
|
|
101
|
-
audit('MUST assume utf-8 if encoding is unspecified', async () => {
|
|
90
|
+
audit('MUST assume utf-8 in request if encoding is unspecified', async () => {
|
|
102
91
|
const res = await fetchFn(await getUrl(opts.url), {
|
|
103
92
|
method: 'POST',
|
|
104
93
|
headers: {
|
|
@@ -106,8 +95,7 @@ export function serverAudits(opts) {
|
|
|
106
95
|
},
|
|
107
96
|
body: JSON.stringify({ query: '{ __typename }' }),
|
|
108
97
|
});
|
|
109
|
-
|
|
110
|
-
assert('Content-Type header', res.headers.get('content-type')).toContain('utf-8');
|
|
98
|
+
ressert(res).status.toBe(200);
|
|
111
99
|
}),
|
|
112
100
|
// Request
|
|
113
101
|
audit('MUST accept POST requests', async () => {
|
|
@@ -116,13 +104,13 @@ export function serverAudits(opts) {
|
|
|
116
104
|
headers: { 'content-type': 'application/json' },
|
|
117
105
|
body: JSON.stringify({ query: '{ __typename }' }),
|
|
118
106
|
});
|
|
119
|
-
|
|
107
|
+
ressert(res).status.toBe(200);
|
|
120
108
|
}),
|
|
121
109
|
audit('MAY accept application/x-www-form-urlencoded formatted GET requests', async () => {
|
|
122
110
|
const url = new URL(await getUrl(opts.url));
|
|
123
111
|
url.searchParams.set('query', '{ __typename }');
|
|
124
112
|
const res = await fetchFn(url.toString());
|
|
125
|
-
|
|
113
|
+
ressert(res).status.toBe(200);
|
|
126
114
|
}),
|
|
127
115
|
// Request GET
|
|
128
116
|
// TODO: this is a MUST if the server supports GET requests
|
|
@@ -134,14 +122,14 @@ export function serverAudits(opts) {
|
|
|
134
122
|
accept: 'application/graphql-response+json',
|
|
135
123
|
},
|
|
136
124
|
});
|
|
137
|
-
|
|
125
|
+
ressert(res).status.toBeBetween(400, 499);
|
|
138
126
|
}),
|
|
139
127
|
// Request POST
|
|
140
128
|
audit('SHOULD respond with 4xx status code if content-type is not supplied on POST requests', async () => {
|
|
141
129
|
const res = await fetchFn(await getUrl(opts.url), {
|
|
142
130
|
method: 'POST',
|
|
143
131
|
});
|
|
144
|
-
|
|
132
|
+
ressert(res).status.toBeBetween(400, 499);
|
|
145
133
|
}),
|
|
146
134
|
audit('MUST accept application/json POST requests', async () => {
|
|
147
135
|
const res = await fetchFn(await getUrl(opts.url), {
|
|
@@ -149,7 +137,7 @@ export function serverAudits(opts) {
|
|
|
149
137
|
headers: { 'content-type': 'application/json' },
|
|
150
138
|
body: JSON.stringify({ query: '{ __typename }' }),
|
|
151
139
|
});
|
|
152
|
-
|
|
140
|
+
ressert(res).status.toBe(200);
|
|
153
141
|
}),
|
|
154
142
|
audit('MUST require a request body on POST', async () => {
|
|
155
143
|
var _a;
|
|
@@ -158,10 +146,10 @@ export function serverAudits(opts) {
|
|
|
158
146
|
headers: { 'content-type': 'application/json' },
|
|
159
147
|
});
|
|
160
148
|
if ((_a = res.headers.get('content-type')) === null || _a === void 0 ? void 0 : _a.includes('application/json')) {
|
|
161
|
-
|
|
149
|
+
await ressert(res).bodyAsExecutionResult.toHaveProperty('errors');
|
|
162
150
|
}
|
|
163
151
|
else {
|
|
164
|
-
|
|
152
|
+
ressert(res).status.toBe(400);
|
|
165
153
|
}
|
|
166
154
|
}),
|
|
167
155
|
// Request Parameters
|
|
@@ -176,7 +164,7 @@ export function serverAudits(opts) {
|
|
|
176
164
|
},
|
|
177
165
|
body: JSON.stringify({ notquery: '{ __typename }' }),
|
|
178
166
|
});
|
|
179
|
-
|
|
167
|
+
ressert(res).status.toBe(400);
|
|
180
168
|
}),
|
|
181
169
|
audit('SHOULD use 200 status code with errors field on missing {query} parameter when accepting application/json', async () => {
|
|
182
170
|
const res = await fetchFn(await getUrl(opts.url), {
|
|
@@ -187,8 +175,8 @@ export function serverAudits(opts) {
|
|
|
187
175
|
},
|
|
188
176
|
body: JSON.stringify({ notquery: '{ __typename }' }),
|
|
189
177
|
});
|
|
190
|
-
|
|
191
|
-
|
|
178
|
+
ressert(res).status.toBe(200);
|
|
179
|
+
await ressert(res).bodyAsExecutionResult.toHaveProperty('errors');
|
|
192
180
|
}),
|
|
193
181
|
...[{ obj: 'ect' }, 0, false, ['array']].map((invalid) => audit(
|
|
194
182
|
// TODO: convert to MUST after watershed
|
|
@@ -203,7 +191,7 @@ export function serverAudits(opts) {
|
|
|
203
191
|
query: invalid,
|
|
204
192
|
}),
|
|
205
193
|
});
|
|
206
|
-
|
|
194
|
+
ressert(res).status.toBe(400);
|
|
207
195
|
})),
|
|
208
196
|
...[{ obj: 'ect' }, 0, false, ['array']].map((invalid) => audit(`SHOULD use 200 status code with errors field on ${extendedTypeof(invalid)} {query} parameter when accepting application/json`, async () => {
|
|
209
197
|
const res = await fetchFn(await getUrl(opts.url), {
|
|
@@ -216,8 +204,8 @@ export function serverAudits(opts) {
|
|
|
216
204
|
query: invalid,
|
|
217
205
|
}),
|
|
218
206
|
});
|
|
219
|
-
|
|
220
|
-
|
|
207
|
+
ressert(res).status.toBe(200);
|
|
208
|
+
await ressert(res).bodyAsExecutionResult.toHaveProperty('errors');
|
|
221
209
|
})),
|
|
222
210
|
audit(
|
|
223
211
|
// TODO: convert to MUST after watershed
|
|
@@ -232,7 +220,7 @@ export function serverAudits(opts) {
|
|
|
232
220
|
query: '{ __typename }',
|
|
233
221
|
}),
|
|
234
222
|
});
|
|
235
|
-
|
|
223
|
+
ressert(res).status.toBe(200);
|
|
236
224
|
}),
|
|
237
225
|
audit('MUST allow string {query} parameter when accepting application/json', async () => {
|
|
238
226
|
const res = await fetchFn(await getUrl(opts.url), {
|
|
@@ -245,8 +233,8 @@ export function serverAudits(opts) {
|
|
|
245
233
|
query: '{ __typename }',
|
|
246
234
|
}),
|
|
247
235
|
});
|
|
248
|
-
|
|
249
|
-
|
|
236
|
+
ressert(res).status.toBe(200);
|
|
237
|
+
await ressert(res).bodyAsExecutionResult.notToHaveProperty('errors');
|
|
250
238
|
}),
|
|
251
239
|
...[{ obj: 'ect' }, 0, false, ['array']].map((invalid) => audit(
|
|
252
240
|
// TODO: convert to MUST after watershed
|
|
@@ -262,7 +250,7 @@ export function serverAudits(opts) {
|
|
|
262
250
|
query: '{ __typename }',
|
|
263
251
|
}),
|
|
264
252
|
});
|
|
265
|
-
|
|
253
|
+
ressert(res).status.toBe(400);
|
|
266
254
|
})),
|
|
267
255
|
...[{ obj: 'ect' }, 0, false, ['array']].map((invalid) => audit(`SHOULD use 200 status code with errors field on ${extendedTypeof(invalid)} {operationName} parameter when accepting application/json`, async () => {
|
|
268
256
|
const res = await fetchFn(await getUrl(opts.url), {
|
|
@@ -276,8 +264,8 @@ export function serverAudits(opts) {
|
|
|
276
264
|
query: '{ __typename }',
|
|
277
265
|
}),
|
|
278
266
|
});
|
|
279
|
-
|
|
280
|
-
|
|
267
|
+
ressert(res).status.toBe(200);
|
|
268
|
+
await ressert(res).bodyAsExecutionResult.toHaveProperty('errors');
|
|
281
269
|
})),
|
|
282
270
|
audit(
|
|
283
271
|
// TODO: convert to MUST after watershed
|
|
@@ -293,7 +281,7 @@ export function serverAudits(opts) {
|
|
|
293
281
|
query: 'query Query { __typename }',
|
|
294
282
|
}),
|
|
295
283
|
});
|
|
296
|
-
|
|
284
|
+
ressert(res).status.toBe(200);
|
|
297
285
|
}),
|
|
298
286
|
audit('MUST allow string {operationName} parameter when accepting application/json', async () => {
|
|
299
287
|
const res = await fetchFn(await getUrl(opts.url), {
|
|
@@ -307,8 +295,8 @@ export function serverAudits(opts) {
|
|
|
307
295
|
query: 'query Query { __typename }',
|
|
308
296
|
}),
|
|
309
297
|
});
|
|
310
|
-
|
|
311
|
-
|
|
298
|
+
ressert(res).status.toBe(200);
|
|
299
|
+
await ressert(res).bodyAsExecutionResult.notToHaveProperty('errors');
|
|
312
300
|
}),
|
|
313
301
|
...['variables', 'operationName', 'extensions'].flatMap((parameter) => [
|
|
314
302
|
audit(
|
|
@@ -325,8 +313,8 @@ export function serverAudits(opts) {
|
|
|
325
313
|
[parameter]: null,
|
|
326
314
|
}),
|
|
327
315
|
});
|
|
328
|
-
|
|
329
|
-
|
|
316
|
+
ressert(res).status.toBe(200);
|
|
317
|
+
await ressert(res).bodyAsExecutionResult.notToHaveProperty('errors');
|
|
330
318
|
}),
|
|
331
319
|
audit(`MUST allow null {${parameter}} parameter when accepting application/json`, async () => {
|
|
332
320
|
const res = await fetchFn(await getUrl(opts.url), {
|
|
@@ -340,8 +328,8 @@ export function serverAudits(opts) {
|
|
|
340
328
|
[parameter]: null,
|
|
341
329
|
}),
|
|
342
330
|
});
|
|
343
|
-
|
|
344
|
-
|
|
331
|
+
ressert(res).status.toBe(200);
|
|
332
|
+
await ressert(res).bodyAsExecutionResult.notToHaveProperty('errors');
|
|
345
333
|
}),
|
|
346
334
|
]),
|
|
347
335
|
...['string', 0, false, ['array']].map((invalid) => audit(
|
|
@@ -358,7 +346,7 @@ export function serverAudits(opts) {
|
|
|
358
346
|
variables: invalid,
|
|
359
347
|
}),
|
|
360
348
|
});
|
|
361
|
-
|
|
349
|
+
ressert(res).status.toBe(400);
|
|
362
350
|
})),
|
|
363
351
|
...['string', 0, false, ['array']].map((invalid) => audit(`SHOULD use 200 status code with errors field on ${extendedTypeof(invalid)} {variables} parameter when accepting application/json`, async () => {
|
|
364
352
|
const res = await fetchFn(await getUrl(opts.url), {
|
|
@@ -372,8 +360,8 @@ export function serverAudits(opts) {
|
|
|
372
360
|
variables: invalid,
|
|
373
361
|
}),
|
|
374
362
|
});
|
|
375
|
-
|
|
376
|
-
|
|
363
|
+
ressert(res).status.toBe(200);
|
|
364
|
+
await ressert(res).bodyAsExecutionResult.toHaveProperty('errors');
|
|
377
365
|
})),
|
|
378
366
|
audit(
|
|
379
367
|
// TODO: convert to MUST after watershed
|
|
@@ -389,7 +377,7 @@ export function serverAudits(opts) {
|
|
|
389
377
|
variables: { name: 'sometype' },
|
|
390
378
|
}),
|
|
391
379
|
});
|
|
392
|
-
|
|
380
|
+
ressert(res).status.toBe(200);
|
|
393
381
|
}),
|
|
394
382
|
audit('MUST allow map {variables} parameter when accepting application/json', async () => {
|
|
395
383
|
const res = await fetchFn(await getUrl(opts.url), {
|
|
@@ -403,8 +391,8 @@ export function serverAudits(opts) {
|
|
|
403
391
|
variables: { name: 'sometype' },
|
|
404
392
|
}),
|
|
405
393
|
});
|
|
406
|
-
|
|
407
|
-
|
|
394
|
+
ressert(res).status.toBe(200);
|
|
395
|
+
await ressert(res).bodyAsExecutionResult.notToHaveProperty('errors');
|
|
408
396
|
}),
|
|
409
397
|
audit('MAY allow URL-encoded JSON string {variables} parameter in GETs when accepting application/graphql-response+json', async () => {
|
|
410
398
|
const url = new URL(await getUrl(opts.url));
|
|
@@ -416,7 +404,7 @@ export function serverAudits(opts) {
|
|
|
416
404
|
accept: 'application/graphql-response+json',
|
|
417
405
|
},
|
|
418
406
|
});
|
|
419
|
-
|
|
407
|
+
ressert(res).status.toBe(200);
|
|
420
408
|
}),
|
|
421
409
|
audit('MAY allow URL-encoded JSON string {variables} parameter in GETs when accepting application/json', async () => {
|
|
422
410
|
const url = new URL(await getUrl(opts.url));
|
|
@@ -428,8 +416,8 @@ export function serverAudits(opts) {
|
|
|
428
416
|
accept: 'application/json',
|
|
429
417
|
},
|
|
430
418
|
});
|
|
431
|
-
|
|
432
|
-
|
|
419
|
+
ressert(res).status.toBe(200);
|
|
420
|
+
await ressert(res).bodyAsExecutionResult.notToHaveProperty('errors');
|
|
433
421
|
}),
|
|
434
422
|
...['string', 0, false, ['array']].map((invalid) => audit(
|
|
435
423
|
// TODO: convert to MUST after watershed
|
|
@@ -445,7 +433,7 @@ export function serverAudits(opts) {
|
|
|
445
433
|
extensions: invalid,
|
|
446
434
|
}),
|
|
447
435
|
});
|
|
448
|
-
|
|
436
|
+
ressert(res).status.toBe(400);
|
|
449
437
|
})),
|
|
450
438
|
...['string', 0, false, ['array']].map((invalid) => audit(`SHOULD use 200 status code with errors field on ${extendedTypeof(invalid)} {extensions} parameter when accepting application/json`, async () => {
|
|
451
439
|
const res = await fetchFn(await getUrl(opts.url), {
|
|
@@ -459,8 +447,8 @@ export function serverAudits(opts) {
|
|
|
459
447
|
extensions: invalid,
|
|
460
448
|
}),
|
|
461
449
|
});
|
|
462
|
-
|
|
463
|
-
|
|
450
|
+
ressert(res).status.toBe(200);
|
|
451
|
+
await ressert(res).bodyAsExecutionResult.toHaveProperty('errors');
|
|
464
452
|
})),
|
|
465
453
|
audit(
|
|
466
454
|
// TODO: convert to MUST after watershed
|
|
@@ -476,7 +464,7 @@ export function serverAudits(opts) {
|
|
|
476
464
|
extensions: { some: 'value' },
|
|
477
465
|
}),
|
|
478
466
|
});
|
|
479
|
-
|
|
467
|
+
ressert(res).status.toBe(200);
|
|
480
468
|
}),
|
|
481
469
|
audit('MUST allow map {extensions} parameter when accepting application/json', async () => {
|
|
482
470
|
const res = await fetchFn(await getUrl(opts.url), {
|
|
@@ -490,8 +478,8 @@ export function serverAudits(opts) {
|
|
|
490
478
|
extensions: { some: 'value' },
|
|
491
479
|
}),
|
|
492
480
|
});
|
|
493
|
-
|
|
494
|
-
|
|
481
|
+
ressert(res).status.toBe(200);
|
|
482
|
+
await ressert(res).bodyAsExecutionResult.notToHaveProperty('errors');
|
|
495
483
|
}),
|
|
496
484
|
// TODO: audit('MUST accept a map for the {extensions} parameter'),
|
|
497
485
|
// Response application/json
|
|
@@ -504,7 +492,7 @@ export function serverAudits(opts) {
|
|
|
504
492
|
},
|
|
505
493
|
body: '{ "not a JSON',
|
|
506
494
|
});
|
|
507
|
-
|
|
495
|
+
ressert(res).status.toBe(200);
|
|
508
496
|
}),
|
|
509
497
|
audit('SHOULD use 200 status code if parameters are invalid when accepting application/json', async () => {
|
|
510
498
|
const res = await fetchFn(await getUrl(opts.url), {
|
|
@@ -517,7 +505,7 @@ export function serverAudits(opts) {
|
|
|
517
505
|
qeury: /* typo */ '{ __typename }',
|
|
518
506
|
}),
|
|
519
507
|
});
|
|
520
|
-
|
|
508
|
+
ressert(res).status.toBe(200);
|
|
521
509
|
}),
|
|
522
510
|
audit('SHOULD use 200 status code on document parsing failure when accepting application/json', async () => {
|
|
523
511
|
const res = await fetchFn(await getUrl(opts.url), {
|
|
@@ -528,7 +516,7 @@ export function serverAudits(opts) {
|
|
|
528
516
|
},
|
|
529
517
|
body: JSON.stringify({ query: '{' }),
|
|
530
518
|
});
|
|
531
|
-
|
|
519
|
+
ressert(res).status.toBe(200);
|
|
532
520
|
}),
|
|
533
521
|
audit('SHOULD use 200 status code on document validation failure when accepting application/json', async () => {
|
|
534
522
|
const res = await fetchFn(await getUrl(opts.url), {
|
|
@@ -541,7 +529,7 @@ export function serverAudits(opts) {
|
|
|
541
529
|
query: '{ 8f31403dfe404bccbb0e835f2629c6a7 }', // making sure the field doesnt exist
|
|
542
530
|
}),
|
|
543
531
|
});
|
|
544
|
-
|
|
532
|
+
ressert(res).status.toBe(200);
|
|
545
533
|
}),
|
|
546
534
|
// Response application/graphql-response+json
|
|
547
535
|
audit(
|
|
@@ -555,7 +543,7 @@ export function serverAudits(opts) {
|
|
|
555
543
|
},
|
|
556
544
|
body: '{ "not a JSON',
|
|
557
545
|
});
|
|
558
|
-
|
|
546
|
+
ressert(res).status.toBeBetween(400, 499);
|
|
559
547
|
}),
|
|
560
548
|
audit('SHOULD use 400 status code on JSON parsing failure when accepting application/graphql-response+json', async () => {
|
|
561
549
|
const res = await fetchFn(await getUrl(opts.url), {
|
|
@@ -566,7 +554,7 @@ export function serverAudits(opts) {
|
|
|
566
554
|
},
|
|
567
555
|
body: '{ "not a JSON',
|
|
568
556
|
});
|
|
569
|
-
|
|
557
|
+
ressert(res).status.toBe(400);
|
|
570
558
|
}),
|
|
571
559
|
audit('SHOULD not contain the data entry on JSON parsing failure when accepting application/graphql-response+json', async () => {
|
|
572
560
|
const res = await fetchFn(await getUrl(opts.url), {
|
|
@@ -577,7 +565,7 @@ export function serverAudits(opts) {
|
|
|
577
565
|
},
|
|
578
566
|
body: '{ "not a JSON',
|
|
579
567
|
});
|
|
580
|
-
|
|
568
|
+
await ressert(res).bodyAsExecutionResult.data.toBe(undefined);
|
|
581
569
|
}),
|
|
582
570
|
audit(
|
|
583
571
|
// TODO: convert to MUST after watershed
|
|
@@ -592,7 +580,7 @@ export function serverAudits(opts) {
|
|
|
592
580
|
qeury /* typo */: '{ __typename }',
|
|
593
581
|
}),
|
|
594
582
|
});
|
|
595
|
-
|
|
583
|
+
ressert(res).status.toBeBetween(400, 599);
|
|
596
584
|
}),
|
|
597
585
|
audit('SHOULD use 400 status code if parameters are invalid when accepting application/graphql-response+json', async () => {
|
|
598
586
|
const res = await fetchFn(await getUrl(opts.url), {
|
|
@@ -605,7 +593,7 @@ export function serverAudits(opts) {
|
|
|
605
593
|
qeury: /* typo */ '{ __typename }',
|
|
606
594
|
}),
|
|
607
595
|
});
|
|
608
|
-
|
|
596
|
+
ressert(res).status.toBe(400);
|
|
609
597
|
}),
|
|
610
598
|
audit('SHOULD not contain the data entry if parameters are invalid when accepting application/graphql-response+json', async () => {
|
|
611
599
|
const res = await fetchFn(await getUrl(opts.url), {
|
|
@@ -618,7 +606,7 @@ export function serverAudits(opts) {
|
|
|
618
606
|
qeury: /* typo */ '{ __typename }',
|
|
619
607
|
}),
|
|
620
608
|
});
|
|
621
|
-
|
|
609
|
+
await ressert(res).bodyAsExecutionResult.data.toBe(undefined);
|
|
622
610
|
}),
|
|
623
611
|
audit(
|
|
624
612
|
// TODO: convert to MUST after watershed
|
|
@@ -633,7 +621,7 @@ export function serverAudits(opts) {
|
|
|
633
621
|
query: '{',
|
|
634
622
|
}),
|
|
635
623
|
});
|
|
636
|
-
|
|
624
|
+
ressert(res).status.toBeBetween(400, 599);
|
|
637
625
|
}),
|
|
638
626
|
audit('SHOULD use 400 status code on document parsing failure when accepting application/graphql-response+json', async () => {
|
|
639
627
|
const res = await fetchFn(await getUrl(opts.url), {
|
|
@@ -646,7 +634,7 @@ export function serverAudits(opts) {
|
|
|
646
634
|
query: '{',
|
|
647
635
|
}),
|
|
648
636
|
});
|
|
649
|
-
|
|
637
|
+
ressert(res).status.toBe(400);
|
|
650
638
|
}),
|
|
651
639
|
audit('SHOULD not contain the data entry on document parsing failure when accepting application/graphql-response+json', async () => {
|
|
652
640
|
const res = await fetchFn(await getUrl(opts.url), {
|
|
@@ -659,7 +647,7 @@ export function serverAudits(opts) {
|
|
|
659
647
|
query: '{',
|
|
660
648
|
}),
|
|
661
649
|
});
|
|
662
|
-
|
|
650
|
+
await ressert(res).bodyAsExecutionResult.data.toBe(undefined);
|
|
663
651
|
}),
|
|
664
652
|
audit(
|
|
665
653
|
// TODO: convert to MUST after watershed
|
|
@@ -674,7 +662,7 @@ export function serverAudits(opts) {
|
|
|
674
662
|
query: '{ 8f31403dfe404bccbb0e835f2629c6a7 }', // making sure the field doesnt exist
|
|
675
663
|
}),
|
|
676
664
|
});
|
|
677
|
-
|
|
665
|
+
ressert(res).status.toBeBetween(400, 599);
|
|
678
666
|
}),
|
|
679
667
|
audit('SHOULD use 400 status code on document validation failure when accepting application/graphql-response+json', async () => {
|
|
680
668
|
const res = await fetchFn(await getUrl(opts.url), {
|
|
@@ -687,7 +675,7 @@ export function serverAudits(opts) {
|
|
|
687
675
|
query: '{ 8f31403dfe404bccbb0e835f2629c6a7 }', // making sure the field doesnt exist
|
|
688
676
|
}),
|
|
689
677
|
});
|
|
690
|
-
|
|
678
|
+
ressert(res).status.toBe(400);
|
|
691
679
|
}),
|
|
692
680
|
audit('SHOULD not contain the data entry on document validation failure when accepting application/graphql-response+json', async () => {
|
|
693
681
|
const res = await fetchFn(await getUrl(opts.url), {
|
|
@@ -700,7 +688,7 @@ export function serverAudits(opts) {
|
|
|
700
688
|
query: '{ 8f31403dfe404bccbb0e835f2629c6a7 }', // making sure the field doesnt exist
|
|
701
689
|
}),
|
|
702
690
|
});
|
|
703
|
-
|
|
691
|
+
await ressert(res).bodyAsExecutionResult.data.toBe(undefined);
|
|
704
692
|
}),
|
|
705
693
|
// TODO: how to fail and have the data entry?
|
|
706
694
|
// audit('MUST use 2xx status code if response contains the data entry and it is not null when accepting application/graphql-response+json'),
|
package/lib/audits/utils.d.mts
CHANGED
|
@@ -13,25 +13,44 @@ export * from '../utils';
|
|
|
13
13
|
*/
|
|
14
14
|
export declare function audit(name: AuditName, fn: () => Promise<void>): Audit;
|
|
15
15
|
/**
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
* All fatal problems will throw an instance of Error.
|
|
16
|
+
* Error thrown when an assertion test fails.
|
|
19
17
|
*
|
|
20
18
|
* @private
|
|
21
19
|
*/
|
|
22
|
-
export declare
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
20
|
+
export declare class AuditError {
|
|
21
|
+
/**
|
|
22
|
+
* Response from the server.
|
|
23
|
+
*/
|
|
24
|
+
response: Response;
|
|
25
|
+
/**
|
|
26
|
+
* Reason for the failing audit.
|
|
27
|
+
*/
|
|
28
|
+
reason: string;
|
|
29
|
+
constructor(response: Response, reason: string);
|
|
30
|
+
}
|
|
30
31
|
/**
|
|
31
|
-
*
|
|
32
|
+
* Will throw an AuditError if the assertion on Response fails.
|
|
33
|
+
*
|
|
34
|
+
* All fatal problems will throw an instance of an Error.
|
|
32
35
|
*
|
|
33
|
-
*
|
|
36
|
+
* The name "ressert" is a wordplay combining "response" and "assert".
|
|
34
37
|
*
|
|
35
38
|
* @private
|
|
36
|
-
|
|
37
|
-
export declare function
|
|
39
|
+
*/
|
|
40
|
+
export declare function ressert(res: Response): {
|
|
41
|
+
status: {
|
|
42
|
+
toBe(code: number): void;
|
|
43
|
+
toBeBetween: (min: number, max: number) => void;
|
|
44
|
+
};
|
|
45
|
+
header(key: 'content-type'): {
|
|
46
|
+
toContain(part: string): void;
|
|
47
|
+
notToContain(part: string): void;
|
|
48
|
+
};
|
|
49
|
+
bodyAsExecutionResult: {
|
|
50
|
+
data: {
|
|
51
|
+
toBe(val: ExecutionResult['data']): Promise<void>;
|
|
52
|
+
};
|
|
53
|
+
toHaveProperty(key: keyof ExecutionResult): Promise<void>;
|
|
54
|
+
notToHaveProperty(key: keyof ExecutionResult): Promise<void>;
|
|
55
|
+
};
|
|
56
|
+
};
|
package/lib/audits/utils.d.ts
CHANGED
|
@@ -13,25 +13,44 @@ export * from '../utils';
|
|
|
13
13
|
*/
|
|
14
14
|
export declare function audit(name: AuditName, fn: () => Promise<void>): Audit;
|
|
15
15
|
/**
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
* All fatal problems will throw an instance of Error.
|
|
16
|
+
* Error thrown when an assertion test fails.
|
|
19
17
|
*
|
|
20
18
|
* @private
|
|
21
19
|
*/
|
|
22
|
-
export declare
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
20
|
+
export declare class AuditError {
|
|
21
|
+
/**
|
|
22
|
+
* Response from the server.
|
|
23
|
+
*/
|
|
24
|
+
response: Response;
|
|
25
|
+
/**
|
|
26
|
+
* Reason for the failing audit.
|
|
27
|
+
*/
|
|
28
|
+
reason: string;
|
|
29
|
+
constructor(response: Response, reason: string);
|
|
30
|
+
}
|
|
30
31
|
/**
|
|
31
|
-
*
|
|
32
|
+
* Will throw an AuditError if the assertion on Response fails.
|
|
33
|
+
*
|
|
34
|
+
* All fatal problems will throw an instance of an Error.
|
|
32
35
|
*
|
|
33
|
-
*
|
|
36
|
+
* The name "ressert" is a wordplay combining "response" and "assert".
|
|
34
37
|
*
|
|
35
38
|
* @private
|
|
36
|
-
|
|
37
|
-
export declare function
|
|
39
|
+
*/
|
|
40
|
+
export declare function ressert(res: Response): {
|
|
41
|
+
status: {
|
|
42
|
+
toBe(code: number): void;
|
|
43
|
+
toBeBetween: (min: number, max: number) => void;
|
|
44
|
+
};
|
|
45
|
+
header(key: 'content-type'): {
|
|
46
|
+
toContain(part: string): void;
|
|
47
|
+
notToContain(part: string): void;
|
|
48
|
+
};
|
|
49
|
+
bodyAsExecutionResult: {
|
|
50
|
+
data: {
|
|
51
|
+
toBe(val: ExecutionResult['data']): Promise<void>;
|
|
52
|
+
};
|
|
53
|
+
toHaveProperty(key: keyof ExecutionResult): Promise<void>;
|
|
54
|
+
notToHaveProperty(key: keyof ExecutionResult): Promise<void>;
|
|
55
|
+
};
|
|
56
|
+
};
|