def disallows_differing_return_type_list_despite_no_overlap_1(): expect_fails_rule_with_schema( schema, OverlappingFieldsCanBeMergedRule, """ { someBox { ... on IntBox { box: listStringBox { scalar } } ... on StringBox { box: stringBox { scalar } } } } """, [{ "message": fields_conflict_message( "box", "they return conflicting types" " [StringBox] and StringBox", ), "locations": [(5, 27), (10, 27)], }], ) expect_fails_rule_with_schema( schema, OverlappingFieldsCanBeMergedRule, """ { someBox { ... on IntBox { box: stringBox { scalar } } ... on StringBox { box: listStringBox { scalar } } } } """, [{ "message": fields_conflict_message( "box", "they return conflicting types" " StringBox and [StringBox]", ), "locations": [(5, 27), (10, 27)], }], )
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_conflict_message( "box", "they return conflicting types" " [StringBox] and StringBox", ), "locations": [(5, 23), (10, 23)], }], schema, ) assert_errors( """ { someBox { ... on IntBox { box: stringBox { scalar } } ... on StringBox { box: listStringBox { scalar } } } } """, [{ "message": fields_conflict_message( "box", "they return conflicting types" " StringBox and [StringBox]", ), "locations": [(5, 23), (10, 23)], }], schema, )
def disallows_differing_return_type_list_despite_no_overlap_1(): expect_fails_rule_with_schema( schema, OverlappingFieldsCanBeMergedRule, """ { someBox { ... on IntBox { box: listStringBox { scalar } } ... on StringBox { box: stringBox { scalar } } } } """, [{ 'message': fields_conflict_message( 'box', 'they return conflicting types' ' [StringBox] and StringBox'), 'locations': [(5, 27), (10, 27)] }]) expect_fails_rule_with_schema( schema, OverlappingFieldsCanBeMergedRule, """ { someBox { ... on IntBox { box: stringBox { scalar } } ... on StringBox { box: listStringBox { scalar } } } } """, [{ 'message': fields_conflict_message( 'box', 'they return conflicting types' ' StringBox and [StringBox]'), 'locations': [(5, 27), (10, 27)] }])
def disallows_differing_deep_return_types_despite_no_overlap(): expect_fails_rule_with_schema( schema, OverlappingFieldsCanBeMergedRule, """ { someBox { ... on IntBox { box: stringBox { scalar } } ... on StringBox { box: intBox { scalar } } } } """, [{ 'message': fields_conflict_message('box', [ ('scalar', 'they return conflicting types String and Int') ]), 'locations': [(5, 27), (6, 29), (10, 27), (11, 29)], 'path': None }])
def reports_deep_conflict_in_nested_fragments(): expect_fails_rule( OverlappingFieldsCanBeMergedRule, """ { field { ...F }, field { ...I } } fragment F on T { x: a ...G } fragment G on T { y: c } fragment I on T { y: d ...J } fragment J on T { x: b } """, [{ 'message': fields_conflict_message( 'field', [('x', 'a and b are different fields'), ('y', 'c and d are different fields')]), 'locations': [(3, 15), (11, 15), (15, 15), (6, 15), (22, 15), (18, 15)], 'path': None }])
def reports_deep_conflict_to_nearest_common_ancestor_in_fragments(): expect_fails_rule( OverlappingFieldsCanBeMergedRule, """ { field { ...F } field { ...F } } fragment F on T { deepField { deeperField { x: a } deeperField { x: b } }, deepField { deeperField { y } } } """, [{ 'message': fields_conflict_message( 'deeperField', [('x', 'a and b are different fields')]), 'locations': [(12, 17), (13, 19), (15, 17), (16, 19)] }])
def very_deep_conflict(): expect_fails_rule( OverlappingFieldsCanBeMergedRule, """ { field { deepField { x: a } }, field { deepField { x: b } } } """, [{ 'message': fields_conflict_message('field', [ ('deepField', [('x', 'a and b are different fields')]) ]), 'locations': [(3, 15), (4, 17), (5, 19), (8, 15), (9, 17), (10, 19)], 'path': None }])
def compares_deep_types_including_list(): expect_fails_rule_with_schema( schema, OverlappingFieldsCanBeMergedRule, """ { connection { ...edgeID edges { node { id: name } } } } fragment edgeID on Connection { edges { node { id } } } """, [{ 'message': fields_conflict_message('edges', [ ('node', [('id', 'name and id are different fields')]) ]), 'locations': [(5, 25), (6, 27), (7, 29), (14, 23), (15, 25), (16, 27)], 'path': None }])
def reports_deep_conflict_to_nearest_common_ancestor_in_fragments(): assert_errors( """ { field { ...F } field { ...F } } fragment F on T { deepField { deeperField { x: a } deeperField { x: b } }, deepField { deeperField { y } } } """, [{ "message": fields_conflict_message( "deeperField", [("x", "a and b are different fields")]), "locations": [(12, 17), (13, 19), (15, 17), (16, 19)], }], )
def disallows_differing_deep_return_types_despite_no_overlap(): expect_fails_rule_with_schema( schema, OverlappingFieldsCanBeMergedRule, """ { someBox { ... on IntBox { box: stringBox { scalar } } ... on StringBox { box: intBox { scalar } } } } """, [{ "message": fields_conflict_message( "box", [( "scalar", "they return conflicting types String and Int", )], ), "locations": [(5, 27), (6, 29), (10, 27), (11, 29)], "path": None, }], )
def very_deep_conflict(): assert_errors( """ { field { deepField { x: a } }, field { deepField { x: b } } } """, [{ "message": fields_conflict_message( "field", [("deepField", [("x", "a and b are different fields")])], ), "locations": [ (3, 15), (4, 17), (5, 19), (8, 15), (9, 17), (10, 19), ], "path": None, }], )
def reports_deep_conflict_to_nearest_common_ancestor(): assert_errors( """ { field { deepField { x: a } deepField { x: b } }, field { deepField { y } } } """, [{ "message": fields_conflict_message( "deepField", [("x", "a and b are different fields")]), "locations": [(4, 17), (5, 19), (7, 17), (8, 19)], }], )
def deep_conflict_with_multiple_issues(): assert_errors( """ { field { x: a y: c }, field { x: b y: d } } """, [{ "message": fields_conflict_message( "field", [ ("x", "a and b are different fields"), ("y", "c and d are different fields"), ], ), "locations": [(3, 15), (4, 17), (5, 17), (7, 15), (8, 17), (9, 17)], "path": None, }], )
def disallows_differing_subfields(): assert_errors( """ { someBox { ... on IntBox { box: stringBox { val: scalar val: unrelatedField } } ... on StringBox { box: stringBox { val: scalar } } } } """, [{ "message": fields_conflict_message( "val", "scalar and unrelatedField are different fields"), "locations": [(6, 25), (7, 25)], }], schema, )
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. expect_fails_rule_with_schema( schema, OverlappingFieldsCanBeMergedRule, """ { someBox { ...on IntBox { scalar } ...on NonNullStringBox1 { scalar } } } """, [{ "message": fields_conflict_message( "scalar", "they return conflicting types Int and String!"), "locations": [(5, 27), (8, 27)], }], )
def reports_deep_conflict_to_nearest_common_ancestor(): expect_fails_rule( OverlappingFieldsCanBeMergedRule, """ { field { deepField { x: a } deepField { x: b } }, field { deepField { y } } } """, [{ "message": fields_conflict_message( "deepField", [("x", "a and b are different fields")]), "locations": [(4, 17), (5, 19), (7, 17), (8, 19)], }], )
def reports_deep_conflict_to_nearest_common_ancestor_in_fragments(): expect_fails_rule( OverlappingFieldsCanBeMergedRule, """ { field { ...F } field { ...F } } fragment F on T { deepField { deeperField { x: a } deeperField { x: b } }, deepField { deeperField { y } } } """, [{ "message": fields_conflict_message( "deeperField", [("x", "a and b are different fields")]), "locations": [(12, 17), (13, 19), (15, 17), (16, 19)], }], )
def deep_conflict_with_multiple_issues(): expect_fails_rule( OverlappingFieldsCanBeMergedRule, """ { field { x: a y: c }, field { x: b y: d } } """, [{ "message": fields_conflict_message( "field", [ ("x", "a and b are different fields"), ("y", "c and d are different fields"), ], ), "locations": [(3, 15), (4, 17), (5, 17), (7, 15), (8, 17), (9, 17)], "path": None, }], )
def very_deep_conflict(): expect_fails_rule( OverlappingFieldsCanBeMergedRule, """ { field { deepField { x: a } }, field { deepField { x: b } } } """, [{ "message": fields_conflict_message( "field", [("deepField", [("x", "a and b are different fields")])], ), "locations": [ (3, 15), (4, 17), (5, 19), (8, 15), (9, 17), (10, 19), ], "path": None, }], )
def disallows_differing_deep_return_types_despite_no_overlap(): assert_errors( """ { someBox { ... on IntBox { box: stringBox { scalar } } ... on StringBox { box: intBox { scalar } } } } """, [{ "message": fields_conflict_message( "box", [( "scalar", "they return conflicting types String and Int", )], ), "locations": [(5, 23), (6, 25), (10, 23), (11, 25)], "path": None, }], schema, )
def disallows_differing_subfields(): expect_fails_rule_with_schema( schema, OverlappingFieldsCanBeMergedRule, """ { someBox { ... on IntBox { box: stringBox { val: scalar val: unrelatedField } } ... on StringBox { box: stringBox { val: scalar } } } } """, [{ "message": fields_conflict_message( "val", "scalar and unrelatedField are different fields"), "locations": [(6, 29), (7, 29)], }], )
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_conflict_message( "scalar", "they return conflicting types Int and String!"), "locations": [(5, 23), (8, 23)], }], schema, )
def reports_each_conflict_once(): expect_fails_rule( OverlappingFieldsCanBeMergedRule, """ { f1 { ...A ...B } f2 { ...B ...A } f3 { ...A ...B x: c } } fragment A on Type { x: a } fragment B on Type { x: b } """, [ { "message": fields_conflict_message("x", "a and b are different fields"), "locations": [(18, 15), (21, 15)], }, { "message": fields_conflict_message("x", "c and a are different fields"), "locations": [(14, 17), (18, 15)], }, { "message": fields_conflict_message("x", "c and b are different fields"), "locations": [(14, 17), (21, 15)], }, ], )
def reports_each_conflict_once(): assert_errors( """ { f1 { ...A ...B } f2 { ...B ...A } f3 { ...A ...B x: c } } fragment A on Type { x: a } fragment B on Type { x: b } """, [ { "message": fields_conflict_message("x", "a and b are different fields"), "locations": [(18, 15), (21, 15)], }, { "message": fields_conflict_message("x", "c and a are different fields"), "locations": [(14, 17), (18, 15)], }, { "message": fields_conflict_message("x", "c and b are different fields"), "locations": [(14, 17), (21, 15)], }, ], )
def error_message_contains_hint_for_alias_conflict(): # The error template should end with a hint for the user to try # using different aliases. error = fields_conflict_message("x", "a and b are different fields") assert error == ( "Fields 'x' conflict because a and b are different fields." " Use different aliases on the fields to fetch both" " if this was intentional.")
def reports_correctly_when_a_non_exclusive_follows_an_exclusive(): expect_fails_rule_with_schema( schema, OverlappingFieldsCanBeMergedRule, """ { 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_conflict_message('other', [ ('scalar', 'scalar and unrelatedField are different fields') ]), 'locations': [(31, 23), (39, 23), (34, 23), (42, 23)], 'path': None }])
def conflicting_args(): expect_fails_rule( OverlappingFieldsCanBeMergedRule, """ fragment conflictingArgs on Dog { doesKnowCommand(dogCommand: SIT) doesKnowCommand(dogCommand: HEEL) } """, [{ 'message': fields_conflict_message('doesKnowCommand', 'they have differing arguments'), 'locations': [(3, 15), (4, 15)] }])
def alias_masking_direct_field_access(): expect_fails_rule( OverlappingFieldsCanBeMergedRule, """ fragment aliasMaskingDirectFieldAccess on Dog { name: nickname name } """, [{ 'message': fields_conflict_message( 'name', 'nickname and name are different fields'), 'locations': [(3, 15), (4, 15)] }])
def finds_invalid_case_even_with_immediately_recursive_fragment(): expect_fails_rule( OverlappingFieldsCanBeMergedRule, """ fragment sameAliasesWithDifferentFieldTargets on Dog { ...sameAliasesWithDifferentFieldTargets fido: name fido: nickname } """, [{ 'message': fields_conflict_message( 'fido', 'name and nickname are different fields'), 'locations': [(4, 15), (5, 15)] }])
def reports_each_conflict_once(): expect_fails_rule( OverlappingFieldsCanBeMergedRule, """ { f1 { ...A ...B } f2 { ...B ...A } f3 { ...A ...B x: c } } fragment A on Type { x: a } fragment B on Type { x: b } """, [{ 'message': fields_conflict_message('x', 'a and b are different fields'), 'locations': [(18, 15), (21, 15)] }, { 'message': fields_conflict_message('x', 'c and a are different fields'), 'locations': [(14, 17), (18, 15)] }, { 'message': fields_conflict_message('x', 'c and b are different fields'), 'locations': [(14, 17), (21, 15)] }])