def should_detect_if_a_field_on_type_was_deleted_or_changed_type():
        old_schema = build_schema("""
            type TypeA {
              field1: String
            }

            interface Type1 {
              field1: TypeA
              field2: String
              field3: String
              field4: TypeA
              field6: String
              field7: [String]
              field8: Int
              field9: Int!
              field10: [Int]!
              field11: Int
              field12: [Int]
              field13: [Int!]
              field14: [Int]
              field15: [[Int]]
              field16: Int!
              field17: [Int]
              field18: [[Int!]!]
            }

            type Query {
              field1: String
            }
            """)

        new_schema = build_schema("""
            type TypeA {
              field1: String
            }

            type TypeB {
              field1: String
            }

            interface Type1 {
              field1: TypeA
              field3: Boolean
              field4: TypeB
              field5: String
              field6: [String]
              field7: String
              field8: Int!
              field9: Int
              field10: [Int]
              field11: [Int]!
              field12: [Int!]
              field13: [Int]
              field14: [[Int]]
              field15: [Int]
              field16: [Int]!
              field17: [Int]!
              field18: [[Int!]]
            }

            type Query {
              field1: String
            }
            """)

        expected_field_changes = [
            (BreakingChangeType.FIELD_REMOVED, "Type1.field2 was removed."),
            (
                BreakingChangeType.FIELD_CHANGED_KIND,
                "Type1.field3 changed type from String to Boolean.",
            ),
            (
                BreakingChangeType.FIELD_CHANGED_KIND,
                "Type1.field4 changed type from TypeA to TypeB.",
            ),
            (
                BreakingChangeType.FIELD_CHANGED_KIND,
                "Type1.field6 changed type from String to [String].",
            ),
            (
                BreakingChangeType.FIELD_CHANGED_KIND,
                "Type1.field7 changed type from [String] to String.",
            ),
            (
                BreakingChangeType.FIELD_CHANGED_KIND,
                "Type1.field9 changed type from Int! to Int.",
            ),
            (
                BreakingChangeType.FIELD_CHANGED_KIND,
                "Type1.field10 changed type from [Int]! to [Int].",
            ),
            (
                BreakingChangeType.FIELD_CHANGED_KIND,
                "Type1.field11 changed type from Int to [Int]!.",
            ),
            (
                BreakingChangeType.FIELD_CHANGED_KIND,
                "Type1.field13 changed type from [Int!] to [Int].",
            ),
            (
                BreakingChangeType.FIELD_CHANGED_KIND,
                "Type1.field14 changed type from [Int] to [[Int]].",
            ),
            (
                BreakingChangeType.FIELD_CHANGED_KIND,
                "Type1.field15 changed type from [[Int]] to [Int].",
            ),
            (
                BreakingChangeType.FIELD_CHANGED_KIND,
                "Type1.field16 changed type from Int! to [Int]!.",
            ),
            (
                BreakingChangeType.FIELD_CHANGED_KIND,
                "Type1.field18 changed type from [[Int!]!] to [[Int!]].",
            ),
        ]

        assert (find_fields_that_changed_type_on_object_or_interface_types(
            old_schema, new_schema) == expected_field_changes)
from functools import partial

from graphql.utilities import build_schema
from graphql.validation import NoDeprecatedCustomRule

from .harness import assert_validation_errors

schema = build_schema(
    """
    enum EnumType {
      NORMAL_VALUE
      DEPRECATED_VALUE @deprecated(reason: "Some enum reason.")
      DEPRECATED_VALUE_WITH_NO_REASON @deprecated
    }

    type Query {
      normalField(enumArg: [EnumType]): String
      deprecatedField: String @deprecated(reason: "Some field reason.")
      deprecatedFieldWithNoReason: String @deprecated
    }
    """
)

assert_errors = partial(assert_validation_errors, NoDeprecatedCustomRule, schema=schema)

assert_valid = partial(assert_errors, errors=[])


def describe_validate_no_deprecated():
    def ignores_fields_and_enum_values_that_are_not_deprecated():
        assert_valid(
    def should_detect_if_a_field_argument_has_changed_type():
        old_schema = build_schema("""
            type Type1 {
              field1(
                arg1: String
                arg2: String
                arg3: [String]
                arg4: String
                arg5: String!
                arg6: String!
                arg7: [Int]!
                arg8: Int
                arg9: [Int]
                arg10: [Int!]
                arg11: [Int]
                arg12: [[Int]]
                arg13: Int!
                arg14: [[Int]!]
                arg15: [[Int]!]
              ): String
            }

            type Query {
              field1: String
            }
            """)

        new_schema = build_schema("""
            type Type1 {
              field1(
                arg1: Int
                arg2: [String]
                arg3: String
                arg4: String!
                arg5: Int
                arg6: Int!
                arg7: [Int]
                arg8: [Int]!
                arg9: [Int!]
                arg10: [Int]
                arg11: [[Int]]
                arg12: [Int]
                arg13: [Int]!
                arg14: [[Int]]
                arg15: [[Int!]!]
               ): String
            }

            type Query {
              field1: String
            }
            """)

        assert find_arg_changes(old_schema, new_schema).breaking_changes == [
            (
                BreakingChangeType.ARG_CHANGED_KIND,
                "Type1.field1 arg arg1 has changed type from String to Int",
            ),
            (
                BreakingChangeType.ARG_CHANGED_KIND,
                "Type1.field1 arg arg2 has changed type from String to [String]",
            ),
            (
                BreakingChangeType.ARG_CHANGED_KIND,
                "Type1.field1 arg arg3 has changed type from [String] to String",
            ),
            (
                BreakingChangeType.ARG_CHANGED_KIND,
                "Type1.field1 arg arg4 has changed type from String to String!",
            ),
            (
                BreakingChangeType.ARG_CHANGED_KIND,
                "Type1.field1 arg arg5 has changed type from String! to Int",
            ),
            (
                BreakingChangeType.ARG_CHANGED_KIND,
                "Type1.field1 arg arg6 has changed type from String! to Int!",
            ),
            (
                BreakingChangeType.ARG_CHANGED_KIND,
                "Type1.field1 arg arg8 has changed type from Int to [Int]!",
            ),
            (
                BreakingChangeType.ARG_CHANGED_KIND,
                "Type1.field1 arg arg9 has changed type from [Int] to [Int!]",
            ),
            (
                BreakingChangeType.ARG_CHANGED_KIND,
                "Type1.field1 arg arg11 has changed type from [Int] to [[Int]]",
            ),
            (
                BreakingChangeType.ARG_CHANGED_KIND,
                "Type1.field1 arg arg12 has changed type from [[Int]] to [Int]",
            ),
            (
                BreakingChangeType.ARG_CHANGED_KIND,
                "Type1.field1 arg arg13 has changed type from Int! to [Int]!",
            ),
            (
                BreakingChangeType.ARG_CHANGED_KIND,
                "Type1.field1 arg arg15 has changed type from [[Int]!] to [[Int!]!]",
            ),
        ]
    def should_detect_all_breaking_changes():
        old_schema = build_schema("""
            directive @DirectiveThatIsRemoved on FIELD_DEFINITION

            directive @DirectiveThatRemovesArg(arg1: String) on FIELD_DEFINITION

            directive @NonNullDirectiveAdded on FIELD_DEFINITION

            directive @DirectiveName on FIELD_DEFINITION | QUERY

            type ArgThatChanges {
                field1(id: Int): String
            }

            enum EnumTypeThatLosesAValue {
                VALUE0
                VALUE1
                VALUE2
            }

            interface Interface1 {
                field1: String
            }

            type TypeThatGainsInterface1 implements Interface1 {
                field1: String
            }

            type TypeInUnion1 {
                field1: String
            }

            type TypeInUnion2 {
                field1: String
            }

            union UnionTypeThatLosesAType = TypeInUnion1 | TypeInUnion2

            type TypeThatChangesType {
                field1: String
            }

            type TypeThatGetsRemoved {
                field1: String
            }

            interface TypeThatHasBreakingFieldChanges {
                field1: String
                field2: String
            }

            type Query {
                field1: String
            }
            """)

        new_schema = build_schema("""
            directive @DirectiveThatRemovesArg on FIELD_DEFINITION

            directive @NonNullDirectiveAdded(arg1: Boolean!) on FIELD_DEFINITION

            directive @DirectiveName on FIELD_DEFINITION

            type ArgThatChanges {
              field1(id: String): String
            }

            enum EnumTypeThatLosesAValue {
              VALUE1
              VALUE2
            }

            interface Interface1 {
              field1: String
            }

            type TypeInUnion1 {
              field1: String
            }

            union UnionTypeThatLosesAType = TypeInUnion1

            interface TypeThatChangesType {
              field1: String
            }

            type TypeThatGainsInterface1 {
              field1: String
            }

            interface TypeThatHasBreakingFieldChanges {
              field2: Boolean
            }

            type Query {
              field1: String
            }
            """)

        expected_breaking_changes = [
            (BreakingChangeType.TYPE_REMOVED, "Int was removed."),
            (BreakingChangeType.TYPE_REMOVED, "TypeInUnion2 was removed."),
            (BreakingChangeType.TYPE_REMOVED,
             "TypeThatGetsRemoved was removed."),
            (
                BreakingChangeType.TYPE_CHANGED_KIND,
                "TypeThatChangesType changed from an Object type to an"
                " Interface type.",
            ),
            (
                BreakingChangeType.FIELD_REMOVED,
                "TypeThatHasBreakingFieldChanges.field1 was removed.",
            ),
            (
                BreakingChangeType.FIELD_CHANGED_KIND,
                "TypeThatHasBreakingFieldChanges.field2 changed type"
                " from String to Boolean.",
            ),
            (
                BreakingChangeType.TYPE_REMOVED_FROM_UNION,
                "TypeInUnion2 was removed from union type UnionTypeThatLosesAType.",
            ),
            (
                BreakingChangeType.VALUE_REMOVED_FROM_ENUM,
                "VALUE0 was removed from enum type EnumTypeThatLosesAValue.",
            ),
            (
                BreakingChangeType.ARG_CHANGED_KIND,
                "ArgThatChanges.field1 arg id has changed type from Int to String",
            ),
            (
                BreakingChangeType.INTERFACE_REMOVED_FROM_OBJECT,
                "TypeThatGainsInterface1 no longer implements interface Interface1.",
            ),
            (
                BreakingChangeType.DIRECTIVE_REMOVED,
                "DirectiveThatIsRemoved was removed",
            ),
            (
                BreakingChangeType.DIRECTIVE_ARG_REMOVED,
                "arg1 was removed from DirectiveThatRemovesArg",
            ),
            (
                BreakingChangeType.REQUIRED_DIRECTIVE_ARG_ADDED,
                "A required arg arg1 on directive NonNullDirectiveAdded was added",
            ),
            (
                BreakingChangeType.DIRECTIVE_LOCATION_REMOVED,
                "QUERY was removed from DirectiveName",
            ),
        ]

        assert (find_breaking_changes(old_schema,
                                      new_schema) == expected_breaking_changes)
Exemple #5
0
def sort_sdl(sdl):
    schema = build_schema(sdl)
    return print_schema(lexicographic_sort_schema(schema))
    def should_detect_if_fields_on_input_types_changed_kind_or_were_removed():
        old_schema = build_schema("""
            input InputType1 {
              field1: String
              field2: Boolean
              field3: [String]
              field4: String!
              field5: String
              field6: [Int]
              field7: [Int]!
              field8: Int
              field9: [Int]
              field10: [Int!]
              field11: [Int]
              field12: [[Int]]
              field13: Int!
              field14: [[Int]!]
              field15: [[Int]!]
            }

            type Query {
              field1: String
            }""")

        new_schema = build_schema("""
            input InputType1 {
              field1: Int
              field3: String
              field4: String
              field5: String!
              field6: [Int]!
              field7: [Int]
              field8: [Int]!
              field9: [Int!]
              field10: [Int]
              field11: [[Int]]
              field12: [Int]
              field13: [Int]!
              field14: [[Int]]
              field15: [[Int!]!]
            }

            type Query {
              field1: String
            }
            """)

        expected_field_changes = [
            (
                BreakingChangeType.FIELD_CHANGED_KIND,
                "InputType1.field1 changed type from String to Int.",
            ),
            (BreakingChangeType.FIELD_REMOVED,
             "InputType1.field2 was removed."),
            (
                BreakingChangeType.FIELD_CHANGED_KIND,
                "InputType1.field3 changed type from [String] to String.",
            ),
            (
                BreakingChangeType.FIELD_CHANGED_KIND,
                "InputType1.field5 changed type from String to String!.",
            ),
            (
                BreakingChangeType.FIELD_CHANGED_KIND,
                "InputType1.field6 changed type from [Int] to [Int]!.",
            ),
            (
                BreakingChangeType.FIELD_CHANGED_KIND,
                "InputType1.field8 changed type from Int to [Int]!.",
            ),
            (
                BreakingChangeType.FIELD_CHANGED_KIND,
                "InputType1.field9 changed type from [Int] to [Int!].",
            ),
            (
                BreakingChangeType.FIELD_CHANGED_KIND,
                "InputType1.field11 changed type from [Int] to [[Int]].",
            ),
            (
                BreakingChangeType.FIELD_CHANGED_KIND,
                "InputType1.field12 changed type from [[Int]] to [Int].",
            ),
            (
                BreakingChangeType.FIELD_CHANGED_KIND,
                "InputType1.field13 changed type from Int! to [Int]!.",
            ),
            (
                BreakingChangeType.FIELD_CHANGED_KIND,
                "InputType1.field15 changed type from [[Int]!] to [[Int!]!].",
            ),
        ]

        assert (find_fields_that_changed_type_on_input_object_types(
            old_schema, new_schema).breaking_changes == expected_field_changes)
    def describe_throws_when_given_invalid_introspection():
        dummy_schema = build_schema("""
            type Query {
              foo(bar: String): String
            }

            interface SomeInterface {
              foo: String
            }

            union SomeUnion = Query

            enum SomeEnum { FOO }

            input SomeInputObject {
              foo: String
            }

            directive @SomeDirective on QUERY
            """)

        def throws_when_introspection_is_missing_schema_property():
            with raises(TypeError) as exc_info:
                # noinspection PyTypeChecker
                build_client_schema(None)  # type: ignore

            assert str(exc_info.value) == (
                "Invalid or incomplete introspection result. Ensure that you"
                " are passing the 'data' attribute of an introspection response"
                " and no 'errors' were returned alongside: None.")

            with raises(TypeError) as exc_info:
                # noinspection PyTypeChecker
                build_client_schema({})  # type: ignore

            assert str(exc_info.value) == (
                "Invalid or incomplete introspection result. Ensure that you"
                " are passing the 'data' attribute of an introspection response"
                " and no 'errors' were returned alongside: {}.")

        def throws_when_referenced_unknown_type():
            introspection = introspection_from_schema(dummy_schema)

            introspection["__schema"]["types"] = [
                type_ for type_ in introspection["__schema"]["types"]
                if type_["name"] != "Query"
            ]

            with raises(TypeError) as exc_info:
                build_client_schema(introspection)

            assert str(exc_info.value) == (
                "Invalid or incomplete schema, unknown type: Query."
                " Ensure that a full introspection query is used"
                " in order to build a client schema.")

        def throws_when_missing_definition_for_one_of_the_standard_scalars():
            schema = build_schema("""
                type Query {
                  foo: Float
                }
                """)
            introspection = introspection_from_schema(schema)
            introspection["__schema"]["types"] = [
                type_ for type_ in introspection["__schema"]["types"]
                if type_["name"] != "Float"
            ]

            with raises(TypeError) as exc_info:
                build_client_schema(introspection)

            assert str(exc_info.value).endswith(
                "Invalid or incomplete schema, unknown type: Float."
                " Ensure that a full introspection query is used"
                " in order to build a client schema.")

        def throws_when_type_reference_is_missing_name():
            introspection = introspection_from_schema(dummy_schema)
            query_type = cast(IntrospectionType,
                              introspection["__schema"]["queryType"])
            assert query_type["name"] == "Query"
            del query_type["name"]  # type: ignore

            with raises(TypeError) as exc_info:
                build_client_schema(introspection)

            assert str(exc_info.value) == "Unknown type reference: {}."

        def throws_when_missing_kind():
            introspection = introspection_from_schema(dummy_schema)
            query_type_introspection = next(
                type_ for type_ in introspection["__schema"]["types"]
                if type_["name"] == "Query")
            assert query_type_introspection["kind"] == "OBJECT"
            del query_type_introspection["kind"]

            with raises(
                    TypeError,
                    match=r"^Invalid or incomplete introspection result\."
                    " Ensure that a full introspection query is used"
                    r" in order to build a client schema: {'name': 'Query', .*}\.$",
            ):
                build_client_schema(introspection)

        def throws_when_missing_interfaces():
            introspection = introspection_from_schema(dummy_schema)
            query_type_introspection = cast(
                IntrospectionObjectType,
                next(type_ for type_ in introspection["__schema"]["types"]
                     if type_["name"] == "Query"),
            )

            assert query_type_introspection["interfaces"] == []
            del query_type_introspection["interfaces"]  # type: ignore

            with raises(
                    TypeError,
                    match="^Query interfaces cannot be resolved."
                    " Introspection result missing interfaces:"
                    r" {'kind': 'OBJECT', 'name': 'Query', .*}\.$",
            ):
                build_client_schema(introspection)

        def legacy_support_for_interfaces_with_null_as_interfaces_field():
            introspection = introspection_from_schema(dummy_schema)
            some_interface_introspection = cast(
                IntrospectionInterfaceType,
                next(type_ for type_ in introspection["__schema"]["types"]
                     if type_["name"] == "SomeInterface"),
            )

            assert some_interface_introspection["interfaces"] == []
            some_interface_introspection["interfaces"] = None  # type: ignore

            client_schema = build_client_schema(introspection)
            assert print_schema(client_schema) == print_schema(dummy_schema)

        def throws_when_missing_fields():
            introspection = introspection_from_schema(dummy_schema)
            query_type_introspection = cast(
                IntrospectionObjectType,
                next(type_ for type_ in introspection["__schema"]["types"]
                     if type_["name"] == "Query"),
            )

            assert query_type_introspection["fields"]
            del query_type_introspection["fields"]  # type: ignore

            with raises(
                    TypeError,
                    match="^Query fields cannot be resolved."
                    " Introspection result missing fields:"
                    r" {'kind': 'OBJECT', 'name': 'Query', .*}\.$",
            ):
                build_client_schema(introspection)

        def throws_when_missing_field_args():
            introspection = introspection_from_schema(dummy_schema)
            query_type_introspection = cast(
                IntrospectionObjectType,
                next(type_ for type_ in introspection["__schema"]["types"]
                     if type_["name"] == "Query"),
            )

            field = query_type_introspection["fields"][0]
            assert field["args"]
            del field["args"]  # type: ignore

            with raises(
                    TypeError,
                    match="^Query fields cannot be resolved."
                    r" Introspection result missing field args: {'name': 'foo', .*}\.$",
            ):
                build_client_schema(introspection)

        def throws_when_output_type_is_used_as_an_arg_type():
            introspection = introspection_from_schema(dummy_schema)
            query_type_introspection = cast(
                IntrospectionObjectType,
                next(type_ for type_ in introspection["__schema"]["types"]
                     if type_["name"] == "Query"),
            )

            arg = query_type_introspection["fields"][0]["args"][0]
            assert arg["type"]["name"] == "String"
            arg["type"]["name"] = "SomeUnion"

            with raises(TypeError) as exc_info:
                build_client_schema(introspection)

            assert str(exc_info.value).startswith(
                "Query fields cannot be resolved."
                " Introspection must provide input type for arguments,"
                " but received: SomeUnion.")

        def throws_when_output_type_is_used_as_an_input_value_type():
            introspection = introspection_from_schema(dummy_schema)
            input_object_type_introspection = cast(
                IntrospectionInputObjectType,
                next(type_ for type_ in introspection["__schema"]["types"]
                     if type_["name"] == "SomeInputObject"),
            )

            input_field = input_object_type_introspection["inputFields"][0]
            assert input_field["type"]["name"] == "String"
            input_field["type"]["name"] = "SomeUnion"

            with raises(TypeError) as exc_info:
                build_client_schema(introspection)

            assert str(exc_info.value).startswith(
                "SomeInputObject fields cannot be resolved."
                " Introspection must provide input type for input fields,"
                " but received: SomeUnion.")

        def throws_when_input_type_is_used_as_a_field_type():
            introspection = introspection_from_schema(dummy_schema)
            query_type_introspection = cast(
                IntrospectionObjectType,
                next(type_ for type_ in introspection["__schema"]["types"]
                     if type_["name"] == "Query"),
            )

            field = query_type_introspection["fields"][0]
            assert field["type"]["name"] == "String"
            field["type"]["name"] = "SomeInputObject"

            with raises(TypeError) as exc_info:
                build_client_schema(introspection)

            assert str(exc_info.value).startswith(
                "Query fields cannot be resolved."
                " Introspection must provide output type for fields,"
                " but received: SomeInputObject.")

        def throws_when_missing_possible_types():
            introspection = introspection_from_schema(dummy_schema)
            some_union_introspection = cast(
                IntrospectionUnionType,
                next(type_ for type_ in introspection["__schema"]["types"]
                     if type_["name"] == "SomeUnion"),
            )

            assert some_union_introspection["possibleTypes"]
            del some_union_introspection["possibleTypes"]  # type: ignore

            with raises(
                    TypeError,
                    match="^Introspection result missing possibleTypes:"
                    r" {'kind': 'UNION', 'name': 'SomeUnion', .*}\.$",
            ):
                build_client_schema(introspection)

        def throws_when_missing_enum_values():
            introspection = introspection_from_schema(dummy_schema)
            some_enum_introspection = cast(
                IntrospectionEnumType,
                next(type_ for type_ in introspection["__schema"]["types"]
                     if type_["name"] == "SomeEnum"),
            )

            assert some_enum_introspection["enumValues"]
            del some_enum_introspection["enumValues"]  # type: ignore

            with raises(
                    TypeError,
                    match="^Introspection result missing enumValues:"
                    r" {'kind': 'ENUM', 'name': 'SomeEnum', .*}\.$",
            ):
                build_client_schema(introspection)

        def throws_when_missing_input_fields():
            introspection = introspection_from_schema(dummy_schema)
            some_input_object_introspection = cast(
                IntrospectionInputObjectType,
                next(type_ for type_ in introspection["__schema"]["types"]
                     if type_["name"] == "SomeInputObject"),
            )

            assert some_input_object_introspection["inputFields"]
            del some_input_object_introspection["inputFields"]  # type: ignore

            with raises(
                    TypeError,
                    match="^Introspection result missing inputFields:"
                    r" {'kind': 'INPUT_OBJECT', 'name': 'SomeInputObject', .*}\.$",
            ):
                build_client_schema(introspection)

        def throws_when_missing_directive_locations():
            introspection = introspection_from_schema(dummy_schema)
            some_directive_introspection = introspection["__schema"][
                "directives"][0]

            assert some_directive_introspection["name"] == "SomeDirective"
            assert some_directive_introspection["locations"] == ["QUERY"]
            del some_directive_introspection["locations"]  # type: ignore

            with raises(
                    TypeError,
                    match="^Introspection result missing directive locations:"
                    r" {'name': 'SomeDirective', .*}\.$",
            ):
                build_client_schema(introspection)

        def throws_when_missing_directive_args():
            introspection = introspection_from_schema(dummy_schema)
            some_directive_introspection = introspection["__schema"][
                "directives"][0]

            assert some_directive_introspection["name"] == "SomeDirective"
            assert some_directive_introspection["args"] == []
            del some_directive_introspection["args"]  # type: ignore

            with raises(
                    TypeError,
                    match="^Introspection result missing directive args:"
                    r" {'name': 'SomeDirective', .*}\.$",
            ):
                build_client_schema(introspection)
Exemple #8
0
 def can_build_invalid_schema():
     # Invalid schema, because it is missing query root type
     schema = build_schema("type Mutation")
     errors = validate_schema(schema)
     assert errors
Exemple #9
0

def misplaced_directive(directive_name, placement, line, column):
    return {
        'message': misplaced_directive_message(directive_name, placement),
        'locations': [(line, column)]
    }


schema_with_sdl_directives = build_schema("""
    directive @onSchema on SCHEMA
    directive @onScalar on SCALAR
    directive @onObject on OBJECT
    directive @onFieldDefinition on FIELD_DEFINITION
    directive @onArgumentDefinition on ARGUMENT_DEFINITION
    directive @onInterface on INTERFACE
    directive @onUnion on UNION
    directive @onEnum on ENUM
    directive @onEnumValue on ENUM_VALUE
    directive @onInputObject on INPUT_OBJECT
    directive @onInputFieldDefinition on INPUT_FIELD_DEFINITION
    """)


def describe_known_directives():
    def with_no_directives():
        expect_passes_rule(
            KnownDirectivesRule, """
            query Foo {
              name
              ...Frag
Exemple #10
0
    def unknown_type_references_inside_extension_document():
        schema = build_schema("type A")
        sdl = """
            type B

            type SomeObject implements C {
              e(d: D): E
            }

            union SomeUnion = F | G

            interface SomeInterface {
              i(h: H): I
            }

            input SomeInput {
              j: J
            }

            directive @SomeDirective(k: K) on QUERY

            schema {
              query: L
              mutation: M
              subscription: N
            }
            """

        assert_sdl_errors(
            sdl,
            [
                {
                    "message": "Unknown type 'C'. Did you mean 'A' or 'B'?",
                    "locations": [(4, 40)],
                },
                {
                    "message":
                    "Unknown type 'D'. Did you mean 'ID', 'A', or 'B'?",
                    "locations": [(5, 20)],
                },
                {
                    "message": "Unknown type 'E'. Did you mean 'A' or 'B'?",
                    "locations": [(5, 24)],
                },
                {
                    "message": "Unknown type 'F'. Did you mean 'A' or 'B'?",
                    "locations": [(8, 31)],
                },
                {
                    "message": "Unknown type 'G'. Did you mean 'A' or 'B'?",
                    "locations": [(8, 35)],
                },
                {
                    "message": "Unknown type 'H'. Did you mean 'A' or 'B'?",
                    "locations": [(11, 20)],
                },
                {
                    "message":
                    "Unknown type 'I'. Did you mean 'ID', 'A', or 'B'?",
                    "locations": [(11, 24)],
                },
                {
                    "message": "Unknown type 'J'. Did you mean 'A' or 'B'?",
                    "locations": [(15, 18)],
                },
                {
                    "message": "Unknown type 'K'. Did you mean 'A' or 'B'?",
                    "locations": [(18, 41)],
                },
                {
                    "message": "Unknown type 'L'. Did you mean 'A' or 'B'?",
                    "locations": [(21, 22)],
                },
                {
                    "message": "Unknown type 'M'. Did you mean 'A' or 'B'?",
                    "locations": [(22, 25)],
                },
                {
                    "message": "Unknown type 'N'. Did you mean 'A' or 'B'?",
                    "locations": [(23, 29)],
                },
            ],
            schema,
        )
    def supports_deprecated_directive():
        sdl = dedent(
            """
            enum MyEnum {
              VALUE
              OLD_VALUE @deprecated
              OTHER_VALUE @deprecated(reason: "Terrible reasons")
            }

            input MyInput {
              oldInput: String @deprecated
              otherInput: String @deprecated(reason: "Use newInput")
              newInput: String
            }

            type Query {
              field1: String @deprecated
              field2: Int @deprecated(reason: "Because I said so")
              enum: MyEnum
              field3(oldArg: String @deprecated, arg: String): String
              field4(oldArg: String @deprecated(reason: "Why not?"), arg: String): String
              field5(arg: MyInput): String
            }
            """  # noqa: E501
        )
        assert cycle_sdl(sdl) == sdl

        schema = build_schema(sdl)

        my_enum = assert_enum_type(schema.get_type("MyEnum"))

        value = my_enum.values["VALUE"]
        assert value.deprecation_reason is None

        old_value = my_enum.values["OLD_VALUE"]
        assert old_value.deprecation_reason == "No longer supported"

        other_value = my_enum.values["OTHER_VALUE"]
        assert other_value.deprecation_reason == "Terrible reasons"

        root_fields = assert_object_type(schema.get_type("Query")).fields
        field1 = root_fields["field1"]
        assert field1.deprecation_reason == "No longer supported"
        field2 = root_fields["field2"]
        assert field2.deprecation_reason == "Because I said so"

        input_fields = assert_input_object_type(schema.get_type("MyInput")).fields

        new_input = input_fields["newInput"]
        assert new_input.deprecation_reason is None

        old_input = input_fields["oldInput"]
        assert old_input.deprecation_reason == "No longer supported"

        other_input = input_fields["otherInput"]
        assert other_input.deprecation_reason == "Use newInput"

        field3_old_arg = root_fields["field3"].args["oldArg"]
        assert field3_old_arg.deprecation_reason == "No longer supported"

        field4_old_arg = root_fields["field4"].args["oldArg"]
        assert field4_old_arg.deprecation_reason == "Why not?"
    def specifying_interface_type_using_typename():
        schema = build_schema("""
            type Query {
              characters: [Character]
            }

            interface Character {
              name: String!
            }

            type Human implements Character {
              name: String!
              totalCredits: Int
            }

            type Droid implements Character {
              name: String!
              primaryFunction: String
            }
            """)

        query = """
            {
              characters {
                name
                ... on Human {
                  totalCredits
                }
                ... on Droid {
                  primaryFunction
                }
              }
            }
            """

        root = {
            "characters": [
                {
                    "name": "Han Solo",
                    "totalCredits": 10,
                    "__typename": "Human"
                },
                {
                    "name": "R2-D2",
                    "primaryFunction": "Astromech",
                    "__typename": "Droid",
                },
            ]
        }

        assert graphql_sync(schema, query, root) == (
            {
                "characters": [
                    {
                        "name": "Han Solo",
                        "totalCredits": 10
                    },
                    {
                        "name": "R2-D2",
                        "primaryFunction": "Astromech"
                    },
                ]
            },
            None,
        )
Exemple #13
0
    def describe_return_types_must_be_unambiguous():

        schema = build_schema("""
            interface SomeBox {
              deepBox: SomeBox
              unrelatedField: String
            }

            type StringBox implements SomeBox {
              scalar: String
              deepBox: StringBox
              unrelatedField: String
              listStringBox: [StringBox]
              stringBox: StringBox
              intBox: IntBox
            }

            type IntBox implements SomeBox {
              scalar: Int
              deepBox: IntBox
              unrelatedField: String
              listStringBox: [StringBox]
              stringBox: StringBox
              intBox: IntBox
            }

            interface NonNullStringBox1 {
              scalar: String!
            }

            type NonNullStringBox1Impl implements SomeBox & NonNullStringBox1 {
              scalar: String!
              unrelatedField: String
              deepBox: SomeBox
            }

            interface NonNullStringBox2 {
              scalar: String!
            }

            type NonNullStringBox2Impl implements SomeBox & NonNullStringBox2 {
              scalar: String!
              unrelatedField: String
              deepBox: SomeBox
            }

            type Connection {
              edges: [Edge]
            }

            type Edge {
              node: Node
            }

            type Node {
              id: ID
              name: String
            }

            type Query {
              someBox: SomeBox
              connection: Connection
            }
            """)

        def conflicting_return_types_which_potentially_overlap():
            # This is invalid since an object could potentially be both the
            # Object type IntBox and the interface type NonNullStringBox1.
            # While that condition does not exist in the current schema, the
            # schema could expand in the future to allow this.
            assert_errors(
                """
                {
                  someBox {
                    ...on IntBox {
                      scalar
                    }
                    ...on NonNullStringBox1 {
                      scalar
                    }
                  }
                }
                """,
                [{
                    "message":
                    "Fields 'scalar' conflict because"
                    " they return conflicting types 'Int' and 'String!'."
                    " Use different aliases on the fields"
                    " to fetch both if this was intentional.",
                    "locations": [(5, 23), (8, 23)],
                }],
                schema,
            )

        def compatible_return_shapes_on_different_return_types():
            # In this case `deepBox` returns `SomeBox` in the first usage, and
            # `StringBox` in the second usage. These types are not the same!
            # However this is valid because the return *shapes* are compatible.
            assert_valid(
                """
                {
                  someBox {
                      ... on SomeBox {
                      deepBox {
                        unrelatedField
                      }
                    }
                    ... on StringBox {
                      deepBox {
                        unrelatedField
                      }
                    }
                  }
                }
                """,
                schema=schema,
            )

        def disallows_differing_return_types_despite_no_overlap():
            assert_errors(
                """
                {
                  someBox {
                    ... on IntBox {
                      scalar
                    }
                    ... on StringBox {
                      scalar
                    }
                  }
                }
                """,
                [{
                    "message":
                    "Fields 'scalar' conflict because"
                    " they return conflicting types 'Int' and 'String'."
                    " Use different aliases on the fields"
                    " to fetch both if this was intentional.",
                    "locations": [(5, 23), (8, 23)],
                }],
                schema,
            )

        def reports_correctly_when_a_non_exclusive_follows_an_exclusive():
            assert_errors(
                """
                {
                  someBox {
                    ... on IntBox {
                      deepBox {
                        ...X
                      }
                    }
                  }
                  someBox {
                    ... on StringBox {
                      deepBox {
                        ...Y
                      }
                    }
                  }
                  memoed: someBox {
                    ... on IntBox {
                      deepBox {
                        ...X
                      }
                    }
                  }
                  memoed: someBox {
                    ... on StringBox {
                      deepBox {
                        ...Y
                      }
                    }
                  }
                  other: someBox {
                    ...X
                  }
                  other: someBox {
                    ...Y
                  }
                }
                fragment X on SomeBox {
                  scalar
                }
                fragment Y on SomeBox {
                  scalar: unrelatedField
                }
                """,
                [{
                    "message":
                    "Fields 'other' conflict because"
                    " subfields 'scalar' conflict because"
                    " 'scalar' and 'unrelatedField' are different fields."
                    " Use different aliases on the fields"
                    " to fetch both if this was intentional.",
                    "locations": [(31, 19), (39, 19), (34, 19), (42, 19)],
                    "path":
                    None,
                }],
                schema,
            )

        def disallows_differing_return_type_nullability_despite_no_overlap():
            assert_errors(
                """
                {
                  someBox {
                    ... on NonNullStringBox1 {
                      scalar
                    }
                    ... on StringBox {
                      scalar
                    }
                  }
                }
                """,
                [{
                    "message":
                    "Fields 'scalar' conflict because"
                    " they return conflicting types 'String!' and 'String'."
                    " Use different aliases on the fields"
                    " to fetch both if this was intentional.",
                    "locations": [(5, 23), (8, 23)],
                }],
                schema,
            )

        def disallows_differing_return_type_list_despite_no_overlap_1():
            assert_errors(
                """
                {
                  someBox {
                    ... on IntBox {
                      box: listStringBox {
                        scalar
                      }
                    }
                    ... on StringBox {
                      box: stringBox {
                        scalar
                      }
                    }
                  }
                }
                """,
                [{
                    "message":
                    "Fields 'box' conflict because they return"
                    " conflicting types '[StringBox]' and 'StringBox'."
                    " Use different aliases on the fields"
                    " to fetch both if this was intentional.",
                    "locations": [(5, 23), (10, 23)],
                }],
                schema,
            )

            assert_errors(
                """
                {
                  someBox {
                    ... on IntBox {
                      box: stringBox {
                        scalar
                      }
                    }
                    ... on StringBox {
                      box: listStringBox {
                        scalar
                      }
                    }
                  }
                }
                """,
                [{
                    "message":
                    "Fields 'box' conflict because they return"
                    " conflicting types 'StringBox' and '[StringBox]'."
                    " Use different aliases on the fields"
                    " to fetch both if this was intentional.",
                    "locations": [(5, 23), (10, 23)],
                }],
                schema,
            )

        def disallows_differing_subfields():
            assert_errors(
                """
                {
                  someBox {
                    ... on IntBox {
                      box: stringBox {
                        val: scalar
                        val: unrelatedField
                      }
                    }
                    ... on StringBox {
                      box: stringBox {
                        val: scalar
                      }
                    }
                  }
                }
                """,
                [{
                    "message":
                    "Fields 'val' conflict because"
                    " 'scalar' and 'unrelatedField' are different fields."
                    " Use different aliases on the fields"
                    " to fetch both if this was intentional.",
                    "locations": [(6, 25), (7, 25)],
                }],
                schema,
            )

        def disallows_differing_deep_return_types_despite_no_overlap():
            assert_errors(
                """
                {
                  someBox {
                    ... on IntBox {
                      box: stringBox {
                        scalar
                      }
                    }
                    ... on StringBox {
                      box: intBox {
                        scalar
                      }
                    }
                  }
                }
                """,
                [{
                    "message":
                    "Fields 'box' conflict"
                    " because subfields 'scalar' conflict"
                    " because they return conflicting types 'String' and 'Int'."
                    " Use different aliases on the fields"
                    " to fetch both if this was intentional.",
                    "locations": [(5, 23), (6, 25), (10, 23), (11, 25)],
                    "path":
                    None,
                }],
                schema,
            )

        def allows_non_conflicting_overlapping_types():
            assert_valid(
                """
                {
                  someBox {
                    ... on IntBox {
                      scalar: unrelatedField
                    }
                    ... on StringBox {
                      scalar
                    }
                  }
                }
                """,
                schema=schema,
            )

        def same_wrapped_scalar_return_types():
            assert_valid(
                """
                {
                  someBox {
                    ...on NonNullStringBox1 {
                      scalar
                    }
                    ...on NonNullStringBox2 {
                      scalar
                    }
                  }
                }
                """,
                schema=schema,
            )

        def allows_inline_fragments_without_type_condition():
            assert_valid(
                """
                {
                  a
                  ... {
                    a
                  }
                }
                """,
                schema=schema,
            )

        def compares_deep_types_including_list():
            assert_errors(
                """
                {
                  connection {
                    ...edgeID
                    edges {
                      node {
                        id: name
                      }
                    }
                  }
                }

                fragment edgeID on Connection {
                  edges {
                    node {
                      id
                    }
                  }
                }
                """,
                [{
                    "message":
                    "Fields 'edges' conflict"
                    " because subfields 'node' conflict"
                    " because subfields 'id' conflict"
                    " because 'name' and 'id' are different fields."
                    " Use different aliases on the fields"
                    " to fetch both if this was intentional.",
                    "locations": [
                        (5, 21),
                        (6, 23),
                        (7, 25),
                        (14, 19),
                        (15, 21),
                        (16, 23),
                    ],
                    "path":
                    None,
                }],
                schema,
            )

        def ignores_unknown_types():
            assert_valid(
                """
                {
                  someBox {
                    ...on UnknownType {
                      scalar
                    }
                    ...on NonNullStringBox2 {
                      scalar
                    }
                  }
                }
                """,
                schema=schema,
            )

        def works_for_field_names_that_are_js_keywords():
            schema_with_keywords = build_schema("""
                type Foo {
                  constructor: String
                }

                type Query {
                  foo: Foo
                }
                """)

            assert_valid(
                """
                {
                  foo {
                    constructor
                  }
                }
                """,
                schema=schema_with_keywords,
            )

        def works_for_field_names_that_are_python_keywords():
            schema_with_keywords = build_schema("""
                type Foo {
                  class: String
                }

                type Query {
                  foo: Foo
                }
                """)

            assert_valid(
                """
                {
                  foo {
                    class
                  }
                }
                """,
                schema=schema_with_keywords,
            )
    def describe_specifying_interface_type_using_typename():
        schema = build_schema("""
            type Query {
              characters: [Character]
            }

            interface Character {
              name: String!
            }

            type Human implements Character {
              name: String!
              totalCredits: Int
            }

            type Droid implements Character {
              name: String!
              primaryFunction: String
            }
            """)

        source = """
            {
              characters {
                name
                ... on Human {
                  totalCredits
                }
                ... on Droid {
                  primaryFunction
                }
              }
            }
            """

        expected = (
            {
                "characters": [
                    {
                        "name": "Han Solo",
                        "totalCredits": 10
                    },
                    {
                        "name": "R2-D2",
                        "primaryFunction": "Astromech"
                    },
                ]
            },
            None,
        )

        def using_dicts():
            root_value = {
                "characters": [
                    {
                        "name": "Han Solo",
                        "totalCredits": 10,
                        "__typename": "Human"
                    },
                    {
                        "name": "R2-D2",
                        "primaryFunction": "Astromech",
                        "__typename": "Droid",
                    },
                ]
            }

            assert (graphql_sync(schema=schema,
                                 source=source,
                                 root_value=root_value) == expected)

        def using_objects():
            class Human:
                __typename = "Human"
                name = "Han Solo"
                totalCredits = 10

            class Droid:
                __typename = "Droid"
                name = "R2-D2"
                primaryFunction = "Astromech"

            class RootValue:
                characters = [Human(), Droid()]

            assert (graphql_sync(schema=schema,
                                 source=source,
                                 root_value=RootValue()) == expected)

        def using_inheritance():
            class Character:
                __typename = "Character"

            class Human(Character):
                __typename = "Human"

            class HanSolo(Human):
                name = "Han Solo"
                totalCredits = 10

            class Droid(Character):
                __typename = "Droid"

            class RemoteControlled:
                name = "R2"

            class Mobile:
                name = "D2"

            class R2D2(RemoteControlled, Droid, Mobile):
                name = "R2-D2"
                primaryFunction = "Astromech"

            class RootValue:
                characters = [HanSolo(), R2D2()]

            assert (graphql_sync(schema=schema,
                                 source=source,
                                 root_value=RootValue()) == expected)
Exemple #15
0
    def resolve_type_on_interface_yields_useful_error():
        schema = build_schema("""
            type Query {
              pet: Pet
            }

            interface Pet {
              name: String
            }

            type Cat implements Pet {
              name: String
            }

            type Dog implements Pet {
              name: String
            }
            """)

        document = parse("""
            {
              pet {
                name
              }
            }
            """)

        def expect_error(for_type_name: Any, message: str) -> None:
            root_value = {"pet": {"__typename": for_type_name}}
            result = execute_sync(schema, document, root_value)
            expected = (
                {
                    "pet": None
                },
                [{
                    "message": message,
                    "locations": [(3, 15)],
                    "path": ["pet"]
                }],
            )
            assert result == expected

        expect_error(
            None,
            "Abstract type 'Pet' must resolve"
            " to an Object type at runtime for field 'Query.pet'."
            " Either the 'Pet' type should provide a 'resolve_type' function"
            " or each possible type should provide an 'is_type_of' function.",
        )

        expect_error(
            "Human",
            "Abstract type 'Pet' was resolved to a type 'Human'"
            " that does not exist inside the schema.",
        )

        expect_error(
            "String",
            "Abstract type 'Pet' was resolved to a non-object type 'String'.")

        expect_error(
            "__Schema",
            "Runtime Object type '__Schema' is not a possible type for 'Pet'.",
        )

        # workaround since we can't inject resolve_type into SDL
        schema.get_type("Pet").resolve_type = lambda *_args: []  # type: ignore

        expect_error(
            None,
            "Abstract type 'Pet' must resolve"
            " to an Object type at runtime for field 'Query.pet'"
            " with value {'__typename': None}, received '[]'.",
        )
Exemple #16
0
 def _complete(list_field):
     return execute_sync(
         build_schema("type Query { listField: [String] }"),
         parse("{ listField }"),
         Data(list_field),
     )
    def should_find_all_dangerous_changes():
        old_schema = build_schema("""
            enum EnumType1 {
              VALUE0
              VALUE1
            }

            type Type1 {
              field1(name: String = "test"): String
            }

            type TypeThatGainsInterface1 {
              field1: String
            }

            type TypeInUnion1 {
              field1: String
            }

            union UnionTypeThatGainsAType = TypeInUnion1

            type Query {
              field1: String
            }
            """)

        new_schema = build_schema("""
            enum EnumType1 {
              VALUE0
              VALUE1
              VALUE2
            }

            interface Interface1 {
              field1: String
            }

            type TypeThatGainsInterface1 implements Interface1 {
              field1: String
            }

            type Type1 {
              field1(name: String = "Test"): String
            }

            type TypeInUnion1 {
              field1: String
            }

            type TypeInUnion2 {
              field1: String
            }

            union UnionTypeThatGainsAType = TypeInUnion1 | TypeInUnion2

            type Query {
              field1: String
            }
            """)

        expected_dangerous_changes = [
            (
                DangerousChangeType.ARG_DEFAULT_VALUE_CHANGE,
                "Type1.field1 arg name has changed defaultValue",
            ),
            (
                DangerousChangeType.VALUE_ADDED_TO_ENUM,
                "VALUE2 was added to enum type EnumType1.",
            ),
            (
                DangerousChangeType.INTERFACE_ADDED_TO_OBJECT,
                "Interface1 added to interfaces implemented"
                " by TypeThatGainsInterface1.",
            ),
            (
                DangerousChangeType.TYPE_ADDED_TO_UNION,
                "TypeInUnion2 was added to union type UnionTypeThatGainsAType.",
            ),
        ]

        assert (find_dangerous_changes(
            old_schema, new_schema) == expected_dangerous_changes)
Exemple #18
0
    def describe_specifying_union_type_using_typename():

        schema = build_schema("""
            type Query {
              fruits: [Fruit]
            }

            union Fruit = Apple | Banana

            type Apple {
              color: String
            }

            type Banana {
              length: Int
            }
            """)

        source = """
            {
              fruits {
                ... on Apple {
                  color
                }
                ... on Banana {
                  length
                }
              }
            }
            """

        expected = ({"fruits": [{"color": "green"}, {"length": 5}]}, None)

        def using_dicts():
            root_value = {
                "fruits": [
                    {
                        "color": "green",
                        "__typename": "Apple"
                    },
                    {
                        "length": 5,
                        "__typename": "Banana"
                    },
                ]
            }

            assert (graphql_sync(schema=schema,
                                 source=source,
                                 root_value=root_value) == expected)

        def using_objects():
            class Apple:
                __typename = "Apple"
                color = "green"

            class Banana:
                __typename = "Banana"
                length = 5

            class RootValue:
                fruits = [Apple(), Banana()]

            assert (graphql_sync(schema=schema,
                                 source=source,
                                 root_value=RootValue()) == expected)

        def using_inheritance():
            class Fruit:
                __typename = "Fruit"

            class Apple(Fruit):
                __typename = "Apple"

            class Delicious(Apple):
                color = "golden or red"

            class GoldenDelicious(Delicious):
                color = "golden"

            class RedDelicious(Delicious):
                color = "red"

            class GrannySmith(Apple):
                color = "green"

            class Banana(Fruit):
                __typename = "Banana"
                length = 5

            class RootValue:
                fruits = [
                    GrannySmith(),
                    RedDelicious(),
                    GoldenDelicious(),
                    Banana()
                ]

            assert graphql_sync(schema=schema,
                                source=source,
                                root_value=RootValue()) == (
                                    {
                                        "fruits": [
                                            {
                                                "color": "green"
                                            },
                                            {
                                                "color": "red"
                                            },
                                            {
                                                "color": "golden"
                                            },
                                            {
                                                "length": 5
                                            },
                                        ]
                                    },
                                    None,
                                )
    def describe_throws_when_given_invalid_introspection():
        dummy_schema = build_schema("""
            type Query {
              foo(bar: String): String
            }

            union SomeUnion = Query

            enum SomeEnum { FOO }

            input SomeInputObject {
              foo: String
            }

            directive @SomeDirective on QUERY
            """)

        def throws_when_referenced_unknown_type():
            introspection = introspection_from_schema(dummy_schema)

            introspection["__schema"]["types"] = [
                type_ for type_ in introspection["__schema"]["types"]
                if type_["name"] != "Query"
            ]

            with raises(TypeError) as exc_info:
                build_client_schema(introspection)

            assert str(exc_info.value) == (
                "Invalid or incomplete schema, unknown type: Query."
                " Ensure that a full introspection query is used"
                " in order to build a client schema.")

        def throws_when_type_reference_is_missing_name():
            introspection = introspection_from_schema(dummy_schema)

            assert introspection["__schema"]["queryType"]["name"] == "Query"
            del introspection["__schema"]["queryType"]["name"]

            with raises(TypeError) as exc_info:
                build_client_schema(introspection)

            assert str(exc_info.value) == "Unknown type reference: {}"

        def throws_when_missing_kind():
            introspection = introspection_from_schema(dummy_schema)

            query_type_introspection = [
                type_ for type_ in introspection["__schema"]["types"]
                if type_["name"] == "Query"
            ][0]
            assert query_type_introspection["kind"] == "OBJECT"
            del query_type_introspection["kind"]

            with raises(TypeError) as exc_info:
                build_client_schema(introspection)

            assert str(exc_info.value).startswith(
                "Invalid or incomplete introspection result."
                " Ensure that a full introspection query is used"
                " in order to build a client schema: {'name': 'Query',")

        def throws_when_missing_interfaces():
            introspection = introspection_from_schema(dummy_schema)

            query_type_introspection = [
                type_ for type_ in introspection["__schema"]["types"]
                if type_["name"] == "Query"
            ][0]
            assert query_type_introspection["interfaces"] == []
            del query_type_introspection["interfaces"]

            with raises(TypeError) as exc_info:
                build_client_schema(introspection)

            assert str(exc_info.value).startswith(
                "Introspection result missing interfaces:"
                " {'kind': 'OBJECT', 'name': 'Query',")

        def throws_when_missing_fields():
            introspection = introspection_from_schema(dummy_schema)

            query_type_introspection = [
                type_ for type_ in introspection["__schema"]["types"]
                if type_["name"] == "Query"
            ][0]
            assert query_type_introspection["fields"]
            del query_type_introspection["fields"]

            with raises(TypeError) as exc_info:
                build_client_schema(introspection)

            assert str(exc_info.value).startswith(
                "Query fields cannot be resolved:"
                " Introspection result missing fields:"
                " {'kind': 'OBJECT', 'name': 'Query',")

        def throws_when_missing_field_args():
            introspection = introspection_from_schema(dummy_schema)

            query_type_introspection = [
                type_ for type_ in introspection["__schema"]["types"]
                if type_["name"] == "Query"
            ][0]
            assert query_type_introspection["fields"][0]["args"]
            del query_type_introspection["fields"][0]["args"]

            with raises(TypeError) as exc_info:
                build_client_schema(introspection)

            assert str(exc_info.value).startswith(
                "Query fields cannot be resolved:"
                " Introspection result missing field args: {'name': 'foo',")

        def throws_when_output_type_is_used_as_an_arg_type():
            introspection = introspection_from_schema(dummy_schema)

            query_type_introspection = [
                type_ for type_ in introspection["__schema"]["types"]
                if type_["name"] == "Query"
            ][0]
            assert (query_type_introspection["fields"][0]["args"][0]["type"]
                    ["name"] == "String")
            query_type_introspection["fields"][0]["args"][0]["type"][
                "name"] = "SomeUnion"

            with raises(TypeError) as exc_info:
                build_client_schema(introspection)

            assert str(exc_info.value).startswith(
                "Query fields cannot be resolved:"
                " Introspection must provide input type for arguments,"
                " but received: SomeUnion.")

        def throws_when_input_type_is_used_as_a_field_type():
            introspection = introspection_from_schema(dummy_schema)

            query_type_introspection = [
                type_ for type_ in introspection["__schema"]["types"]
                if type_["name"] == "Query"
            ][0]
            assert query_type_introspection["fields"][0]["type"][
                "name"] == "String"
            query_type_introspection["fields"][0]["type"][
                "name"] = "SomeInputObject"

            with raises(TypeError) as exc_info:
                build_client_schema(introspection)

            assert str(exc_info.value).startswith(
                "Query fields cannot be resolved:"
                " Introspection must provide output type for fields,"
                " but received: SomeInputObject.")

        def throws_when_missing_possible_types():
            introspection = introspection_from_schema(dummy_schema)

            some_union_introspection = [
                type_ for type_ in introspection["__schema"]["types"]
                if type_["name"] == "SomeUnion"
            ][0]
            assert some_union_introspection["possibleTypes"]
            del some_union_introspection["possibleTypes"]

            with raises(TypeError) as exc_info:
                build_client_schema(introspection)

            assert str(exc_info.value).startswith(
                "Introspection result missing possibleTypes:"
                " {'kind': 'UNION', 'name': 'SomeUnion',")

        def throws_when_missing_enum_values():
            introspection = introspection_from_schema(dummy_schema)

            some_enum_introspection = [
                type_ for type_ in introspection["__schema"]["types"]
                if type_["name"] == "SomeEnum"
            ][0]
            assert some_enum_introspection["enumValues"]
            del some_enum_introspection["enumValues"]

            with raises(TypeError) as exc_info:
                build_client_schema(introspection)

            assert str(exc_info.value).startswith(
                "Introspection result missing enumValues:"
                " {'kind': 'ENUM', 'name': 'SomeEnum',")

        def throws_when_missing_input_fields():
            introspection = introspection_from_schema(dummy_schema)

            some_input_object_introspection = [
                type_ for type_ in introspection["__schema"]["types"]
                if type_["name"] == "SomeInputObject"
            ][0]
            assert some_input_object_introspection["inputFields"]
            del some_input_object_introspection["inputFields"]

            with raises(TypeError) as exc_info:
                build_client_schema(introspection)

            assert str(exc_info.value).startswith(
                "Introspection result missing inputFields:"
                " {'kind': 'INPUT_OBJECT', 'name': 'SomeInputObject',")

        def throws_when_missing_directive_locations():
            introspection = introspection_from_schema(dummy_schema)

            some_directive_introspection = introspection["__schema"][
                "directives"][0]
            assert some_directive_introspection["name"] == "SomeDirective"
            assert some_directive_introspection["locations"] == ["QUERY"]
            del some_directive_introspection["locations"]

            with raises(TypeError) as exc_info:
                build_client_schema(introspection)

            assert str(exc_info.value).startswith(
                "Introspection result missing directive locations:"
                " {'name': 'SomeDirective',")

        def throws_when_missing_directive_args():
            introspection = introspection_from_schema(dummy_schema)

            some_directive_introspection = introspection["__schema"][
                "directives"][0]
            assert some_directive_introspection["name"] == "SomeDirective"
            assert some_directive_introspection["args"] == []
            del some_directive_introspection["args"]

            with raises(TypeError) as exc_info:
                build_client_schema(introspection)

            assert str(exc_info.value).startswith(
                "Introspection result missing directive args:"
                " {'name': 'SomeDirective',")