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)]
         }])