def test_missing_non_nullable_arg_without_default(): arg = Argument("foo", NonNullType(Int)) field = Field("test", Int, [arg]) node = _test_node() with pytest.raises(CoercionError) as exc_info: coerce_argument_values(field, node) assert ( str(exc_info.value) == 'Argument "foo" of required type "Int!" was not provided' )
def test_provided_unknown_variable_without_default_non_nullable(): arg = Argument("foo", NonNullType(Int)) field = Field("test", Int, [arg]) node = _test_node(_var("bar")) with pytest.raises(CoercionError) as exc_info: coerce_argument_values(field, node) assert str(exc_info.value) == ( 'Argument "foo" of required type "Int!" was provided the missing ' 'variable "$bar"' )
async def test_merge_of_parallel_fragments(assert_execution): T = ObjectType( "Type", [ Field("a", String, resolver=lambda *_: "Apple"), Field("b", String, resolver=lambda *_: "Banana"), Field("c", String, resolver=lambda *_: "Cherry"), Field("deep", lambda: T, resolver=lambda *_: dict()), ], ) # type: ObjectType schema = Schema(T) await assert_execution( schema, parse(""" { a, ...FragOne, ...FragTwo } fragment FragOne on Type { b deep { b, deeper: deep { b } } } fragment FragTwo on Type { c deep { c, deeper: deep { c } } } """), expected_data={ "a": "Apple", "b": "Banana", "c": "Cherry", "deep": { "b": "Banana", "c": "Cherry", "deeper": { "b": "Banana", "c": "Cherry" }, }, }, )
def test_object_field(): schema = _single_field_schema(ObjectType("Foo", [Field("str", String)])) assert print_schema(schema, indent=" ") == dedent(""" type Foo { str: String } type Query { singleField: Foo } """)
def test_reject_input_type_with_incorectly_typed_fields(): BadInput = InputObjectType("BadInput", [InputField("f", SomeObject)]) schema = _single_type_schema( ObjectType("Object", [Field("field", String, [Argument("arg", BadInput)])])) with pytest.raises(SchemaError) as exc_info: validate_schema(schema) assert ('Expected input type for field "f" on "BadInput" ' 'but got "SomeObject"' in str(exc_info.value))
def test_reject_field_args_with_incorrect_names(): schema = _single_type_schema( ObjectType( "SomeObject", [Field("field", String, [Argument("bad-arg", String)])], )) with pytest.raises(SchemaError) as exc_info: validate_schema(schema) assert 'Invalid name "bad-arg"' in str(exc_info.value)
def test_register_default_resolver(): Query = ObjectType("Query", [Field("id", String)]) schema = Schema(Query) def query_default(root, ctx, info): return 42 schema.register_default_resolver("Query", query_default) assert (cast(ObjectType, schema.get_type("Query")).default_resolver is query_default)
def test_Schema_includes_introspection_types(): schema = Schema(ObjectType("Query", [Field("author", BlogAuthor)])) assert schema.get_type("__Schema") is not None assert schema.get_type("__Directive") is not None assert schema.get_type("__DirectiveLocation") is not None assert schema.get_type("__Type") is not None assert schema.get_type("__EnumValue") is not None assert schema.get_type("__InputValue") is not None assert schema.get_type("__Field") is not None assert schema.get_type("__TypeKind") is not None
def test_reject_input_type_with_no_fields(): EmptyInput = InputObjectType("EmptyInput", []) schema = _single_type_schema( ObjectType("Object", [Field("field", String, [Argument("arg", EmptyInput)])])) with pytest.raises(SchemaError) as exc_info: validate_schema(schema) assert 'Type "EmptyInput" must define at least one field' in str( exc_info.value)
def test_reject_non_object_subscription_type(): schema = Schema( ObjectType("Query", [Field("test", String)]), subscription_type=String, # type: ignore ) with pytest.raises(SchemaError) as exc_info: validate_schema(schema) assert 'Subscription must be ObjectType but got "String"' in str( exc_info.value)
def on_object(self, object_definition): assert self.name not in object_definition.field_map assert all(s in object_definition.field_map for s in self.source) return ObjectType( name=object_definition.name, description=object_definition.description, fields=([Field(self.name, String, resolver=self.resolve)] + object_definition.fields), interfaces=object_definition.interfaces, nodes=object_definition.nodes, )
def test_custom_subscription(): assert (dedent(""" schema { subscription: CustomSubscription } type CustomSubscription { foo: Int } """) == print_schema( Schema(subscription_type=ObjectType("CustomSubscription", [Field("foo", Int)]))))
def test_custom_mutation(): assert (dedent(""" schema { mutation: CustomMutation } type CustomMutation { foo: Int } """) == print_schema( Schema( mutation_type=ObjectType("CustomMutation", [Field("foo", Int)]))))
def test_custom_query_root_type(): assert print_schema( Schema(ObjectType("CustomQueryType", [Field("foo", String)])), indent=" ", ) == dedent(""" schema { query: CustomQueryType } type CustomQueryType { foo: String } """)
def test_input_type(): Input = InputObjectType("InputType", [InputField("int", Int)]) Query = ObjectType("Query", [Field("str", String, [Argument("argOne", Input)])]) assert print_schema(Schema(Query), indent=" ") == dedent(""" input InputType { int: Int } type Query { str(argOne: InputType): String } """)
def test_reject_object_implementing_same_interface_twice(): schema = _single_type_schema( ObjectType( "SomeObject", [Field("f", String)], interfaces=[SomeInterface, SomeInterface], )) with pytest.raises(SchemaError) as exc_info: validate_schema(schema) assert ( 'Type "SomeObject" mut only implement interface "SomeInterface" once' in str(exc_info.value))
def test_custom_python_name_in_input_object(): arg = Argument( "foo", NonNullType( InputObjectType( "Foo", [InputField("field", NonNullType(Int), python_name="value")], ) ), ) field = Field("test", Int, [arg]) node = _test_node(parse_value("{ field: 42 }")) assert coerce_argument_values(field, node) == {"foo": {"value": 42}}
async def test_invalid_scalar(assert_execution): schema = Schema(ObjectType("Query", [Field("field", Int)])) await assert_execution( schema, "{ field }", initial_value={"field": "aff929fe-25a1"}, expected_exc=( RuntimeError, ('Field "field" cannot be serialized as "Int": ' "Int cannot represent non integer value: aff929fe-25a1"), ), )
def test_enum(): Enum = EnumType("RGB", [("RED", 0), ("GREEN", 1), ("BLUE", 2)]) assert print_schema(Schema(ObjectType("Query", [Field("rgb", Enum)])), indent=" ") == dedent(""" type Query { rgb: RGB } enum RGB { RED GREEN BLUE } """)
async def test_type_resolution_supports_object_attribute(assert_execution): PetType = InterfaceType("Pet", [Field("name", String)]) DogType = ObjectType( "Dog", [Field("name", String), Field("woofs", Boolean)], interfaces=[PetType], ) CatType = ObjectType( "Cat", [Field("name", String), Field("meows", Boolean)], interfaces=[PetType], ) class Dog: __typename__ = "Dog" def __init__(self, name, woofs): self.name = name self.woofs = woofs class Cat: __typename__ = "Cat" def __init__(self, name, meows): self.name = name self.meows = meows schema = Schema( ObjectType( "Query", [ Field( "pets", ListType(PetType), resolver=lambda *_: [ Dog("Odie", True), Cat("Garfield", False), ], ) ], ), types=[DogType, CatType], ) await assert_execution( schema, """{ pets { name __typename ... on Dog { woofs } ... on Cat { meows } } }""", )
async def test_type_resolution_on_union_yields_useful_error(assert_execution): # WARN: Different from ref implementation -> this should never happen so we crash . def _resolve_pet_type(value, *_): return { Dog: DogType, Cat: CatType, Human: HumanType }.get(type(value), None) HumanType = ObjectType("Human", [Field("name", String)]) DogType = ObjectType( "Dog", [Field("name", String), Field("woofs", Boolean)]) CatType = ObjectType( "Cat", [Field("name", String), Field("meows", Boolean)]) PetType = UnionType("Pet", [DogType, CatType], resolve_type=_resolve_pet_type) schema = Schema( ObjectType( "Query", [ Field( "pets", ListType(PetType), resolver=lambda *_: [ Dog("Odie", True), Cat("Garfield", False), Human("Jon"), ], ) ], ), types=[DogType, CatType], ) await assert_execution( schema, """{ pets { __typename ... on Dog { woofs, name } ... on Cat { meows, name } } }""", expected_exc=( RuntimeError, ('Runtime ObjectType "Human" is not a possible type for field ' '"pets[2]" of type "Pet".'), ), )
def test_custom_directive(): directive = Directive( "customDirective", locations=["FIELD"], args=[Argument("argOne", String)], ) schema = Schema(ObjectType("Query", [Field("foo", String)]), directives=[directive]) assert print_schema(schema, indent=" ") == dedent(""" directive @customDirective(argOne: String) on FIELD type Query { foo: String } """)
def schema(self) -> Schema: return _single_type_schema( ObjectType( "Object", [ Field( "field", String, [ Argument("a", String), Argument("b", NonNullType(String)), Argument("c", String, default_value="c"), ], ) ], ))
async def test_raises_on_missing_subscription_resolver(starwars_schema): schema = subscription_schema( Field( "counter", NonNullType(Int), args=[Argument("delay", NonNullType(Float))], resolver=lambda event, *_, **__: event, ) ) with pytest.raises(RuntimeError): subscribe( schema, parse("subscription { counter(delay: 0.001) }"), runtime=AsyncIORuntime(), )
def test_replace_type_in_union(schema: Schema) -> None: NewPerson = ObjectType( "Person", fields=(list(cast(ObjectType, schema.types["Person"]).fields) + [Field("last_name", NonNullType(String))]), interfaces=[cast(InterfaceType, schema.types["Object"])], ) schema._replace_types_and_directives({"Person": NewPerson}) assert cast(ObjectType, schema.get_type("Person")) is NewPerson union_type = cast(UnionType, schema.get_type("LivingBeing")) assert NewPerson in union_type.types assert 2 == len(union_type.types)
async def test_raises_on_unsupported_runtime(): schema = subscription_schema( Field( "counter", NonNullType(Int), args=[Argument("delay", NonNullType(Float))], subscription_resolver=lambda *_, delay: AsyncCounter(delay, 10), resolver=lambda event, *_, **__: event, ) ) with pytest.raises(RuntimeError): subscribe( schema, parse("subscription { counter(delay: 0.001) }"), runtime=BlockingRuntime(), # type: ignore )
def test_register_default_resolver_already_set(): Query = ObjectType("Query", [Field("id", String)]) schema = Schema(Query) def query_default(root, ctx, info): return 42 def query_default_2(root, ctx, info): return 84 schema.register_default_resolver("Query", query_default) with pytest.raises(ValueError): schema.register_default_resolver("Query", query_default_2) assert (cast(ObjectType, schema.get_type("Query")).default_resolver is query_default)
def test_register_default_resolver_allow_override(): Query = ObjectType("Query", [Field("id", String)]) schema = Schema(Query) def query_default(root, ctx, info): return 42 def query_default_2(root, ctx, info): return 84 schema.register_default_resolver("Query", query_default) schema.register_default_resolver("Query", query_default_2, allow_override=True) assert (cast(ObjectType, schema.get_type("Query")).default_resolver is query_default_2)
async def test_raises_on_multiple_fields(starwars_schema): schema = subscription_schema( Field( "counter", NonNullType(Int), args=[Argument("delay", NonNullType(Float))], subscription_resolver=lambda *_, delay: AsyncCounter(delay, 10), resolver=lambda event, *_, **__: event, ) ) with pytest.raises(ExecutionError): subscribe( schema, parse( "subscription { counter(delay: 0.001), other: counter(delay: 0.001) }" ), runtime=AsyncIORuntime(), )
def wrap_resolver(field_def, func): source_resolver = field_def.resolver or default_resolver @ft.wraps(source_resolver) def wrapped(parent_value, context, info, **args): value = source_resolver(parent_value, context, info, **args) if value is None: return value return func(value) return Field( name=field_def.name, type_=field_def.type, description=field_def.description, deprecation_reason=field_def.deprecation_reason, args=field_def.arguments, resolver=wrapped, node=field_def.node, )