예제 #1
0
def test_interface_field_added_and_removed():
    a = schema("""
    interface Person {
      name: String
      age: Int
    }
    """)
    b = schema("""
    interface Person {
      name: String
      age: Int
      favorite_number: Float
    }
    """)

    diff = Schema(a, b).diff()
    assert diff and len(diff) == 1
    assert diff[
        0].message == "Field `favorite_number` of type `Float` was added to interface `Person`"
    assert diff[0].path == 'Person.favorite_number'
    assert diff[0].criticality == Criticality.dangerous(
        'Adding an interface to an object type may break existing clients '
        'that were not programming defensively against a new possible type.')

    diff = Schema(b, a).diff()
    assert diff[
        0].message == "Field `favorite_number` was removed from interface `Person`"
    assert diff[0].path == 'Person.favorite_number'
    assert diff[0].criticality == Criticality.dangerous(
        'Removing an interface field can break existing queries that use this in a fragment spread.'
    )
예제 #2
0
def test_enums_added():
    a = schema("""
    type Query {
        a: String
    }
    enum Letters {
        A
        B
    }
    """)
    b = schema("""
    type Query {
        a: String
    }
    enum Letters {
        A
        B
        C
        D
    }
    """)
    diff = Schema(a, b).diff()
    assert len(diff) == 2
    expected_diff = {
        "Enum value `C` was added to `Letters` enum",
        "Enum value `D` was added to `Letters` enum",
    }
    expected_paths = {'Letters.C', 'Letters.D'}
    for change in diff:
        assert change.message in expected_diff
        assert change.path in expected_paths
        assert change.criticality == Criticality.dangerous(
            "Adding an enum value may break existing clients that "
            "were not programming defensively against an added case when querying an enum."
        )
예제 #3
0
def test_directive_default_value_changed():
    default_100 = schema("""
    directive @limit(number: Int=100) on FIELD_DEFINITION
    type A {
        a: String
    }
    """)
    default_0 = schema("""
    directive @limit(number: Int=0) on FIELD_DEFINITION
    type A {
        a: String
    }
    """)

    diff = Schema(default_100, default_0).diff()
    assert diff and len(diff) == 1
    expected_message = (
        'Default value for argument `number` on `@limit` directive changed from `100` to `0`'
    )
    assert diff[0].message == expected_message
    assert diff[0].path == '@limit'
    assert diff[0].criticality == Criticality.dangerous(
        'Changing the default value for an argument may change '
        'the runtime behaviour of a field if it was never provided.'
    )
예제 #4
0
    class DangerousChange(Change):
        criticality = Criticality.dangerous('this is dangerous')

        def message(self):
            return 'test message'

        def path(self):
            return 'test path'
 def __init__(self, directive, arg_name, old_default, new_default):
     self.criticality = Criticality.dangerous(
         "Changing the default value for an argument may change the runtime "
         "behaviour of a field if it was never provided.")
     self.directive = directive
     self.arg_name = arg_name
     self.old_default = old_default
     self.new_default = new_default
def test_add_type_to_union():
    two_types = schema("""
    type Query {
        c: Int
    }
    type Result {
        message: String
    }
    type Error {
        message: String
        details: String
    }
        type Unknown {
        message: String
        details: String
        traceback: String
    }

    union Outcome = Result | Error
    """)

    three_types = schema("""
    type Query {
        c: Int
    }
    type Result {
        message: String
    }
    type Error {
        message: String
        details: String
    }
    type Unknown {
        message: String
        details: String
        traceback: String
    }

    union Outcome = Result | Error | Unknown
    """)
    diff = Schema(two_types, three_types).diff()
    assert diff and len(diff) == 1
    assert diff[
        0].message == "Union member `Unknown` was added to `Outcome` Union type"
    assert diff[0].path == 'Outcome'
    assert diff[0].criticality == Criticality.dangerous(
        'Adding a possible type to Unions may break existing clients '
        'that were not programming defensively against a new possible type.')

    diff = Schema(three_types, two_types).diff()
    assert diff and len(diff) == 1
    assert diff[
        0].message == "Union member `Unknown` was removed from `Outcome` Union type"
    assert diff[0].path == 'Outcome'
    assert diff[0].criticality == Criticality.breaking(
        'Removing a union member from a union can break queries that use this union member in a fragment spread'
    )
    class MyChange(Change):
        criticality = Criticality.dangerous('This might be dangerous')

        @property
        def message(self):
            return 'Unagi'

        @property
        def path(self):
            return 'Unagi.Friends'
예제 #8
0
class FieldArgumentDefaultValueChanged(FieldAbstractArgumentChange):
    criticality = Criticality.dangerous(
        "Changing the default value for an argument may change the runtime "
        "behaviour of a field if it was never provided.")

    @property
    def message(self):
        return (
            f"Default value for argument `{self.arg_name}` on field `{self.parent}.{self.field_name}` "
            f"changed from `{self.old_arg.default_value!r}` to `{self.new_arg.default_value!r}`"
        )
예제 #9
0
 def __init__(self, parent, field_name, field):
     self.parent = parent
     self.field_name = field_name
     self.field = field
     self.criticality = (
         Criticality.dangerous(
             "Removing deprecated fields without sufficient time for clients "
             "to update their queries may break their code"
         ) if field.deprecation_reason else Criticality.breaking(
             "Removing a field is a breaking change. It is preferred to deprecate the field before removing it."
         )
     )
예제 #10
0
class UnionMemberAdded(Change):

    criticality = Criticality.dangerous(
        "Adding a possible type to Unions may break existing clients "
        "that were not programming defensively against a new possible type.")

    def __init__(self, union, value):
        self.union = union
        self.value = value

    @property
    def message(self):
        return f"Union member `{self.value}` was added to `{self.union.name}` Union type"

    @property
    def path(self):
        return f"{self.union.name}"
예제 #11
0
class NewInterfaceImplemented(Change):

    criticality = Criticality.dangerous(
        "Adding an interface to an object type may break existing clients "
        "that were not programming defensively against a new possible type."
    )

    def __init__(self, interface, type_):
        self.interface = interface
        self.type_ = type_

    @property
    def message(self):
        return f"`{self.type_.name}` implements new interface `{self.interface.name}`"

    @property
    def path(self):
        return f"{self.type_}"
예제 #12
0
class InterfaceFieldRemoved(Change):

    criticality = Criticality.dangerous(
        "Removing an interface field can break existing "
        "queries that use this in a fragment spread."
    )

    def __init__(self, interface, field_name):
        self.interface = interface
        self.field_name = field_name

    @property
    def message(self):
        return f"Field `{self.field_name}` was removed from interface `{self.interface}`"

    @property
    def path(self):
        return f"{self.interface.name}.{self.field_name}"
예제 #13
0
class InterfaceFieldAdded(Change):

    criticality = Criticality.dangerous(
        "Adding an interface to an object type may break existing clients "
        "that were not programming defensively against a new possible type."
    )

    def __init__(self, interface, field_name, field):
        self.interface = interface
        self.field_name = field_name
        self.field = field

    @property
    def message(self):
        return f"Field `{self.field_name}` of type `{self.field.type}` was added to interface `{self.interface}`"

    @property
    def path(self):
        return f"{self.interface.name}.{self.field_name}"
def test_input_field_default_value_changed():
    a = schema("""
    input Params {
        love: Int = 0
    }
    """)
    b = schema("""
    input Params {
        love: Int = 100
    }
    """)
    diff = Schema(a, b).diff()
    assert diff and len(diff) == 1
    assert diff[
        0].message == "Default value for input field `Params.love` changed from `0` to `100`"
    assert diff[0].path == 'Params.love'
    assert diff[0].criticality == Criticality.dangerous(
        'Changing the default value for an argument may change '
        'the runtime behaviour of a field if it was never provided.')
예제 #15
0
class InputFieldDefaultChanged(Change):

    criticality = Criticality.dangerous(
        "Changing the default value for an argument may change the runtime "
        "behaviour of a field if it was never provided."
    )

    def __init__(self, input_, name, new_field, old_field):
        self.input_ = input_
        self.name = name
        self.new_field = new_field
        self.old_field = old_field

    @property
    def message(self):
        return (
            f"Default value for input field `{self.input_.name}.{self.name}` "
            f"changed from `{self.old_field.default_value!r}` to `{self.new_field.default_value!r}`"
        )

    @property
    def path(self):
        return f"{self.input_.name}.{self.name}"