Skip to content

Commit 37c022d

Browse files
buildClientSchema: add missing tests for introspection validations (#1666)
1 parent 3fef0d4 commit 37c022d

File tree

1 file changed

+174
-29
lines changed

1 file changed

+174
-29
lines changed

src/utilities/__tests__/buildClientSchema-test.js

Lines changed: 174 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -490,46 +490,67 @@ describe('Type System: build schema from introspection', () => {
490490
const introspection = introspectionFromSchema(schema);
491491
const clientSchema = buildClientSchema(introspection);
492492

493-
const result = graphqlSync(
494-
clientSchema,
495-
'query Limited($v: CustomScalar) { foo(custom1: 123, custom2: $v) }',
496-
{ foo: 'bar', unused: 'value' },
497-
null,
498-
{ v: 'baz' },
499-
);
493+
const result = graphqlSync({
494+
schema: clientSchema,
495+
source:
496+
'query Limited($v: CustomScalar) { foo(custom1: 123, custom2: $v) }',
497+
rootValue: { foo: 'bar', unused: 'value' },
498+
variableValues: { v: 'baz' },
499+
});
500500

501501
expect(result.data).to.deep.equal({ foo: 'bar' });
502502
});
503503

504-
describe('throws when given incomplete introspection', () => {
504+
describe('throws when given invalid introspection', () => {
505505
const dummySchema = buildSchema(`
506506
type Query {
507+
foo(bar: String): String
508+
}
509+
510+
union SomeUnion = Query
511+
512+
enum SomeEnum { FOO }
513+
514+
input SomeInputObject {
507515
foo: String
508516
}
509517
510-
directive @Foo on QUERY
518+
directive @SomeDirective on QUERY
511519
`);
512520

513-
it('throws when given empty types', () => {
521+
it('throws when referenced unknown type', () => {
514522
const introspection = introspectionFromSchema(dummySchema);
515523

516524
// $DisableFlowOnNegativeTest
517-
introspection.__schema.types = [];
525+
introspection.__schema.types = introspection.__schema.types.filter(
526+
({ name }) => name !== 'Query',
527+
);
518528

519529
expect(() => buildClientSchema(introspection)).to.throw(
520530
'Invalid or incomplete schema, unknown type: Query. Ensure that a ' +
521531
'full introspection query is used in order to build a client schema.',
522532
);
523533
});
524534

535+
it('throws when type reference is missing name', () => {
536+
const introspection = introspectionFromSchema(dummySchema);
537+
538+
expect(introspection).to.have.nested.property('__schema.queryType.name');
539+
// $DisableFlowOnNegativeTest
540+
delete introspection.__schema.queryType.name;
541+
542+
expect(() => buildClientSchema(introspection)).to.throw(
543+
'Unknown type reference: {}',
544+
);
545+
});
546+
525547
it('throws when missing kind', () => {
526548
const introspection = introspectionFromSchema(dummySchema);
549+
const queryTypeIntrospection = introspection.__schema.types.find(
550+
({ name }) => name === 'Query',
551+
);
527552

528-
const queryTypeIntrospection = introspection.__schema.types[0];
529-
expect(queryTypeIntrospection).to.deep.include({
530-
name: 'Query',
531-
kind: 'OBJECT',
532-
});
553+
expect(queryTypeIntrospection).to.have.property('kind');
533554
// $DisableFlowOnNegativeTest
534555
delete queryTypeIntrospection.kind;
535556

@@ -541,35 +562,159 @@ describe('Type System: build schema from introspection', () => {
541562

542563
it('throws when missing interfaces', () => {
543564
const introspection = introspectionFromSchema(dummySchema);
565+
const queryTypeIntrospection = introspection.__schema.types.find(
566+
({ name }) => name === 'Query',
567+
);
544568

545-
const queryTypeIntrospection = introspection.__schema.types[0];
546-
expect(queryTypeIntrospection).to.deep.include({
547-
name: 'Query',
548-
interfaces: [],
549-
});
569+
expect(queryTypeIntrospection).to.have.property('interfaces');
550570
// $DisableFlowOnNegativeTest
551571
delete queryTypeIntrospection.interfaces;
552572

553573
expect(() => buildClientSchema(introspection)).to.throw(
554-
'Introspection result missing interfaces: ' +
555-
'{ kind: "OBJECT", name: "Query", description: null, fields: [{ name: "foo", description: null, args: [], type: { kind: "SCALAR", name: "String", ofType: null }, isDeprecated: false, deprecationReason: null }], inputFields: null, enumValues: null, possibleTypes: null }',
574+
'Introspection result missing interfaces: { kind: "OBJECT", name: "Query",',
575+
);
576+
});
577+
578+
it('throws when missing fields', () => {
579+
const introspection = introspectionFromSchema(dummySchema);
580+
const queryTypeIntrospection = introspection.__schema.types.find(
581+
({ name }) => name === 'Query',
582+
);
583+
584+
expect(queryTypeIntrospection).to.have.property('fields');
585+
// $DisableFlowOnNegativeTest
586+
delete queryTypeIntrospection.fields;
587+
588+
expect(() => buildClientSchema(introspection)).to.throw(
589+
'Introspection result missing fields: { kind: "OBJECT", name: "Query",',
590+
);
591+
});
592+
593+
it('throws when missing field args', () => {
594+
const introspection = introspectionFromSchema(dummySchema);
595+
const queryTypeIntrospection = introspection.__schema.types.find(
596+
({ name }) => name === 'Query',
597+
);
598+
599+
expect(queryTypeIntrospection).to.have.nested.property('fields[0].args');
600+
// $DisableFlowOnNegativeTest
601+
delete queryTypeIntrospection.fields[0].args;
602+
603+
expect(() => buildClientSchema(introspection)).to.throw(
604+
'Introspection result missing field args: { name: "foo",',
605+
);
606+
});
607+
608+
it('throws when output type is used as an arg type', () => {
609+
const introspection = introspectionFromSchema(dummySchema);
610+
const queryTypeIntrospection = introspection.__schema.types.find(
611+
({ name }) => name === 'Query',
612+
);
613+
614+
expect(queryTypeIntrospection).to.have.nested.property(
615+
'fields[0].args[0].type.name',
616+
'String',
617+
);
618+
// $DisableFlowOnNegativeTest
619+
queryTypeIntrospection.fields[0].args[0].type.name = 'SomeUnion';
620+
621+
expect(() => buildClientSchema(introspection)).to.throw(
622+
'Introspection must provide input type for arguments.',
623+
);
624+
});
625+
626+
it('throws when input type is used as a field type', () => {
627+
const introspection = introspectionFromSchema(dummySchema);
628+
const queryTypeIntrospection = introspection.__schema.types.find(
629+
({ name }) => name === 'Query',
630+
);
631+
632+
expect(queryTypeIntrospection).to.have.nested.property(
633+
'fields[0].type.name',
634+
'String',
635+
);
636+
// $DisableFlowOnNegativeTest
637+
queryTypeIntrospection.fields[0].type.name = 'SomeInputObject';
638+
639+
expect(() => buildClientSchema(introspection)).to.throw(
640+
'Introspection must provide output type for fields.',
641+
);
642+
});
643+
644+
it('throws when missing possibleTypes', () => {
645+
const introspection = introspectionFromSchema(dummySchema);
646+
const someUnionIntrospection = introspection.__schema.types.find(
647+
({ name }) => name === 'SomeUnion',
648+
);
649+
650+
expect(someUnionIntrospection).to.have.property('possibleTypes');
651+
// $DisableFlowOnNegativeTest
652+
delete someUnionIntrospection.possibleTypes;
653+
654+
expect(() => buildClientSchema(introspection)).to.throw(
655+
'Introspection result missing possibleTypes: { kind: "UNION", name: "SomeUnion",',
656+
);
657+
});
658+
659+
it('throws when missing enumValues', () => {
660+
const introspection = introspectionFromSchema(dummySchema);
661+
const someEnumIntrospection = introspection.__schema.types.find(
662+
({ name }) => name === 'SomeEnum',
663+
);
664+
665+
expect(someEnumIntrospection).to.have.property('enumValues');
666+
// $DisableFlowOnNegativeTest
667+
delete someEnumIntrospection.enumValues;
668+
669+
expect(() => buildClientSchema(introspection)).to.throw(
670+
'Introspection result missing enumValues: { kind: "ENUM", name: "SomeEnum",',
671+
);
672+
});
673+
674+
it('throws when missing inputFields', () => {
675+
const introspection = introspectionFromSchema(dummySchema);
676+
const someInputObjectIntrospection = introspection.__schema.types.find(
677+
({ name }) => name === 'SomeInputObject',
678+
);
679+
680+
expect(someInputObjectIntrospection).to.have.property('inputFields');
681+
// $DisableFlowOnNegativeTest
682+
delete someInputObjectIntrospection.inputFields;
683+
684+
expect(() => buildClientSchema(introspection)).to.throw(
685+
'Introspection result missing inputFields: { kind: "INPUT_OBJECT", name: "SomeInputObject",',
556686
);
557687
});
558688

559689
it('throws when missing directive locations', () => {
560690
const introspection = introspectionFromSchema(dummySchema);
561691

562-
const fooDirectiveIntrospection = introspection.__schema.directives[0];
563-
expect(fooDirectiveIntrospection).to.deep.include({
564-
name: 'Foo',
692+
const someDirectiveIntrospection = introspection.__schema.directives[0];
693+
expect(someDirectiveIntrospection).to.deep.include({
694+
name: 'SomeDirective',
565695
locations: ['QUERY'],
566696
});
567697
// $DisableFlowOnNegativeTest
568-
delete fooDirectiveIntrospection.locations;
698+
delete someDirectiveIntrospection.locations;
699+
700+
expect(() => buildClientSchema(introspection)).to.throw(
701+
'Introspection result missing directive locations: { name: "SomeDirective",',
702+
);
703+
});
704+
705+
it('throws when missing directive args', () => {
706+
const introspection = introspectionFromSchema(dummySchema);
707+
708+
const someDirectiveIntrospection = introspection.__schema.directives[0];
709+
expect(someDirectiveIntrospection).to.deep.include({
710+
name: 'SomeDirective',
711+
args: [],
712+
});
713+
// $DisableFlowOnNegativeTest
714+
delete someDirectiveIntrospection.args;
569715

570716
expect(() => buildClientSchema(introspection)).to.throw(
571-
'Introspection result missing directive locations: ' +
572-
'{ name: "Foo", description: null, args: [] }',
717+
'Introspection result missing directive args: { name: "SomeDirective",',
573718
);
574719
});
575720
});

0 commit comments

Comments
 (0)