Skip to content

Commit 86106a7

Browse files
committed
fix: handle more unhandled promise rejections
including mixture of sync/async errors in lists following graphql#3706 fix for field execution
1 parent 5009d9f commit 86106a7

File tree

3 files changed

+220
-99
lines changed

3 files changed

+220
-99
lines changed

src/execution/__tests__/lists-test.ts

Lines changed: 98 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,17 @@ describe('Execute: Accepts async iterables as list value', () => {
8989

9090
function completeObjectList(
9191
resolve: GraphQLFieldResolver<{ index: number }, unknown>,
92+
nonNullable = false,
9293
): PromiseOrValue<ExecutionResult> {
94+
const ObjectWrapperType = new GraphQLObjectType({
95+
name: 'ObjectWrapper',
96+
fields: {
97+
index: {
98+
type: new GraphQLNonNull(GraphQLString),
99+
resolve,
100+
},
101+
},
102+
});
93103
const schema = new GraphQLSchema({
94104
query: new GraphQLObjectType({
95105
name: 'Query',
@@ -101,15 +111,9 @@ describe('Execute: Accepts async iterables as list value', () => {
101111
yield await Promise.resolve({ index: 2 });
102112
},
103113
type: new GraphQLList(
104-
new GraphQLObjectType({
105-
name: 'ObjectWrapper',
106-
fields: {
107-
index: {
108-
type: new GraphQLNonNull(GraphQLString),
109-
resolve,
110-
},
111-
},
112-
}),
114+
nonNullable
115+
? new GraphQLNonNull(ObjectWrapperType)
116+
: ObjectWrapperType,
113117
),
114118
},
115119
},
@@ -216,6 +220,27 @@ describe('Execute: Accepts async iterables as list value', () => {
216220
],
217221
});
218222
});
223+
224+
it('Handles mixture of synchronous and asynchronous errors from `completeValue` in AsyncIterables', async () => {
225+
expectJSON(
226+
await completeObjectList(({ index }) => {
227+
if (index === 0) {
228+
return Promise.reject(new Error('bad'));
229+
}
230+
throw new Error('also bad');
231+
}, true),
232+
).toDeepEqual({
233+
data: { listField: null },
234+
errors: [
235+
{
236+
message: 'also bad',
237+
locations: [{ line: 1, column: 15 }],
238+
path: ['listField', 1, 'index'],
239+
},
240+
],
241+
});
242+
});
243+
219244
it('Handles nulls yielded by async generator', async () => {
220245
async function* listField() {
221246
yield await Promise.resolve(1);
@@ -265,6 +290,11 @@ describe('Execute: Handles list nullability', () => {
265290
expectJSON(await executeQuery(promisify(listOfPromises))).toDeepEqual(
266291
result,
267292
);
293+
294+
// Test mix of synchronous and non-synchronous values
295+
const [first, ...rest] = listField;
296+
const listOfSomePromises = [first, ...rest.map(promisify)];
297+
expectJSON(await executeQuery(listOfSomePromises)).toDeepEqual(result);
268298
}
269299
return result;
270300

@@ -322,6 +352,32 @@ describe('Execute: Handles list nullability', () => {
322352
});
323353
});
324354

355+
it('Contains multiple nulls', async () => {
356+
const listField = [null, null, 2];
357+
const errors = [
358+
{
359+
message: 'Cannot return null for non-nullable field Query.listField.',
360+
locations: [{ line: 1, column: 3 }],
361+
path: ['listField', 0],
362+
},
363+
];
364+
365+
expect(await complete({ listField, as: '[Int]' })).to.deep.equal({
366+
data: { listField: [null, null, 2] },
367+
});
368+
expect(await complete({ listField, as: '[Int]!' })).to.deep.equal({
369+
data: { listField: [null, null, 2] },
370+
});
371+
expectJSON(await complete({ listField, as: '[Int!]' })).toDeepEqual({
372+
data: { listField: null },
373+
errors,
374+
});
375+
expectJSON(await complete({ listField, as: '[Int!]!' })).toDeepEqual({
376+
data: null,
377+
errors,
378+
});
379+
});
380+
325381
it('Returns null', async () => {
326382
const listField = null;
327383
const errors = [
@@ -376,6 +432,39 @@ describe('Execute: Handles list nullability', () => {
376432
});
377433
});
378434

435+
it('Contains multiple errors', async () => {
436+
const listField = [new Error('bad'), new Error('also bad'), 2];
437+
438+
const firstError = {
439+
message: 'bad',
440+
locations: [{ line: 1, column: 3 }],
441+
path: ['listField', 0],
442+
};
443+
444+
const secondError = {
445+
message: 'also bad',
446+
locations: [{ line: 1, column: 3 }],
447+
path: ['listField', 1],
448+
};
449+
450+
expectJSON(await complete({ listField, as: '[Int]' })).toDeepEqual({
451+
data: { listField: [null, null, 2] },
452+
errors: [firstError, secondError],
453+
});
454+
expectJSON(await complete({ listField, as: '[Int]!' })).toDeepEqual({
455+
data: { listField: [null, null, 2] },
456+
errors: [firstError, secondError],
457+
});
458+
expectJSON(await complete({ listField, as: '[Int!]' })).toDeepEqual({
459+
data: { listField: null },
460+
errors: [firstError],
461+
});
462+
expectJSON(await complete({ listField, as: '[Int!]!' })).toDeepEqual({
463+
data: null,
464+
errors: [firstError],
465+
});
466+
});
467+
379468
it('Results in error', async () => {
380469
const listField = new Error('bad');
381470
const errors = [

src/execution/__tests__/stream-test.ts

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -483,11 +483,6 @@ describe('Execute: stream directive', () => {
483483
},
484484
],
485485
},
486-
],
487-
hasNext: true,
488-
},
489-
{
490-
incremental: [
491486
{
492487
items: [{ name: 'Leia', id: '3' }],
493488
path: ['friendList', 2],

0 commit comments

Comments
 (0)