Skip to content

Commit 6c2cbb9

Browse files
committed
More type validation tests & fixes for checking resolve_type and is_type_of functions.
#8
1 parent e399b18 commit 6c2cbb9

File tree

2 files changed

+286
-11
lines changed

2 files changed

+286
-11
lines changed

graphql/core/type/definition.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,7 @@ def __init__(self, name, fields, interfaces=None, is_type_of=None, description=N
178178
self.name = name
179179
self.description = description
180180

181-
if is_type_of:
181+
if is_type_of is not None:
182182
assert callable(is_type_of), '{} must provide "is_type_of" as a function.'.format(self)
183183

184184
self.is_type_of = is_type_of
@@ -331,7 +331,7 @@ def __init__(self, name, fields=None, resolve_type=None, description=None):
331331
self.name = name
332332
self.description = description
333333

334-
if resolve_type:
334+
if resolve_type is not None:
335335
assert callable(resolve_type), '{} must provide "resolve_type" as a function.'.format(self)
336336

337337
self.type_resolver = resolve_type
@@ -397,12 +397,12 @@ def __init__(self, name, types=None, resolve_type=None, description=None):
397397
self.name = name
398398
self.description = description
399399

400-
if resolve_type:
400+
if resolve_type is not None:
401401
assert callable(resolve_type), '{} must provide "resolve_type" as a function.'.format(self)
402402

403403
self._resolve_type = resolve_type
404404

405-
assert types, 'Must provide types for Union {}.'.format(name)
405+
assert isinstance(types, (list, tuple)) and len(types) > 0, 'Must provide types for Union {}.'.format(name)
406406
has_resolve_type_fn = callable(self._resolve_type)
407407

408408
for type in types:
@@ -590,14 +590,14 @@ def _define_field_map(self):
590590
for field_name, field in fields.items():
591591
assert_valid_name(field_name)
592592
assert isinstance(field, GraphQLInputObjectField), (
593-
'{}.{} must be an instance of GraphQLInputObjectField.'.format(type, field_name)
593+
'{}.{} must be an instance of GraphQLInputObjectField.'.format(self, field_name)
594594
)
595595

596596
field = copy.copy(field)
597597
field.name = field_name
598598

599599
assert is_input_type(field.type), (
600-
'{}.{} field type must be Input Type but got: {}.'.format(type, field_name, field.type)
600+
'{}.{} field type must be Input Type but got: {}.'.format(self, field_name, field.type)
601601
)
602602

603603
field_map[field_name] = field

tests/core_type/test_validation.py

Lines changed: 280 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -384,11 +384,286 @@ def test_rejects_an_object_with_incorrectly_typed_field_args_with_an_invalid_val
384384

385385
assert str(excinfo.value) == 'SomeObject.badField(badArg:) argument must be an instance of GraphQLArgument.'
386386

387-
# describe('Type System: Object interfaces must be array', () => {
388-
# describe('Type System: Union types must be array', () => {
389-
# describe('Type System: Input Objects must have fields', () => {
390-
# describe('Type System: Object types must be assertable', () => {
391-
# describe('Type System: Interface types must be resolvable', () => {
387+
388+
# noinspection PyMethodMayBeStatic,PyPep8Naming
389+
class TestTypeSystem_ObjectInterfacesMustBeArray:
390+
def test_accepts_an_object_type_with_array_interface(self):
391+
AnotherInterfaceType = GraphQLInterfaceType(
392+
name='AnotherInterface',
393+
resolve_type=_none,
394+
fields={'f': GraphQLField(GraphQLString)}
395+
)
396+
397+
assert schema_with_field_type(GraphQLObjectType(
398+
name='SomeObject',
399+
interfaces=[AnotherInterfaceType],
400+
fields={'f': GraphQLField(GraphQLString)}
401+
))
402+
403+
def test_accepts_an_object_type_with_interfaces_as_a_function_returning_an_array(self):
404+
AnotherInterfaceType = GraphQLInterfaceType(
405+
name='AnotherInterface',
406+
resolve_type=_none,
407+
fields={'f': GraphQLField(GraphQLString)}
408+
)
409+
410+
assert schema_with_field_type(GraphQLObjectType(
411+
name='SomeObject',
412+
interfaces=lambda: [AnotherInterfaceType],
413+
fields={'f': GraphQLField(GraphQLString)}
414+
))
415+
416+
def test_rejects_an_object_type_with_incorrectly_typed_interfaces(self):
417+
with raises(AssertionError) as excinfo:
418+
schema_with_field_type(GraphQLObjectType(
419+
name='SomeObject',
420+
interfaces={},
421+
fields={'f': GraphQLField(GraphQLString)}
422+
))
423+
424+
assert str(excinfo.value) == 'SomeObject interfaces must be a list/tuple or a function which returns a ' \
425+
'list/tuple.'
426+
427+
def test_rejects_an_object_type_with_interfaces_as_a_function_returning_an_incorrect_type(self):
428+
with raises(AssertionError) as excinfo:
429+
schema_with_field_type(GraphQLObjectType(
430+
name='SomeObject',
431+
interfaces=lambda: {},
432+
fields={'f': GraphQLField(GraphQLString)}
433+
))
434+
435+
assert str(excinfo.value) == 'SomeObject interfaces must be a list/tuple or a function which returns a ' \
436+
'list/tuple.'
437+
438+
439+
# noinspection PyMethodMayBeStatic,PyPep8Naming
440+
class TestTypeSystem_UnionTypesMustBeArray:
441+
def test_accepts_a_union_type_with_aray_types(self):
442+
assert schema_with_field_type(GraphQLUnionType(
443+
name='SomeUnion',
444+
resolve_type=_none,
445+
types=[SomeObjectType]
446+
))
447+
448+
def test_rejects_a_union_without_types(self):
449+
with raises(AssertionError) as excinfo:
450+
schema_with_field_type(GraphQLUnionType(
451+
name='SomeUnion',
452+
resolve_type=_none
453+
))
454+
455+
assert str(excinfo.value) == 'Must provide types for Union SomeUnion.'
456+
457+
def test_rejects_a_union_type_with_empty_types(self):
458+
with raises(AssertionError) as excinfo:
459+
schema_with_field_type(GraphQLUnionType(
460+
name='SomeUnion',
461+
resolve_type=_none,
462+
types=[]
463+
))
464+
465+
assert str(excinfo.value) == 'Must provide types for Union SomeUnion.'
466+
467+
def test_rejects_a_union_type_with_incorrectly_typed_types(self):
468+
with raises(AssertionError) as excinfo:
469+
schema_with_field_type(GraphQLUnionType(
470+
name='SomeUnion',
471+
resolve_type=_none,
472+
types={'SomeObjectType': SomeObjectType}
473+
))
474+
475+
assert str(excinfo.value) == 'Must provide types for Union SomeUnion.'
476+
477+
478+
def schema_with_input_object(input_object_type):
479+
return GraphQLSchema(
480+
query=GraphQLObjectType(
481+
name='Query',
482+
fields={
483+
'f': GraphQLField(GraphQLString, args={
484+
'badArg': GraphQLArgument(input_object_type)
485+
})
486+
}
487+
)
488+
)
489+
490+
491+
# noinspection PyMethodMayBeStatic,PyPep8Naming
492+
class TestTypeSystem_InputObjectsMustHaveFields:
493+
def test_accepts_an_input_object_type_with_fields(self):
494+
assert schema_with_input_object(GraphQLInputObjectType(
495+
name='SomeInputObject',
496+
fields={
497+
'f': GraphQLInputObjectField(GraphQLString)
498+
}
499+
))
500+
501+
def test_accepts_an_input_object_type_with_field_function(self):
502+
assert schema_with_input_object(GraphQLInputObjectType(
503+
name='SomeInputObject',
504+
fields=lambda: {
505+
'f': GraphQLInputObjectField(GraphQLString)
506+
}
507+
))
508+
509+
def test_rejects_an_input_object_type_with_missing_fields(self):
510+
with raises(AssertionError) as excinfo:
511+
schema_with_input_object(GraphQLInputObjectType(
512+
name='SomeInputObject',
513+
fields=None
514+
))
515+
516+
assert str(excinfo.value) == 'SomeInputObject fields must be a mapping (dict / OrderedDict) with ' \
517+
'field names as keys or a function which returns such a mapping.'
518+
519+
def test_rejects_an_input_object_type_with_incorrectly_typed_fields(self):
520+
with raises(AssertionError) as excinfo:
521+
schema_with_input_object(GraphQLInputObjectType(
522+
name='SomeInputObject',
523+
fields=[GraphQLInputObjectField(GraphQLString)]
524+
))
525+
526+
assert str(excinfo.value) == 'SomeInputObject fields must be a mapping (dict / OrderedDict) with ' \
527+
'field names as keys or a function which returns such a mapping.'
528+
529+
def test_rejects_an_input_object_type_with_incorrectly_typed_field_value(self):
530+
with raises(AssertionError) as excinfo:
531+
schema_with_input_object(GraphQLInputObjectType(
532+
name='SomeInputObject',
533+
fields={'f': GraphQLField(GraphQLString)}
534+
))
535+
536+
assert str(excinfo.value) == 'SomeInputObject.f must be an instance of GraphQLInputObjectField.'
537+
538+
def test_rejects_an_input_object_type_with_fields_function_returning_incorrectly_typed_field_value(self):
539+
with raises(AssertionError) as excinfo:
540+
schema_with_input_object(GraphQLInputObjectType(
541+
name='SomeInputObject',
542+
fields=lambda: {'f': GraphQLField(GraphQLString)}
543+
))
544+
545+
assert str(excinfo.value) == 'SomeInputObject.f must be an instance of GraphQLInputObjectField.'
546+
547+
def test_rejects_an_input_object_type_with_empty_fields(self):
548+
with raises(AssertionError) as excinfo:
549+
schema_with_input_object(GraphQLInputObjectType(
550+
name='SomeInputObject',
551+
fields={}
552+
))
553+
554+
assert str(excinfo.value) == 'SomeInputObject fields must be a mapping (dict / OrderedDict) with ' \
555+
'field names as keys or a function which returns such a mapping.'
556+
557+
def test_rejects_an_input_object_type_with_a_field_function_that_returns_nothing(self):
558+
with raises(AssertionError) as excinfo:
559+
schema_with_input_object(GraphQLInputObjectType(
560+
name='SomeInputObject',
561+
fields=_none
562+
))
563+
564+
assert str(excinfo.value) == 'SomeInputObject fields must be a mapping (dict / OrderedDict) with ' \
565+
'field names as keys or a function which returns such a mapping.'
566+
567+
def test_rejects_an_input_object_type_with_a_field_function_that_returns_empty(self):
568+
with raises(AssertionError) as excinfo:
569+
schema_with_input_object(GraphQLInputObjectType(
570+
name='SomeInputObject',
571+
fields=lambda: {}
572+
))
573+
574+
assert str(excinfo.value) == 'SomeInputObject fields must be a mapping (dict / OrderedDict) with ' \
575+
'field names as keys or a function which returns such a mapping.'
576+
577+
578+
# noinspection PyMethodMayBeStatic,PyPep8Naming
579+
class TestTypeSystem_ObjectTypesMustBeAssertable:
580+
def test_accepts_an_object_type_with_an_is_type_of_function(self):
581+
assert schema_with_field_type(GraphQLObjectType(
582+
name='AnotherObject',
583+
is_type_of=_true,
584+
fields={'f': GraphQLField(GraphQLString)}
585+
))
586+
587+
def test_rejects_an_object_type_with_an_incorrect_type_for_is_type_of(self):
588+
with raises(AssertionError) as excinfo:
589+
schema_with_field_type(GraphQLObjectType(
590+
name='AnotherObject',
591+
is_type_of={},
592+
fields={'f': GraphQLField(GraphQLString)}
593+
))
594+
595+
assert str(excinfo.value) == 'AnotherObject must provide "is_type_of" as a function.'
596+
597+
598+
# noinspection PyMethodMayBeStatic,PyPep8Naming
599+
class TestTypeSystem_InterfaceTypesMustBeResolvable:
600+
def test_accepts_an_interface_type_defining_resolve_type(self):
601+
AnotherInterfaceType = GraphQLInterfaceType(
602+
name='AnotherInterface',
603+
resolve_type=_none,
604+
fields={'f': GraphQLField(GraphQLString)}
605+
)
606+
607+
assert schema_with_field_type(GraphQLObjectType(
608+
name='SomeObject',
609+
interfaces=[AnotherInterfaceType],
610+
fields={'f': GraphQLField(GraphQLString)}
611+
))
612+
613+
def test_accepts_an_interface_with_implementing_type_defining_is_type_of(self):
614+
AnotherInterfaceType = GraphQLInterfaceType(
615+
name='AnotherInterface',
616+
fields={'f': GraphQLField(GraphQLString)}
617+
)
618+
619+
assert schema_with_field_type(GraphQLObjectType(
620+
name='SomeObject',
621+
is_type_of=_true,
622+
interfaces=[AnotherInterfaceType],
623+
fields={'f': GraphQLField(GraphQLString)}
624+
))
625+
626+
def test_accepts_an_interface_type_defining_resolve_type_with_implementing_type_defining_is_type_of(self):
627+
AnotherInterfaceType = GraphQLInterfaceType(
628+
name='AnotherInterface',
629+
resolve_type=_none,
630+
fields={'f': GraphQLField(GraphQLString)}
631+
)
632+
assert schema_with_field_type(GraphQLObjectType(
633+
name='SomeObject',
634+
is_type_of=_true,
635+
interfaces=[AnotherInterfaceType],
636+
fields={'f': GraphQLField(GraphQLString)}
637+
))
638+
639+
def test_rejects_an_interface_type_with_an_incorrect_type_for_resolve_type(self):
640+
with raises(AssertionError) as excinfo:
641+
GraphQLInterfaceType(
642+
name='AnotherInterface',
643+
resolve_type={},
644+
fields={'f': GraphQLField(GraphQLString)}
645+
)
646+
647+
assert str(excinfo.value) == 'AnotherInterface must provide "resolve_type" as a function.'
648+
649+
def test_rejects_an_interface_type_not_defining_resolve_type_with_implementing_type_not_defining_is_type_of(self):
650+
AnotherInterfaceType = GraphQLInterfaceType(
651+
name='AnotherInterface',
652+
fields={'f': GraphQLField(GraphQLString)}
653+
)
654+
655+
with raises(AssertionError) as excinfo:
656+
schema_with_field_type(GraphQLObjectType(
657+
name='SomeObject',
658+
interfaces=[AnotherInterfaceType],
659+
fields={'f': GraphQLField(GraphQLString)}
660+
))
661+
662+
assert str(excinfo.value) == 'Interface Type AnotherInterface does not provide a "resolve_type" function and ' \
663+
'implementing Type SomeObject does not provide a "is_type_of" function. ' \
664+
'There is no way to resolve this implementing type during execution.'
665+
666+
# describe('Type System: Union types must be resolvable', () => {
392667
# describe('Type System: Scalar types must be serializable', () => {
393668
# describe('Type System: Enum types must be well defined', () => {
394669
# describe('Type System: Object fields must have output types', () => {

0 commit comments

Comments
 (0)