graphql-http 1.6.0 → 1.6.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/README.md CHANGED
@@ -739,6 +739,10 @@ for (const audit of serverAudits({
739
739
 
740
740
  Check the [docs folder](docs/) out for [TypeDoc](https://typedoc.org) generated documentation.
741
741
 
742
+ ## [Audits](implementations/)
743
+
744
+ Inspect audits of other implementations in the [implementations folder](implementations/). Adding your implementation is very welcome!
745
+
742
746
  ## [Want to help?](CONTRIBUTING.md)
743
747
 
744
748
  File a bug, contribute with code, or improve documentation? Read up on our guidelines for [contributing](CONTRIBUTING.md) and drive development with `yarn test --watch` away!
@@ -0,0 +1,58 @@
1
+ /**
2
+ *
3
+ * audit/common
4
+ *
5
+ */
6
+ /**
7
+ * Audit requirement levels as per [RFC2119](https://www.rfc-editor.org/rfc/rfc2119).
8
+ *
9
+ * @category Audits
10
+ */
11
+ export declare type AuditRequirement = 'MUST' | 'SHOULD' | 'MAY';
12
+ /**
13
+ * Audit name starting with the audit requirement level.
14
+ *
15
+ * @category Audits
16
+ */
17
+ export declare type AuditName = `${AuditRequirement} ${string}`;
18
+ /**
19
+ * Actual audit test returning an result.
20
+ *
21
+ * The test function will throw only if the error is fatal.
22
+ *
23
+ * @category Audits
24
+ */
25
+ export interface Audit {
26
+ name: AuditName;
27
+ fn: () => Promise<AuditResult>;
28
+ }
29
+ /**
30
+ * Indicates that the audit was successful.
31
+ *
32
+ * @category Audits
33
+ */
34
+ export interface AuditOk {
35
+ name: AuditName;
36
+ status: 'ok';
37
+ }
38
+ /**
39
+ * Indicates that the audit failed.
40
+ *
41
+ * If the status is `warn`, the audit is not a requirement but rather a recommendation.
42
+ *
43
+ * On the other hand, if the status is `error`, the audit is a requirement and the source
44
+ * is therefore not compliant.
45
+ *
46
+ * @category Audits
47
+ */
48
+ export interface AuditFail {
49
+ name: AuditName;
50
+ status: 'warn' | 'error';
51
+ reason: string;
52
+ }
53
+ /**
54
+ * Result of the performed audit. See `AuditOk` and `AuditFail` for more information.
55
+ *
56
+ * @category Audits
57
+ */
58
+ export declare type AuditResult = AuditOk | AuditFail;
@@ -0,0 +1,2 @@
1
+ export * from './common';
2
+ export * from './server';
@@ -0,0 +1,39 @@
1
+ /**
2
+ *
3
+ * audit/server
4
+ *
5
+ */
6
+ import { Audit, AuditResult } from './common';
7
+ /**
8
+ * Options for server audits required to check GraphQL over HTTP spec conformance.
9
+ *
10
+ * @category Audits
11
+ */
12
+ export interface ServerAuditOptions {
13
+ /**
14
+ * The URL of the GraphQL server for the audit.
15
+ */
16
+ url: string;
17
+ /**
18
+ * The Fetch function to use.
19
+ *
20
+ * For NodeJS environments consider using [`@whatwg-node/fetch`](https://github.com/ardatan/whatwg-node/tree/master/packages/fetch).
21
+ *
22
+ * @default global.fetch
23
+ */
24
+ fetchFn?: unknown;
25
+ }
26
+ /**
27
+ * List of server audits required to check GraphQL over HTTP spec conformance.
28
+ *
29
+ * @category Audits
30
+ */
31
+ export declare function serverAudits(opts: ServerAuditOptions): Audit[];
32
+ /**
33
+ * Performs the full list of server audits required for GraphQL over HTTP spec conformance.
34
+ *
35
+ * Please consult the `AuditResult` for more information.
36
+ *
37
+ * @category Audits
38
+ */
39
+ export declare function auditServer(opts: ServerAuditOptions): Promise<AuditResult[]>;
@@ -16,7 +16,9 @@ function serverAudits(opts) {
16
16
  const fetchFn = (opts.fetchFn || fetch);
17
17
  return [
18
18
  // Media Types
19
- (0, utils_1.audit)('MUST accept application/graphql-response+json and match the content-type', async () => {
19
+ (0, utils_1.audit)(
20
+ // TODO: convert to MUST after watershed
21
+ 'SHOULD accept application/graphql-response+json and match the content-type', async () => {
20
22
  const url = new URL(opts.url);
21
23
  url.searchParams.set('query', '{ __typename }');
22
24
  const res = await fetchFn(url.toString(), {
@@ -38,7 +40,9 @@ function serverAudits(opts) {
38
40
  (0, utils_1.assert)('Status code', res.status).toBe(200);
39
41
  (0, utils_1.assert)('Content-Type header', res.headers.get('content-type')).toContain('application/json');
40
42
  }),
41
- (0, utils_1.audit)('MUST accept */* and use application/graphql-response+json for the content-type', async () => {
43
+ (0, utils_1.audit)(
44
+ // TODO: convert to MUST after watershed
45
+ 'SHOULD accept */* and use application/graphql-response+json for the content-type', async () => {
42
46
  const url = new URL(opts.url);
43
47
  url.searchParams.set('query', '{ __typename }');
44
48
  const res = await fetchFn(url.toString(), {
@@ -49,19 +53,39 @@ function serverAudits(opts) {
49
53
  (0, utils_1.assert)('Status code', res.status).toBe(200);
50
54
  (0, utils_1.assert)('Content-Type header', res.headers.get('content-type')).toContain('application/graphql-response+json');
51
55
  }),
52
- (0, utils_1.audit)('MUST assume application/graphql-response+json content-type when accept is missing', async () => {
56
+ (0, utils_1.audit)(
57
+ // TODO: convert to MUST after watershed
58
+ 'SHOULD assume application/graphql-response+json content-type when accept is missing', async () => {
53
59
  const url = new URL(opts.url);
54
60
  url.searchParams.set('query', '{ __typename }');
55
61
  const res = await fetchFn(url.toString());
56
62
  (0, utils_1.assert)('Status code', res.status).toBe(200);
57
63
  (0, utils_1.assert)('Content-Type header', res.headers.get('content-type')).toContain('application/graphql-response+json');
58
64
  }),
59
- (0, utils_1.audit)('MUST use utf-8 charset in response', async () => {
65
+ (0, utils_1.audit)('MUST use utf-8 encoding when responding', async () => {
60
66
  const url = new URL(opts.url);
61
67
  url.searchParams.set('query', '{ __typename }');
62
68
  const res = await fetchFn(url.toString());
63
69
  (0, utils_1.assert)('Status code', res.status).toBe(200);
64
- (0, utils_1.assert)('Content-Type header', res.headers.get('content-type')).toContain('charset=utf-8');
70
+ // has charset set to utf-8
71
+ try {
72
+ (0, utils_1.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
+ (0, utils_1.assert)('Content-Type header', res.headers.get('content-type')).notToContain('charset');
80
+ // and the content is utf-8 encoded
81
+ try {
82
+ const decoder = new TextDecoder('utf-8');
83
+ const decoded = decoder.decode(await res.arrayBuffer());
84
+ (0, utils_1.assert)('UTF-8 decoded body', decoded).toBe('{"data":{"__typename":"Query"}}');
85
+ }
86
+ catch (_b) {
87
+ throw 'Body is not UTF-8 encoded';
88
+ }
65
89
  }),
66
90
  (0, utils_1.audit)('MUST accept only utf-8 charset', async () => {
67
91
  const url = new URL(opts.url);
@@ -71,8 +95,16 @@ function serverAudits(opts) {
71
95
  accept: 'application/graphql-response+json; charset=iso-8859-1',
72
96
  },
73
97
  });
74
- (0, utils_1.assert)('Status code', res.status).toBe(406);
75
- (0, utils_1.assert)('Accept header', res.headers.get('accept')).toContain('charset=utf-8');
98
+ // application/json is 200 + errors
99
+ const contentType = res.headers.get('content-type') || '';
100
+ if (contentType.includes('application/json')) {
101
+ (0, utils_1.assert)(`Content-Type ${contentType} status code`, res.status).toBe(200);
102
+ (0, utils_1.assert)('Execution result', await (0, utils_1.assertBodyAsExecutionResult)(res)).toHaveProperty('errors');
103
+ return;
104
+ }
105
+ // other content-types must be 4xx
106
+ (0, utils_1.assert)(`Content-Type ${contentType} status code`, res.status).toBeGreaterThanOrEqual(400);
107
+ (0, utils_1.assert)(`Content-Type ${contentType} status code`, res.status).toBeLessThanOrEqual(499);
76
108
  }),
77
109
  // Request
78
110
  (0, utils_1.audit)('MUST accept POST requests', async () => {
@@ -106,7 +138,7 @@ function serverAudits(opts) {
106
138
  method: 'POST',
107
139
  });
108
140
  (0, utils_1.assert)('Status code', res.status).toBeGreaterThanOrEqual(400);
109
- (0, utils_1.assert)('Status code', res.status).toBeLessThanOrEqual(599);
141
+ (0, utils_1.assert)('Status code', res.status).toBeLessThanOrEqual(499);
110
142
  }),
111
143
  (0, utils_1.audit)('MUST accept application/json POST requests', async () => {
112
144
  const res = await fetchFn(opts.url, {
@@ -124,7 +156,9 @@ function serverAudits(opts) {
124
156
  (0, utils_1.assert)('Status code', res.status).toBe(400);
125
157
  }),
126
158
  // Request Parameters
127
- (0, utils_1.audit)('MUST require the {query} parameter', async () => {
159
+ (0, utils_1.audit)(
160
+ // TODO: convert to MUST after watershed
161
+ 'SHOULD use 400 status code on missing {query} parameter when accepting application/graphql-response+json', async () => {
128
162
  const res = await fetchFn(opts.url, {
129
163
  method: 'POST',
130
164
  headers: {
@@ -135,7 +169,21 @@ function serverAudits(opts) {
135
169
  });
136
170
  (0, utils_1.assert)('Status code', res.status).toBe(400);
137
171
  }),
138
- ...[{ obj: 'ect' }, 0, false, ['array']].map((invalid) => (0, utils_1.audit)(`MUST NOT allow ${(0, utils_1.extendedTypeof)(invalid)} for the {query} parameter`, async () => {
172
+ (0, utils_1.audit)('SHOULD use 200 status code with errors field on missing {query} parameter when accepting application/json', async () => {
173
+ const res = await fetchFn(opts.url, {
174
+ method: 'POST',
175
+ headers: {
176
+ 'content-type': 'application/json',
177
+ accept: 'application/json',
178
+ },
179
+ body: JSON.stringify({ notquery: '{ __typename }' }),
180
+ });
181
+ (0, utils_1.assert)('Status code', res.status).toBe(200);
182
+ (0, utils_1.assert)('Execution result', await (0, utils_1.assertBodyAsExecutionResult)(res)).toHaveProperty('errors');
183
+ }),
184
+ ...[{ obj: 'ect' }, 0, false, ['array']].map((invalid) => (0, utils_1.audit)(
185
+ // TODO: convert to MUST after watershed
186
+ `SHOULD use 400 status code on ${(0, utils_1.extendedTypeof)(invalid)} {query} parameter when accepting application/graphql-response+json`, async () => {
139
187
  const res = await fetchFn(opts.url, {
140
188
  method: 'POST',
141
189
  headers: {
@@ -148,7 +196,23 @@ function serverAudits(opts) {
148
196
  });
149
197
  (0, utils_1.assert)('Status code', res.status).toBe(400);
150
198
  })),
151
- (0, utils_1.audit)('MUST accept a string for the {query} parameter', async () => {
199
+ ...[{ obj: 'ect' }, 0, false, ['array']].map((invalid) => (0, utils_1.audit)(`SHOULD use 200 status code with errors field on ${(0, utils_1.extendedTypeof)(invalid)} {query} parameter when accepting application/json`, async () => {
200
+ const res = await fetchFn(opts.url, {
201
+ method: 'POST',
202
+ headers: {
203
+ 'content-type': 'application/json',
204
+ accept: 'application/json',
205
+ },
206
+ body: JSON.stringify({
207
+ query: invalid,
208
+ }),
209
+ });
210
+ (0, utils_1.assert)('Status code', res.status).toBe(200);
211
+ (0, utils_1.assert)('Execution result', await (0, utils_1.assertBodyAsExecutionResult)(res)).toHaveProperty('errors');
212
+ })),
213
+ (0, utils_1.audit)(
214
+ // TODO: convert to MUST after watershed
215
+ 'SHOULD allow string {query} parameter when accepting application/graphql-response+json', async () => {
152
216
  const res = await fetchFn(opts.url, {
153
217
  method: 'POST',
154
218
  headers: {
@@ -161,7 +225,23 @@ function serverAudits(opts) {
161
225
  });
162
226
  (0, utils_1.assert)('Status code', res.status).toBe(200);
163
227
  }),
164
- ...[{ obj: 'ect' }, 0, false, ['array']].map((invalid) => (0, utils_1.audit)(`MUST NOT allow ${(0, utils_1.extendedTypeof)(invalid)} for the {operationName} parameter`, async () => {
228
+ (0, utils_1.audit)('MUST allow string {query} parameter when accepting application/json', async () => {
229
+ const res = await fetchFn(opts.url, {
230
+ method: 'POST',
231
+ headers: {
232
+ 'content-type': 'application/json',
233
+ accept: 'application/json',
234
+ },
235
+ body: JSON.stringify({
236
+ query: '{ __typename }',
237
+ }),
238
+ });
239
+ (0, utils_1.assert)('Status code', res.status).toBe(200);
240
+ (0, utils_1.assert)('Execution result', await (0, utils_1.assertBodyAsExecutionResult)(res)).notToHaveProperty('errors');
241
+ }),
242
+ ...[{ obj: 'ect' }, 0, false, ['array']].map((invalid) => (0, utils_1.audit)(
243
+ // TODO: convert to MUST after watershed
244
+ `SHOULD use 400 status code on ${(0, utils_1.extendedTypeof)(invalid)} {operationName} parameter when accepting application/graphql-response+json`, async () => {
165
245
  const res = await fetchFn(opts.url, {
166
246
  method: 'POST',
167
247
  headers: {
@@ -175,7 +255,24 @@ function serverAudits(opts) {
175
255
  });
176
256
  (0, utils_1.assert)('Status code', res.status).toBe(400);
177
257
  })),
178
- (0, utils_1.audit)('MUST accept a string for the {operationName} parameter', async () => {
258
+ ...[{ obj: 'ect' }, 0, false, ['array']].map((invalid) => (0, utils_1.audit)(`SHOULD use 200 status code with errors field on ${(0, utils_1.extendedTypeof)(invalid)} {operationName} parameter when accepting application/json`, async () => {
259
+ const res = await fetchFn(opts.url, {
260
+ method: 'POST',
261
+ headers: {
262
+ 'content-type': 'application/json',
263
+ accept: 'application/json',
264
+ },
265
+ body: JSON.stringify({
266
+ operationName: invalid,
267
+ query: '{ __typename }',
268
+ }),
269
+ });
270
+ (0, utils_1.assert)('Status code', res.status).toBe(200);
271
+ (0, utils_1.assert)('Execution result', await (0, utils_1.assertBodyAsExecutionResult)(res)).toHaveProperty('errors');
272
+ })),
273
+ (0, utils_1.audit)(
274
+ // TODO: convert to MUST after watershed
275
+ 'SHOULD allow string {operationName} parameter when accepting application/graphql-response+json', async () => {
179
276
  const res = await fetchFn(opts.url, {
180
277
  method: 'POST',
181
278
  headers: {
@@ -189,7 +286,24 @@ function serverAudits(opts) {
189
286
  });
190
287
  (0, utils_1.assert)('Status code', res.status).toBe(200);
191
288
  }),
192
- ...['string', 0, false, ['array']].map((invalid) => (0, utils_1.audit)(`MUST NOT allow ${(0, utils_1.extendedTypeof)(invalid)} for the {variables} parameter`, async () => {
289
+ (0, utils_1.audit)('MUST allow string {operationName} parameter when accepting application/json', async () => {
290
+ const res = await fetchFn(opts.url, {
291
+ method: 'POST',
292
+ headers: {
293
+ 'content-type': 'application/json',
294
+ accept: 'application/json',
295
+ },
296
+ body: JSON.stringify({
297
+ operationName: 'Query',
298
+ query: 'query Query { __typename }',
299
+ }),
300
+ });
301
+ (0, utils_1.assert)('Status code', res.status).toBe(200);
302
+ (0, utils_1.assert)('Execution result', await (0, utils_1.assertBodyAsExecutionResult)(res)).notToHaveProperty('errors');
303
+ }),
304
+ ...['string', 0, false, ['array']].map((invalid) => (0, utils_1.audit)(
305
+ // TODO: convert to MUST after watershed
306
+ `SHOULD use 400 status code on ${(0, utils_1.extendedTypeof)(invalid)} {variables} parameter when accepting application/graphql-response+json`, async () => {
193
307
  const res = await fetchFn(opts.url, {
194
308
  method: 'POST',
195
309
  headers: {
@@ -203,7 +317,24 @@ function serverAudits(opts) {
203
317
  });
204
318
  (0, utils_1.assert)('Status code', res.status).toBe(400);
205
319
  })),
206
- (0, utils_1.audit)('MUST accept a map for the {variables} parameter', async () => {
320
+ ...['string', 0, false, ['array']].map((invalid) => (0, utils_1.audit)(`SHOULD use 200 status code with errors field on ${(0, utils_1.extendedTypeof)(invalid)} {variables} parameter when accepting application/json`, async () => {
321
+ const res = await fetchFn(opts.url, {
322
+ method: 'POST',
323
+ headers: {
324
+ 'content-type': 'application/json',
325
+ accept: 'application/json',
326
+ },
327
+ body: JSON.stringify({
328
+ query: '{ __typename }',
329
+ variables: invalid,
330
+ }),
331
+ });
332
+ (0, utils_1.assert)('Status code', res.status).toBe(200);
333
+ (0, utils_1.assert)('Execution result', await (0, utils_1.assertBodyAsExecutionResult)(res)).toHaveProperty('errors');
334
+ })),
335
+ (0, utils_1.audit)(
336
+ // TODO: convert to MUST after watershed
337
+ 'SHOULD allow map {variables} parameter when accepting application/graphql-response+json', async () => {
207
338
  const res = await fetchFn(opts.url, {
208
339
  method: 'POST',
209
340
  headers: {
@@ -216,21 +347,52 @@ function serverAudits(opts) {
216
347
  }),
217
348
  });
218
349
  (0, utils_1.assert)('Status code', res.status).toBe(200);
219
- const result = (await res.json());
220
- (0, utils_1.assert)('Execution result', result).notToHaveProperty('errors');
221
350
  }),
222
- (0, utils_1.audit)('MUST accept a URL-encoded JSON string for the {variables} parameter in GETs', async () => {
351
+ (0, utils_1.audit)('MUST allow map {variables} parameter when accepting application/json', async () => {
352
+ const res = await fetchFn(opts.url, {
353
+ method: 'POST',
354
+ headers: {
355
+ 'content-type': 'application/json',
356
+ accept: 'application/json',
357
+ },
358
+ body: JSON.stringify({
359
+ query: 'query Type($name: String!) { __type(name: $name) { name } }',
360
+ variables: { name: 'sometype' },
361
+ }),
362
+ });
363
+ (0, utils_1.assert)('Status code', res.status).toBe(200);
364
+ (0, utils_1.assert)('Execution result', await (0, utils_1.assertBodyAsExecutionResult)(res)).notToHaveProperty('errors');
365
+ }),
366
+ (0, utils_1.audit)(
367
+ // TODO: convert to MUST after watershed
368
+ 'SHOULD allow URL-encoded JSON string {variables} parameter in GETs when accepting application/graphql-response+json', async () => {
223
369
  const url = new URL(opts.url);
224
370
  url.searchParams.set('query', 'query Type($name: String!) { __type(name: $name) { name } }');
225
371
  url.searchParams.set('variables', JSON.stringify({ name: 'sometype' }));
226
372
  const res = await fetchFn(url.toString(), {
227
373
  method: 'GET',
374
+ headers: {
375
+ accept: 'application/graphql-response+json',
376
+ },
228
377
  });
229
378
  (0, utils_1.assert)('Status code', res.status).toBe(200);
230
- const result = (await res.json());
231
- (0, utils_1.assert)('Execution result', result).notToHaveProperty('errors');
232
379
  }),
233
- ...['string', 0, false, ['array']].map((invalid) => (0, utils_1.audit)(`MUST NOT allow ${(0, utils_1.extendedTypeof)(invalid)} for the {extensions} parameter`, async () => {
380
+ (0, utils_1.audit)('MUST allow URL-encoded JSON string {variables} parameter in GETs when accepting application/json', async () => {
381
+ const url = new URL(opts.url);
382
+ url.searchParams.set('query', 'query Type($name: String!) { __type(name: $name) { name } }');
383
+ url.searchParams.set('variables', JSON.stringify({ name: 'sometype' }));
384
+ const res = await fetchFn(url.toString(), {
385
+ method: 'GET',
386
+ headers: {
387
+ accept: 'application/json',
388
+ },
389
+ });
390
+ (0, utils_1.assert)('Status code', res.status).toBe(200);
391
+ (0, utils_1.assert)('Execution result', await (0, utils_1.assertBodyAsExecutionResult)(res)).notToHaveProperty('errors');
392
+ }),
393
+ ...['string', 0, false, ['array']].map((invalid) => (0, utils_1.audit)(
394
+ // TODO: convert to MUST after watershed
395
+ `SHOULD use 400 status code on ${(0, utils_1.extendedTypeof)(invalid)} {extensions} parameter when accepting application/graphql-response+json`, async () => {
234
396
  const res = await fetchFn(opts.url, {
235
397
  method: 'POST',
236
398
  headers: {
@@ -244,7 +406,24 @@ function serverAudits(opts) {
244
406
  });
245
407
  (0, utils_1.assert)('Status code', res.status).toBe(400);
246
408
  })),
247
- (0, utils_1.audit)('MUST accept a map for the {extensions} parameter', async () => {
409
+ ...['string', 0, false, ['array']].map((invalid) => (0, utils_1.audit)(`SHOULD use 200 status code with errors field on ${(0, utils_1.extendedTypeof)(invalid)} {extensions} parameter when accepting application/json`, async () => {
410
+ const res = await fetchFn(opts.url, {
411
+ method: 'POST',
412
+ headers: {
413
+ 'content-type': 'application/json',
414
+ accept: 'application/json',
415
+ },
416
+ body: JSON.stringify({
417
+ query: '{ __typename }',
418
+ extensions: invalid,
419
+ }),
420
+ });
421
+ (0, utils_1.assert)('Status code', res.status).toBe(200);
422
+ (0, utils_1.assert)('Execution result', await (0, utils_1.assertBodyAsExecutionResult)(res)).toHaveProperty('errors');
423
+ })),
424
+ (0, utils_1.audit)(
425
+ // TODO: convert to MUST after watershed
426
+ 'SHOULD allow map {extensions} parameter when accepting application/graphql-response+json', async () => {
248
427
  const res = await fetchFn(opts.url, {
249
428
  method: 'POST',
250
429
  headers: {
@@ -258,6 +437,21 @@ function serverAudits(opts) {
258
437
  });
259
438
  (0, utils_1.assert)('Status code', res.status).toBe(200);
260
439
  }),
440
+ (0, utils_1.audit)('MUST allow map {extensions} parameter when accepting application/json', async () => {
441
+ const res = await fetchFn(opts.url, {
442
+ method: 'POST',
443
+ headers: {
444
+ 'content-type': 'application/json',
445
+ accept: 'application/json',
446
+ },
447
+ body: JSON.stringify({
448
+ query: '{ __typename }',
449
+ extensions: { some: 'value' },
450
+ }),
451
+ });
452
+ (0, utils_1.assert)('Status code', res.status).toBe(200);
453
+ (0, utils_1.assert)('Execution result', await (0, utils_1.assertBodyAsExecutionResult)(res)).notToHaveProperty('errors');
454
+ }),
261
455
  // TODO: audit('MUST accept a map for the {extensions} parameter'),
262
456
  // Response application/json
263
457
  (0, utils_1.audit)('SHOULD use 200 status code on JSON parsing failure when accepting application/json', async () => {
@@ -299,7 +493,9 @@ function serverAudits(opts) {
299
493
  (0, utils_1.assert)('Status code', res.status).toBe(200);
300
494
  }),
301
495
  // Response application/graphql-response+json
302
- (0, utils_1.audit)('MUST use 4xx or 5xx status codes on JSON parsing failure when accepting application/graphql-response+json', async () => {
496
+ (0, utils_1.audit)(
497
+ // TODO: convert to MUST after watershed
498
+ 'SHOULD use 4xx or 5xx status codes on JSON parsing failure when accepting application/graphql-response+json', async () => {
303
499
  const res = await fetchFn(opts.url, {
304
500
  method: 'POST',
305
501
  headers: {
@@ -331,9 +527,11 @@ function serverAudits(opts) {
331
527
  },
332
528
  body: '{ "not a JSON',
333
529
  });
334
- (0, utils_1.assert)('Data entry', (await res.json()).data).toBe(undefined);
530
+ (0, utils_1.assert)('Data entry', (await (0, utils_1.assertBodyAsExecutionResult)(res)).data).toBe(undefined);
335
531
  }),
336
- (0, utils_1.audit)('MUST use 4xx or 5xx status codes if parameters are invalid when accepting application/graphql-response+json', async () => {
532
+ (0, utils_1.audit)(
533
+ // TODO: convert to MUST after watershed
534
+ 'SHOULD use 4xx or 5xx status codes if parameters are invalid when accepting application/graphql-response+json', async () => {
337
535
  const url = new URL(opts.url);
338
536
  url.searchParams.set('qeury' /* typo */, '{ __typename }');
339
537
  const res = await fetchFn(url.toString(), {
@@ -359,9 +557,11 @@ function serverAudits(opts) {
359
557
  method: 'GET',
360
558
  headers: { accept: 'application/graphql-response+json' },
361
559
  });
362
- (0, utils_1.assert)('Data entry', (await res.json()).data).toBe(undefined);
560
+ (0, utils_1.assert)('Data entry', (await (0, utils_1.assertBodyAsExecutionResult)(res)).data).toBe(undefined);
363
561
  }),
364
- (0, utils_1.audit)('MUST use 4xx or 5xx status codes on document parsing failure when accepting application/graphql-response+json', async () => {
562
+ (0, utils_1.audit)(
563
+ // TODO: convert to MUST after watershed
564
+ 'SHOULD use 4xx or 5xx status codes on document parsing failure when accepting application/graphql-response+json', async () => {
365
565
  const url = new URL(opts.url);
366
566
  url.searchParams.set('query', '{');
367
567
  const res = await fetchFn(url.toString(), {
@@ -387,9 +587,11 @@ function serverAudits(opts) {
387
587
  method: 'GET',
388
588
  headers: { accept: 'application/graphql-response+json' },
389
589
  });
390
- (0, utils_1.assert)('Data entry', (await res.json()).data).toBe(undefined);
590
+ (0, utils_1.assert)('Data entry', (await (0, utils_1.assertBodyAsExecutionResult)(res)).data).toBe(undefined);
391
591
  }),
392
- (0, utils_1.audit)('MUST use 4xx or 5xx status codes on document validation failure when accepting application/graphql-response+json', async () => {
592
+ (0, utils_1.audit)(
593
+ // TODO: convert to MUST after watershed
594
+ 'SHOULD use 4xx or 5xx status codes on document validation failure when accepting application/graphql-response+json', async () => {
393
595
  const url = new URL(opts.url);
394
596
  url.searchParams.set('query', '{ 8f31403dfe404bccbb0e835f2629c6a7 }'); // making sure the field doesnt exist
395
597
  const res = await fetchFn(url.toString(), {
@@ -415,7 +617,7 @@ function serverAudits(opts) {
415
617
  method: 'GET',
416
618
  headers: { accept: 'application/graphql-response+json' },
417
619
  });
418
- (0, utils_1.assert)('Data entry', (await res.json()).data).toBe(undefined);
620
+ (0, utils_1.assert)('Data entry', (await (0, utils_1.assertBodyAsExecutionResult)(res)).data).toBe(undefined);
419
621
  }),
420
622
  // TODO: how to fail and have the data entry?
421
623
  // audit('MUST use 2xx status code if response contains the data entry and it is not null when accepting application/graphql-response+json'),