Skip to content

Commit bd5fc30

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 bd5fc30

File tree

3 files changed

+223
-99
lines changed

3 files changed

+223
-99
lines changed

src/execution/__tests__/lists-test.ts

+101-9
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,30 @@ 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+
if (index === 1) {
231+
throw new Error('also bad');
232+
}
233+
return Promise.resolve(index);
234+
}, true),
235+
).toDeepEqual({
236+
data: { listField: null },
237+
errors: [
238+
{
239+
message: 'also bad',
240+
locations: [{ line: 1, column: 15 }],
241+
path: ['listField', 1, 'index'],
242+
},
243+
],
244+
});
245+
});
246+
219247
it('Handles nulls yielded by async generator', async () => {
220248
async function* listField() {
221249
yield await Promise.resolve(1);
@@ -265,6 +293,11 @@ describe('Execute: Handles list nullability', () => {
265293
expectJSON(await executeQuery(promisify(listOfPromises))).toDeepEqual(
266294
result,
267295
);
296+
297+
// Test mix of synchronous and non-synchronous values
298+
const [first, ...rest] = listField;
299+
const listOfSomePromises = [first, ...rest.map(promisify)];
300+
expectJSON(await executeQuery(listOfSomePromises)).toDeepEqual(result);
268301
}
269302
return result;
270303

@@ -322,6 +355,32 @@ describe('Execute: Handles list nullability', () => {
322355
});
323356
});
324357

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

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

src/execution/__tests__/stream-test.ts

-5
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)