diff --git a/src/HotChocolate/Core/src/Validation/DocumentValidatorContext.cs b/src/HotChocolate/Core/src/Validation/DocumentValidatorContext.cs index fe2a3c3f599..e5588b5520f 100644 --- a/src/HotChocolate/Core/src/Validation/DocumentValidatorContext.cs +++ b/src/HotChocolate/Core/src/Validation/DocumentValidatorContext.cs @@ -61,6 +61,9 @@ public IOutputType NonNullString public IDictionary> FieldSets { get; } = new Dictionary>(); + public ISet<(FieldNode, FieldNode)> FieldTuples { get; } = + new HashSet<(FieldNode, FieldNode)>(); + public ISet VisitedFragments { get; } = new HashSet(); public IDictionary VariableValues { get; } = @@ -130,6 +133,7 @@ public void Clear() Path.Clear(); SelectionSets.Clear(); FieldSets.Clear(); + FieldTuples.Clear(); VisitedFragments.Clear(); VariableValues.Clear(); Variables.Clear(); diff --git a/src/HotChocolate/Core/src/Validation/IDocumentValidatorContext.cs b/src/HotChocolate/Core/src/Validation/IDocumentValidatorContext.cs index 2fcb7d5b2e2..3cb3c6516c5 100644 --- a/src/HotChocolate/Core/src/Validation/IDocumentValidatorContext.cs +++ b/src/HotChocolate/Core/src/Validation/IDocumentValidatorContext.cs @@ -35,6 +35,11 @@ public interface IDocumentValidatorContext : ISyntaxVisitorContext /// A dictionary to store field infos per selection set. /// IDictionary> FieldSets { get; } + + /// + /// A set of field tuples. + /// + ISet<(FieldNode, FieldNode)> FieldTuples { get; } /// /// Gets a set of already visited fragment names. diff --git a/src/HotChocolate/Core/src/Validation/Rules/FieldVisitor.cs b/src/HotChocolate/Core/src/Validation/Rules/FieldVisitor.cs index 51e8391135d..848125a12f5 100644 --- a/src/HotChocolate/Core/src/Validation/Rules/FieldVisitor.cs +++ b/src/HotChocolate/Core/src/Validation/Rules/FieldVisitor.cs @@ -42,7 +42,10 @@ internal sealed class FieldVisitor : TypeDocumentValidatorVisitor { if (context.FieldSets.Count > 0) { - TryMergeFieldsInSet(context, context.FieldSets[node.SelectionSet]); + foreach (SelectionSetNode selectionSet in context.FieldSets.Keys) + { + TryMergeFieldsInSet(context, context.FieldSets[selectionSet]); + } } if (node.SelectionSet.Selections.Count == 0) @@ -75,7 +78,7 @@ internal sealed class FieldVisitor : TypeDocumentValidatorVisitor if (context.Types.TryPeek(out IType type) && type.NamedType() is IComplexOutputType ct) { - if (ct.Fields.TryGetField(node.Name.Value, out IOutputField of)) + if (ct.Fields.TryGetField(node.Name.Value, out IOutputField? of)) { fields.Add(new FieldInfo(context.Types.Peek(), of.Type, node)); @@ -155,6 +158,7 @@ internal sealed class FieldVisitor : TypeDocumentValidatorVisitor context.SelectionSets.Pop(); } } + return Continue; } @@ -235,14 +239,14 @@ private static bool IsTypeNameField(NameString fieldName) { TryMergeFieldsInSet(context, fieldA, fieldB); } - else + else if(context.FieldTuples.Add((fieldA.Field, fieldB.Field))) { context.Errors.Add( context.FieldsAreNotMergable(fieldA, fieldB)); } } } - else + else if(context.FieldTuples.Add((fieldA.Field, fieldB.Field))) { context.Errors.Add(context.FieldsAreNotMergable(fieldA, fieldB)); } diff --git a/src/HotChocolate/Core/test/Validation.Tests/FieldSelectionMergingRuleTests.cs b/src/HotChocolate/Core/test/Validation.Tests/FieldSelectionMergingRuleTests.cs index 061a5a2a878..7cb37021b91 100644 --- a/src/HotChocolate/Core/test/Validation.Tests/FieldSelectionMergingRuleTests.cs +++ b/src/HotChocolate/Core/test/Validation.Tests/FieldSelectionMergingRuleTests.cs @@ -1,4 +1,5 @@ using ChilliCream.Testing; +using HotChocolate.Types; using Microsoft.Extensions.DependencyInjection; using Xunit; @@ -309,7 +310,6 @@ ... FooLevel2 t.Message)); } - [Fact] public void ShortHandQueryWithDupMergableFieldInSecondLevelFragment() { @@ -368,9 +368,15 @@ ... interfaceFieldSelection } [Fact] - public void TypeNameFieldOnObjectIsMergable() + public void TypeNameFieldOnObjectIsMergeable() { ExpectValid(@" + { + catOrDog { + ... interfaceFieldSelection + } + } + fragment interfaceFieldSelection on Cat { __typename __typename @@ -383,5 +389,763 @@ public void InvalidFieldsShouldNotRaiseValidationError() { ExpectValid(FileResource.Open("InvalidIntrospectionQuery.graphql")); } + + [Fact] + public void UniqueFields() + { + ExpectValid(@" + { + catOrDog { + ... uniqueFields + } + } + + fragment uniqueFields on Dog { + name + nickname + } + "); + } + + [Fact] + public void IdenticalFields() + { + ExpectValid(@" + { + catOrDog { + ... mergeIdenticalFields + } + } + + fragment mergeIdenticalFields on Dog { + name + name + } + "); + } + + [Fact] + public void IdenticalFieldsWithIdenticalArgs() + { + ExpectValid(@" + { + catOrDog { + ... mergeIdenticalFieldsWithIdenticalArgs + } + } + + fragment mergeIdenticalFieldsWithIdenticalArgs on Dog { + doesKnowCommand(dogCommand: SIT) + doesKnowCommand(dogCommand: SIT) + } + "); + } + + [Fact] + public void DifferentArgsWithDifferentAliases() + { + ExpectValid(@" + { + catOrDog { + ... differentArgsWithDifferentAliases + } + } + + fragment differentArgsWithDifferentAliases on Dog { + knowsSit: doesKnowCommand(dogCommand: SIT) + knowsDown: doesKnowCommand(dogCommand: DOWN) + } + "); + } + + [Fact] + public void DifferentDirectivesWithDifferentAliases() + { + ExpectValid(@" + { + catOrDog { + ... differentDirectivesWithDifferentAliases + } + } + + fragment differentDirectivesWithDifferentAliases on Dog { + nameIfTrue: name @include(if: true) + nameIfFalse: name @include(if: false) + } + "); + } + + [Fact] + public void DifferentSkipIncludeDirectivesAccepted() + { + ExpectValid(@" + { + catOrDog { + ... differentDirectivesWithDifferentAliases + } + } + + fragment differentDirectivesWithDifferentAliases on Dog { + name @include(if: true) + name @include(if: false) + } + "); + } + + [Fact] + public void SameAliasesWithDifferentFieldTargets() + { + ExpectErrors(@" + { + catOrDog { + ... sameAliasesWithDifferentFieldTargets + } + } + + fragment sameAliasesWithDifferentFieldTargets on Dog { + fido: name + fido: nickname + } + "); + } + + [Fact] + public void SameAliasesAllowedOnNonOverlappingFields() + { + ExpectErrors(@" + { + catOrDog { + ... sameAliasesWithDifferentFieldTargets + } + } + + fragment sameAliasesWithDifferentFieldTargets on Pet { + ... on Dog { + name + } + ... on Cat { + name: nickname + } + } + "); + } + + [Fact] + public void AliasMaskingDirectFieldAccess() + { + ExpectErrors(@" + { + catOrDog { + ... aliasMaskingDirectFieldAccess + } + } + + fragment aliasMaskingDirectFieldAccess on Dog { + name: nickname + name + } + "); + } + + [Fact] + public void DifferentArgsSecondAddsAnArgument() + { + ExpectErrors(@" + { + catOrDog { + ... conflictingArgs + } + } + + fragment conflictingArgs on Dog { + doesKnowCommand + doesKnowCommand(dogCommand: HEEL) + } + "); + } + + [Fact] + public void DifferentArgsSecondMissingAnArgument() + { + ExpectErrors(@" + { + catOrDog { + ... conflictingArgs + } + } + + fragment conflictingArgs on Dog { + doesKnowCommand(dogCommand: SIT) + doesKnowCommand + } + "); + } + + [Fact] + public void ConflictingArgValues() + { + ExpectErrors(@" + { + catOrDog { + ... conflictingArgs + } + } + + fragment conflictingArgs on Dog { + doesKnowCommand(dogCommand: SIT) + doesKnowCommand(dogCommand: HEEL) + } + "); + } + + [Fact] + public void ConflictingArgNames() + { + ExpectErrors(@" + { + catOrDog { + ... conflictingArgs + } + } + + fragment conflictingArgs on Dog { + isAtLocation(x: 0) + isAtLocation(y: 0) + } + "); + } + + [Fact] + public void AllowsDifferentArgsWhereNoConflictIsPossible() + { + ExpectValid(@" + { + catOrDog { + ... conflictingArgs + } + } + + fragment conflictingArgs on Pet { + ... on Dog { + name(surname: true) + } + ... on Cat { + name + } + } + "); + } + + [Fact] + public void EncountersConflictInFragments() + { + ExpectErrors(@" + { + ...A + ...B + } + + fragment A on Query { + x: a + } + + fragment B on Query { + x: b + } + "); + } + + [Fact] + public void ReportsEachConflictOnce() + { + ExpectErrors(@" + { + f1 { + ...A + ...B + } + f2 { + ...B + ...A + } + f3 { + ...A + ...B + x: c + } + } + + fragment A on Query { + x: a + } + + fragment B on Query { + x: b + } + "); + } + + [Fact] + public void DeepConflict() + { + ExpectErrors(@" + { + f1 { + x: a + } + f1 { + x: b + } + } + "); + } + + [Fact] + public void DeepConflictWithMultipleIssues() + { + ExpectErrors(@" + { + f1 { + x: a + y: c + }, + f1 { + x: b + y: d + } + } + "); + } + + [Fact] + public void VeryDeepConflict() + { + ExpectErrors(@" + { + f1 { + f2 { + x: a + } + } + f1 { + f2 { + x: b + } + } + } + "); + } + + [Fact] + public void ReportsDeepConflictToNearestCommonAncestor() + { + ExpectErrors(@" + { + f1 { + f2 { + x: a + } + f2 { + x: b + } + } + f1 { + f2 { + y + } + } + } + "); + } + + [Fact] + public void ReportsDeepConflictToNearestCommonAncestorInFragments() + { + ExpectErrors(@" + { + f1 { + ...F + } + f1 { + ...F + } + } + fragment F on Query { + f2 { + f3 { + x: a + } + f3 { + x: b + } + }, + f2 { + f3 { + y + } + } + } + "); + } + + [Fact] + public void ReportsDeepConflictInNestedFragments() + { + ExpectErrors(@" + { + f1 { + ...F + } + f1 { + ...I + } + } + + fragment F on Query { + x: a + ...G + } + + fragment G on Query { + y: c + } + + fragment I on Query { + y: d + ...J + } + + fragment J on Query { + x: b + } + "); + } + + + [Fact] + public void ConflictingReturnTypesWhichPotentiallyOverlap() + { + ExpectErrors(TestSchema, @" + { + someBox { + ...on IntBox { + scalar + } + ...on NonNullStringBox1 { + scalar + } + } + } + "); + } + + [Fact] + public void CompatibleReturnShapesOnDifferentReturnTypes() + { + ExpectValid(TestSchema, @" + { + someBox { + ... on SomeBox { + deepBox { + unrelatedField + } + } + ... on StringBox { + deepBox { + unrelatedField + } + } + } + } + "); + } + + [Fact] + public void DisallowsDifferingReturnTypesDespiteNoOverlap() + { + ExpectErrors(TestSchema, @" + { + someBox { + ... on IntBox { + scalar + } + ... on StringBox { + scalar + } + } + } + "); + } + + [Fact] + public void DisallowsDifferingReturnTypeNullabilityDespiteNoOverlap() + { + ExpectErrors(TestSchema, @" + { + someBox { + ... on NonNullStringBox1 { + scalar + } + ... on StringBox { + scalar + } + } + } + "); + } + + [Fact] + public void DisallowsDifferingReturnTypeListDespiteNoOverlap() + { + ExpectErrors(TestSchema, @" + { + someBox { + ... on IntBox { + box: listStringBox { + scalar + } + } + ... on StringBox { + box: stringBox { + scalar + } + } + } + }"); + } + + [Fact] + public void DisallowsDifferingReturnTypeListDespiteNoOverlapReverse() + { + ExpectErrors(TestSchema, @" + { + someBox { + ... on IntBox { + box: stringBox { + scalar + } + } + ... on StringBox { + box: listStringBox { + scalar + } + } + } + }"); + } + + [Fact] + public void DisallowsDifferingSubfields() + { + ExpectErrors(TestSchema, + @"{ + someBox { + ... on IntBox { + box: stringBox { + val: scalar + val: unrelatedField + } + } + ... on StringBox { + box: stringBox { + val: scalar + } + } + } + }"); + } + + // TODO : Fix this issue + [Fact(Skip = "This one needs fixing!")] + public void DisallowsDifferingDeepReturnTypesDespiteNoOverlap() + { + ExpectErrors(TestSchema, @" + { + someBox { + ... on IntBox { + box: stringBox { + scalar + } + } + ... on StringBox { + box: intBox { + scalar + } + } + } + }"); + } + + [Fact] + public void AllowsNonConflictingOverlappingTypes() + { + ExpectValid(TestSchema, @" + { + someBox { + ... on IntBox { + scalar: unrelatedField + } + ... on StringBox { + scalar + } + } + }"); + } + + // TODO : we need to analyze this validation issue further. + [Fact(Skip = "This one needs to be analyzed further.")] + public void SameWrappedScalarReturnTypes() + { + ExpectErrors(TestSchema, @" + { + someBox { + ...on NonNullStringBox1 { + scalar + } + ...on NonNullStringBox2 { + scalar + } + } + }"); + } + + [Fact] + public void AllowsInlineFragmentsWithoutTypeCondition() + { + ExpectValid(TestSchema, @" + { + a + ... { + a + } + }"); + } + + [Fact] + public void ComparesDeepTypesIncludingList() + { + ExpectErrors(TestSchema, @" + { + connection { + ...edgeID + edges { + node { + id: name + } + } + } + } + + fragment edgeID on Connection { + edges { + node { + id + } + } + }"); + } + + [Fact] + public void FindsInvalidCaseEvenWithImmediatelyRecursiveFragment() + { + ExpectErrors(@" + { + dogOrHuman { + ... sameAliasesWithDifferentFieldTargets + } + } + + fragment sameAliasesWithDifferentFieldTargets on Dog { + ...sameAliasesWithDifferentFieldTargets + fido: name + fido: nickname + }"); + } + + private static readonly ISchema TestSchema = + SchemaBuilder.New() + .AddDocumentFromString(@" + 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 + a: String + d: String + y: String + }") + .AddResolver("StringBox", "deepBox", () => "") + .AddResolver("StringBox", "intBox", () => "") + .AddResolver("StringBox", "listStringBox", () => "") + .AddResolver("StringBox", "scalar", () => "") + .AddResolver("StringBox", "stringBox", () => "") + .AddResolver("StringBox", "unrelatedField", () => "") + .AddResolver("IntBox", "deepBox", () => "") + .AddResolver("IntBox", "intBox", () => "") + .AddResolver("IntBox", "listStringBox", () => "") + .AddResolver("IntBox", "scalar", () => "") + .AddResolver("IntBox", "stringBox", () => "") + .AddResolver("IntBox", "unrelatedField", () => "") + .AddResolver("NonNullStringBox1Impl", "deepBox", () => "") + .AddResolver("NonNullStringBox1Impl", "scalar", () => "") + .AddResolver("NonNullStringBox1Impl", "unrelatedField", () => "") + .AddResolver("NonNullStringBox2Impl", "deepBox", () => "") + .AddResolver("NonNullStringBox2Impl", "scalar", () => "") + .AddResolver("NonNullStringBox2Impl", "unrelatedField", () => "") + .AddResolver("Connection", "edges", () => "") + .AddResolver("Edge", "node", () => "") + .AddResolver("Node", "id", () => "") + .AddResolver("Node", "name", () => "") + .AddResolver("Query", "connection", () => "") + .AddResolver("Query", "someBox", () => "") + .AddResolver("Query", "a", () => "") + .AddResolver("Query", "d", () => "") + .AddResolver("Query", "y", () => "") + .AddType(new AnyType()) + .Create(); } } diff --git a/src/HotChocolate/Core/test/Validation.Tests/FragmentSpreadsMustNotFormCyclesRuleTests.cs b/src/HotChocolate/Core/test/Validation.Tests/FragmentSpreadsMustNotFormCyclesRuleTests.cs index c94823f1c52..b09f30a005d 100644 --- a/src/HotChocolate/Core/test/Validation.Tests/FragmentSpreadsMustNotFormCyclesRuleTests.cs +++ b/src/HotChocolate/Core/test/Validation.Tests/FragmentSpreadsMustNotFormCyclesRuleTests.cs @@ -355,5 +355,57 @@ public void NoSpreadingItselfDeeplyAndImmediately() fragment fragC on Dog { ...fragA, ...fragB } "); } + + [Fact] + public void DoesNotInfiniteLoopOnRecursiveFragment() + { + ExpectErrors(@" + { + dogOrHuman { + ... fragA + } + } + + fragment fragA on Human { + name + ... fragA + }"); + } + + [Fact] + public void DoesNotInfiniteLoopOnImmediatelyRecursiveFragment() + { + ExpectErrors(@" + { + dogOrHuman { + ... fragA + } + } + + fragment fragA on Human { + name + relatives { + name + ... fragA + } + }"); + } + + [Fact] + public void DoesNotInfiniteLoopOnTransitivelyRecursiveFragment() + { + ExpectErrors(@" + { + dogOrHuman { + ... fragA + ... fragB + ... fragC + } + } + + fragment fragA on Human { name, ...fragB } + fragment fragB on Human { name, ...fragC } + fragment fragC on Human { name, ...fragA }"); + } } } diff --git a/src/HotChocolate/Core/test/Validation.Tests/Models/Query.cs b/src/HotChocolate/Core/test/Validation.Tests/Models/Query.cs index ace78ce5dcc..1033ea04562 100644 --- a/src/HotChocolate/Core/test/Validation.Tests/Models/Query.cs +++ b/src/HotChocolate/Core/test/Validation.Tests/Models/Query.cs @@ -2,6 +2,24 @@ { public class Query { + public string A { get; set; } + + public string B { get; set; } + + public string C { get; set; } + + public string D { get; set; } + + public string Y { get; set; } + + public Query F1 { get; set; } + + public Query F2 { get; set; } + + public Query F3 { get; set; } + + public Query GetField() => null; + public Dog GetDog() { return null; diff --git a/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/FieldSelectionMergingRuleTests.AliasMaskingDirectFieldAccess.snap b/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/FieldSelectionMergingRuleTests.AliasMaskingDirectFieldAccess.snap new file mode 100644 index 00000000000..7c4ba94f481 --- /dev/null +++ b/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/FieldSelectionMergingRuleTests.AliasMaskingDirectFieldAccess.snap @@ -0,0 +1,29 @@ +[ + { + "Message": "Encountered fields for the same object that cannot be merged.", + "Code": null, + "Path": null, + "Locations": [ + { + "Line": 9, + "Column": 21 + }, + { + "Line": 10, + "Column": 21 + } + ], + "Extensions": { + "declaringTypeA": "Dog", + "declaringTypeB": "Dog", + "fieldA": "nickname", + "fieldB": "name", + "typeA": "String", + "typeB": "String!", + "responseNameA": "name", + "responseNameB": "name", + "specifiedBy": "http://spec.graphql.org/June2018/#sec-Field-Selection-Merging" + }, + "Exception": null + } +] diff --git a/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/FieldSelectionMergingRuleTests.ComparesDeepTypesIncludingList.snap b/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/FieldSelectionMergingRuleTests.ComparesDeepTypesIncludingList.snap new file mode 100644 index 00000000000..b713b2d0e75 --- /dev/null +++ b/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/FieldSelectionMergingRuleTests.ComparesDeepTypesIncludingList.snap @@ -0,0 +1,29 @@ +[ + { + "Message": "Encountered fields for the same object that cannot be merged.", + "Code": null, + "Path": null, + "Locations": [ + { + "Line": 16, + "Column": 25 + }, + { + "Line": 7, + "Column": 29 + } + ], + "Extensions": { + "declaringTypeA": "Node", + "declaringTypeB": "Node", + "fieldA": "id", + "fieldB": "name", + "typeA": "ID", + "typeB": "String", + "responseNameA": "id", + "responseNameB": "id", + "specifiedBy": "http://spec.graphql.org/June2018/#sec-Field-Selection-Merging" + }, + "Exception": null + } +] diff --git a/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/FieldSelectionMergingRuleTests.ConflictingArgNames.snap b/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/FieldSelectionMergingRuleTests.ConflictingArgNames.snap new file mode 100644 index 00000000000..70bf164c694 --- /dev/null +++ b/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/FieldSelectionMergingRuleTests.ConflictingArgNames.snap @@ -0,0 +1,46 @@ +[ + { + "Message": "The field `isAtLocation` does not exist on the type `Dog`.", + "Code": null, + "Path": { + "Parent": null, + "Depth": 0, + "Name": "catOrDog" + }, + "Locations": [ + { + "Line": 9, + "Column": 21 + } + ], + "Extensions": { + "type": "Dog", + "field": "isAtLocation", + "responseName": "isAtLocation", + "specifiedBy": "http://spec.graphql.org/June2018/#sec-Field-Selections-on-Objects-Interfaces-and-Unions-Types" + }, + "Exception": null + }, + { + "Message": "The field `isAtLocation` does not exist on the type `Dog`.", + "Code": null, + "Path": { + "Parent": null, + "Depth": 0, + "Name": "catOrDog" + }, + "Locations": [ + { + "Line": 10, + "Column": 21 + } + ], + "Extensions": { + "type": "Dog", + "field": "isAtLocation", + "responseName": "isAtLocation", + "specifiedBy": "http://spec.graphql.org/June2018/#sec-Field-Selections-on-Objects-Interfaces-and-Unions-Types" + }, + "Exception": null + } +] diff --git a/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/FieldSelectionMergingRuleTests.ConflictingArgValues.snap b/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/FieldSelectionMergingRuleTests.ConflictingArgValues.snap new file mode 100644 index 00000000000..2503a0d7a5c --- /dev/null +++ b/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/FieldSelectionMergingRuleTests.ConflictingArgValues.snap @@ -0,0 +1,29 @@ +[ + { + "Message": "Encountered fields for the same object that cannot be merged.", + "Code": null, + "Path": null, + "Locations": [ + { + "Line": 9, + "Column": 21 + }, + { + "Line": 10, + "Column": 21 + } + ], + "Extensions": { + "declaringTypeA": "Dog", + "declaringTypeB": "Dog", + "fieldA": "doesKnowCommand", + "fieldB": "doesKnowCommand", + "typeA": "Boolean!", + "typeB": "Boolean!", + "responseNameA": "doesKnowCommand", + "responseNameB": "doesKnowCommand", + "specifiedBy": "http://spec.graphql.org/June2018/#sec-Field-Selection-Merging" + }, + "Exception": null + } +] diff --git a/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/FieldSelectionMergingRuleTests.ConflictingReturnTypesWhichPotentiallyOverlap.snap b/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/FieldSelectionMergingRuleTests.ConflictingReturnTypesWhichPotentiallyOverlap.snap new file mode 100644 index 00000000000..02ac4a0c980 --- /dev/null +++ b/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/FieldSelectionMergingRuleTests.ConflictingReturnTypesWhichPotentiallyOverlap.snap @@ -0,0 +1,29 @@ +[ + { + "Message": "Encountered fields for the same object that cannot be merged.", + "Code": null, + "Path": null, + "Locations": [ + { + "Line": 5, + "Column": 29 + }, + { + "Line": 8, + "Column": 29 + } + ], + "Extensions": { + "declaringTypeA": "IntBox", + "declaringTypeB": "NonNullStringBox1", + "fieldA": "scalar", + "fieldB": "scalar", + "typeA": "Int", + "typeB": "String!", + "responseNameA": "scalar", + "responseNameB": "scalar", + "specifiedBy": "http://spec.graphql.org/June2018/#sec-Field-Selection-Merging" + }, + "Exception": null + } +] diff --git a/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/FieldSelectionMergingRuleTests.DeepConflict.snap b/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/FieldSelectionMergingRuleTests.DeepConflict.snap new file mode 100644 index 00000000000..9fc1802a044 --- /dev/null +++ b/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/FieldSelectionMergingRuleTests.DeepConflict.snap @@ -0,0 +1,29 @@ +[ + { + "Message": "Encountered fields for the same object that cannot be merged.", + "Code": null, + "Path": null, + "Locations": [ + { + "Line": 4, + "Column": 25 + }, + { + "Line": 7, + "Column": 25 + } + ], + "Extensions": { + "declaringTypeA": "Query", + "declaringTypeB": "Query", + "fieldA": "a", + "fieldB": "b", + "typeA": "String", + "typeB": "String", + "responseNameA": "x", + "responseNameB": "x", + "specifiedBy": "http://spec.graphql.org/June2018/#sec-Field-Selection-Merging" + }, + "Exception": null + } +] diff --git a/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/FieldSelectionMergingRuleTests.DeepConflictWithMultipleIssues.snap b/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/FieldSelectionMergingRuleTests.DeepConflictWithMultipleIssues.snap new file mode 100644 index 00000000000..d98f7f1a7b4 --- /dev/null +++ b/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/FieldSelectionMergingRuleTests.DeepConflictWithMultipleIssues.snap @@ -0,0 +1,56 @@ +[ + { + "Message": "Encountered fields for the same object that cannot be merged.", + "Code": null, + "Path": null, + "Locations": [ + { + "Line": 4, + "Column": 25 + }, + { + "Line": 8, + "Column": 25 + } + ], + "Extensions": { + "declaringTypeA": "Query", + "declaringTypeB": "Query", + "fieldA": "a", + "fieldB": "b", + "typeA": "String", + "typeB": "String", + "responseNameA": "x", + "responseNameB": "x", + "specifiedBy": "http://spec.graphql.org/June2018/#sec-Field-Selection-Merging" + }, + "Exception": null + }, + { + "Message": "Encountered fields for the same object that cannot be merged.", + "Code": null, + "Path": null, + "Locations": [ + { + "Line": 5, + "Column": 25 + }, + { + "Line": 9, + "Column": 25 + } + ], + "Extensions": { + "declaringTypeA": "Query", + "declaringTypeB": "Query", + "fieldA": "c", + "fieldB": "d", + "typeA": "String", + "typeB": "String", + "responseNameA": "y", + "responseNameB": "y", + "specifiedBy": "http://spec.graphql.org/June2018/#sec-Field-Selection-Merging" + }, + "Exception": null + } +] diff --git a/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/FieldSelectionMergingRuleTests.DifferentArgsSecondAddsAnArgument.snap b/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/FieldSelectionMergingRuleTests.DifferentArgsSecondAddsAnArgument.snap new file mode 100644 index 00000000000..2503a0d7a5c --- /dev/null +++ b/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/FieldSelectionMergingRuleTests.DifferentArgsSecondAddsAnArgument.snap @@ -0,0 +1,29 @@ +[ + { + "Message": "Encountered fields for the same object that cannot be merged.", + "Code": null, + "Path": null, + "Locations": [ + { + "Line": 9, + "Column": 21 + }, + { + "Line": 10, + "Column": 21 + } + ], + "Extensions": { + "declaringTypeA": "Dog", + "declaringTypeB": "Dog", + "fieldA": "doesKnowCommand", + "fieldB": "doesKnowCommand", + "typeA": "Boolean!", + "typeB": "Boolean!", + "responseNameA": "doesKnowCommand", + "responseNameB": "doesKnowCommand", + "specifiedBy": "http://spec.graphql.org/June2018/#sec-Field-Selection-Merging" + }, + "Exception": null + } +] diff --git a/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/FieldSelectionMergingRuleTests.DifferentArgsSecondMissingAnArgument.snap b/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/FieldSelectionMergingRuleTests.DifferentArgsSecondMissingAnArgument.snap new file mode 100644 index 00000000000..2503a0d7a5c --- /dev/null +++ b/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/FieldSelectionMergingRuleTests.DifferentArgsSecondMissingAnArgument.snap @@ -0,0 +1,29 @@ +[ + { + "Message": "Encountered fields for the same object that cannot be merged.", + "Code": null, + "Path": null, + "Locations": [ + { + "Line": 9, + "Column": 21 + }, + { + "Line": 10, + "Column": 21 + } + ], + "Extensions": { + "declaringTypeA": "Dog", + "declaringTypeB": "Dog", + "fieldA": "doesKnowCommand", + "fieldB": "doesKnowCommand", + "typeA": "Boolean!", + "typeB": "Boolean!", + "responseNameA": "doesKnowCommand", + "responseNameB": "doesKnowCommand", + "specifiedBy": "http://spec.graphql.org/June2018/#sec-Field-Selection-Merging" + }, + "Exception": null + } +] diff --git a/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/FieldSelectionMergingRuleTests.DisallowsDifferingReturnTypeListDespiteNoOverlap.snap b/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/FieldSelectionMergingRuleTests.DisallowsDifferingReturnTypeListDespiteNoOverlap.snap new file mode 100644 index 00000000000..1d1406a240a --- /dev/null +++ b/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/FieldSelectionMergingRuleTests.DisallowsDifferingReturnTypeListDespiteNoOverlap.snap @@ -0,0 +1,29 @@ +[ + { + "Message": "Encountered fields for the same object that cannot be merged.", + "Code": null, + "Path": null, + "Locations": [ + { + "Line": 5, + "Column": 29 + }, + { + "Line": 10, + "Column": 29 + } + ], + "Extensions": { + "declaringTypeA": "IntBox", + "declaringTypeB": "StringBox", + "fieldA": "listStringBox", + "fieldB": "stringBox", + "typeA": "[StringBox]", + "typeB": "StringBox", + "responseNameA": "box", + "responseNameB": "box", + "specifiedBy": "http://spec.graphql.org/June2018/#sec-Field-Selection-Merging" + }, + "Exception": null + } +] diff --git a/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/FieldSelectionMergingRuleTests.DisallowsDifferingReturnTypeListDespiteNoOverlapReverse.snap b/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/FieldSelectionMergingRuleTests.DisallowsDifferingReturnTypeListDespiteNoOverlapReverse.snap new file mode 100644 index 00000000000..be9d20d22a4 --- /dev/null +++ b/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/FieldSelectionMergingRuleTests.DisallowsDifferingReturnTypeListDespiteNoOverlapReverse.snap @@ -0,0 +1,29 @@ +[ + { + "Message": "Encountered fields for the same object that cannot be merged.", + "Code": null, + "Path": null, + "Locations": [ + { + "Line": 5, + "Column": 29 + }, + { + "Line": 10, + "Column": 29 + } + ], + "Extensions": { + "declaringTypeA": "IntBox", + "declaringTypeB": "StringBox", + "fieldA": "stringBox", + "fieldB": "listStringBox", + "typeA": "StringBox", + "typeB": "[StringBox]", + "responseNameA": "box", + "responseNameB": "box", + "specifiedBy": "http://spec.graphql.org/June2018/#sec-Field-Selection-Merging" + }, + "Exception": null + } +] diff --git a/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/FieldSelectionMergingRuleTests.DisallowsDifferingReturnTypeNullabilityDespiteNoOverlap.snap b/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/FieldSelectionMergingRuleTests.DisallowsDifferingReturnTypeNullabilityDespiteNoOverlap.snap new file mode 100644 index 00000000000..76a8849ade0 --- /dev/null +++ b/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/FieldSelectionMergingRuleTests.DisallowsDifferingReturnTypeNullabilityDespiteNoOverlap.snap @@ -0,0 +1,29 @@ +[ + { + "Message": "Encountered fields for the same object that cannot be merged.", + "Code": null, + "Path": null, + "Locations": [ + { + "Line": 5, + "Column": 29 + }, + { + "Line": 8, + "Column": 29 + } + ], + "Extensions": { + "declaringTypeA": "NonNullStringBox1", + "declaringTypeB": "StringBox", + "fieldA": "scalar", + "fieldB": "scalar", + "typeA": "String!", + "typeB": "String", + "responseNameA": "scalar", + "responseNameB": "scalar", + "specifiedBy": "http://spec.graphql.org/June2018/#sec-Field-Selection-Merging" + }, + "Exception": null + } +] diff --git a/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/FieldSelectionMergingRuleTests.DisallowsDifferingReturnTypesDespiteNoOverlap.snap b/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/FieldSelectionMergingRuleTests.DisallowsDifferingReturnTypesDespiteNoOverlap.snap new file mode 100644 index 00000000000..2269eabca2c --- /dev/null +++ b/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/FieldSelectionMergingRuleTests.DisallowsDifferingReturnTypesDespiteNoOverlap.snap @@ -0,0 +1,29 @@ +[ + { + "Message": "Encountered fields for the same object that cannot be merged.", + "Code": null, + "Path": null, + "Locations": [ + { + "Line": 5, + "Column": 25 + }, + { + "Line": 8, + "Column": 25 + } + ], + "Extensions": { + "declaringTypeA": "IntBox", + "declaringTypeB": "StringBox", + "fieldA": "scalar", + "fieldB": "scalar", + "typeA": "Int", + "typeB": "String", + "responseNameA": "scalar", + "responseNameB": "scalar", + "specifiedBy": "http://spec.graphql.org/June2018/#sec-Field-Selection-Merging" + }, + "Exception": null + } +] diff --git a/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/FieldSelectionMergingRuleTests.DisallowsDifferingSubfields.snap b/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/FieldSelectionMergingRuleTests.DisallowsDifferingSubfields.snap new file mode 100644 index 00000000000..2bb96b3f2bc --- /dev/null +++ b/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/FieldSelectionMergingRuleTests.DisallowsDifferingSubfields.snap @@ -0,0 +1,29 @@ +[ + { + "Message": "Encountered fields for the same object that cannot be merged.", + "Code": null, + "Path": null, + "Locations": [ + { + "Line": 5, + "Column": 33 + }, + { + "Line": 6, + "Column": 33 + } + ], + "Extensions": { + "declaringTypeA": "StringBox", + "declaringTypeB": "StringBox", + "fieldA": "scalar", + "fieldB": "unrelatedField", + "typeA": "String", + "typeB": "String", + "responseNameA": "val", + "responseNameB": "val", + "specifiedBy": "http://spec.graphql.org/June2018/#sec-Field-Selection-Merging" + }, + "Exception": null + } +] diff --git a/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/FieldSelectionMergingRuleTests.DoesNotInfiniteLoopOnImmediatelyRecursiveFragment.snap b/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/FieldSelectionMergingRuleTests.DoesNotInfiniteLoopOnImmediatelyRecursiveFragment.snap new file mode 100644 index 00000000000..cd01c3bebff --- /dev/null +++ b/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/FieldSelectionMergingRuleTests.DoesNotInfiniteLoopOnImmediatelyRecursiveFragment.snap @@ -0,0 +1,20 @@ +[ + { + "Message": "The field `` does not exist on the type `Query`.", + "Code": null, + "Path": null, + "Locations": [ + { + "Line": 3, + "Column": 21 + } + ], + "Extensions": { + "type": "Query", + "field": "", + "responseName": "", + "specifiedBy": "http://spec.graphql.org/June2018/#sec-Field-Selections-on-Objects-Interfaces-and-Unions-Types" + }, + "Exception": null + } +] diff --git a/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/FieldSelectionMergingRuleTests.DoesNotInfiniteLoopOnRecursiveFragment.snap b/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/FieldSelectionMergingRuleTests.DoesNotInfiniteLoopOnRecursiveFragment.snap new file mode 100644 index 00000000000..cd01c3bebff --- /dev/null +++ b/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/FieldSelectionMergingRuleTests.DoesNotInfiniteLoopOnRecursiveFragment.snap @@ -0,0 +1,20 @@ +[ + { + "Message": "The field `` does not exist on the type `Query`.", + "Code": null, + "Path": null, + "Locations": [ + { + "Line": 3, + "Column": 21 + } + ], + "Extensions": { + "type": "Query", + "field": "", + "responseName": "", + "specifiedBy": "http://spec.graphql.org/June2018/#sec-Field-Selections-on-Objects-Interfaces-and-Unions-Types" + }, + "Exception": null + } +] diff --git a/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/FieldSelectionMergingRuleTests.DoesNotInfiniteLoopOnTransitivelyRecursiveFragment.snap b/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/FieldSelectionMergingRuleTests.DoesNotInfiniteLoopOnTransitivelyRecursiveFragment.snap new file mode 100644 index 00000000000..cd01c3bebff --- /dev/null +++ b/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/FieldSelectionMergingRuleTests.DoesNotInfiniteLoopOnTransitivelyRecursiveFragment.snap @@ -0,0 +1,20 @@ +[ + { + "Message": "The field `` does not exist on the type `Query`.", + "Code": null, + "Path": null, + "Locations": [ + { + "Line": 3, + "Column": 21 + } + ], + "Extensions": { + "type": "Query", + "field": "", + "responseName": "", + "specifiedBy": "http://spec.graphql.org/June2018/#sec-Field-Selections-on-Objects-Interfaces-and-Unions-Types" + }, + "Exception": null + } +] diff --git a/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/FieldSelectionMergingRuleTests.EncountersConflictInFragments.snap b/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/FieldSelectionMergingRuleTests.EncountersConflictInFragments.snap new file mode 100644 index 00000000000..6c249ae4b91 --- /dev/null +++ b/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/FieldSelectionMergingRuleTests.EncountersConflictInFragments.snap @@ -0,0 +1,29 @@ +[ + { + "Message": "Encountered fields for the same object that cannot be merged.", + "Code": null, + "Path": null, + "Locations": [ + { + "Line": 8, + "Column": 21 + }, + { + "Line": 12, + "Column": 21 + } + ], + "Extensions": { + "declaringTypeA": "Query", + "declaringTypeB": "Query", + "fieldA": "a", + "fieldB": "b", + "typeA": "String", + "typeB": "String", + "responseNameA": "x", + "responseNameB": "x", + "specifiedBy": "http://spec.graphql.org/June2018/#sec-Field-Selection-Merging" + }, + "Exception": null + } +] diff --git a/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/FieldSelectionMergingRuleTests.FindsInvalidCaseEvenWithImmediatelyRecursiveFragment.snap b/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/FieldSelectionMergingRuleTests.FindsInvalidCaseEvenWithImmediatelyRecursiveFragment.snap new file mode 100644 index 00000000000..bc37acb9e9d --- /dev/null +++ b/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/FieldSelectionMergingRuleTests.FindsInvalidCaseEvenWithImmediatelyRecursiveFragment.snap @@ -0,0 +1,29 @@ +[ + { + "Message": "Encountered fields for the same object that cannot be merged.", + "Code": null, + "Path": null, + "Locations": [ + { + "Line": 10, + "Column": 21 + }, + { + "Line": 11, + "Column": 21 + } + ], + "Extensions": { + "declaringTypeA": "Dog", + "declaringTypeB": "Dog", + "fieldA": "name", + "fieldB": "nickname", + "typeA": "String!", + "typeB": "String", + "responseNameA": "fido", + "responseNameB": "fido", + "specifiedBy": "http://spec.graphql.org/June2018/#sec-Field-Selection-Merging" + }, + "Exception": null + } +] diff --git a/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/FieldSelectionMergingRuleTests.ReportsDeepConflictInNestedFragments.snap b/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/FieldSelectionMergingRuleTests.ReportsDeepConflictInNestedFragments.snap new file mode 100644 index 00000000000..680e37ea963 --- /dev/null +++ b/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/FieldSelectionMergingRuleTests.ReportsDeepConflictInNestedFragments.snap @@ -0,0 +1,56 @@ +[ + { + "Message": "Encountered fields for the same object that cannot be merged.", + "Code": null, + "Path": null, + "Locations": [ + { + "Line": 12, + "Column": 21 + }, + { + "Line": 26, + "Column": 21 + } + ], + "Extensions": { + "declaringTypeA": "Query", + "declaringTypeB": "Query", + "fieldA": "a", + "fieldB": "b", + "typeA": "String", + "typeB": "String", + "responseNameA": "x", + "responseNameB": "x", + "specifiedBy": "http://spec.graphql.org/June2018/#sec-Field-Selection-Merging" + }, + "Exception": null + }, + { + "Message": "Encountered fields for the same object that cannot be merged.", + "Code": null, + "Path": null, + "Locations": [ + { + "Line": 17, + "Column": 21 + }, + { + "Line": 21, + "Column": 21 + } + ], + "Extensions": { + "declaringTypeA": "Query", + "declaringTypeB": "Query", + "fieldA": "c", + "fieldB": "d", + "typeA": "String", + "typeB": "String", + "responseNameA": "y", + "responseNameB": "y", + "specifiedBy": "http://spec.graphql.org/June2018/#sec-Field-Selection-Merging" + }, + "Exception": null + } +] diff --git a/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/FieldSelectionMergingRuleTests.ReportsDeepConflictToNearestCommonAncestor.snap b/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/FieldSelectionMergingRuleTests.ReportsDeepConflictToNearestCommonAncestor.snap new file mode 100644 index 00000000000..94070231bf1 --- /dev/null +++ b/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/FieldSelectionMergingRuleTests.ReportsDeepConflictToNearestCommonAncestor.snap @@ -0,0 +1,29 @@ +[ + { + "Message": "Encountered fields for the same object that cannot be merged.", + "Code": null, + "Path": null, + "Locations": [ + { + "Line": 5, + "Column": 29 + }, + { + "Line": 8, + "Column": 29 + } + ], + "Extensions": { + "declaringTypeA": "Query", + "declaringTypeB": "Query", + "fieldA": "a", + "fieldB": "b", + "typeA": "String", + "typeB": "String", + "responseNameA": "x", + "responseNameB": "x", + "specifiedBy": "http://spec.graphql.org/June2018/#sec-Field-Selection-Merging" + }, + "Exception": null + } +] diff --git a/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/FieldSelectionMergingRuleTests.ReportsDeepConflictToNearestCommonAncestorInFragments.snap b/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/FieldSelectionMergingRuleTests.ReportsDeepConflictToNearestCommonAncestorInFragments.snap new file mode 100644 index 00000000000..95bbdd5867d --- /dev/null +++ b/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/FieldSelectionMergingRuleTests.ReportsDeepConflictToNearestCommonAncestorInFragments.snap @@ -0,0 +1,56 @@ +[ + { + "Message": "Encountered fields for the same object that cannot be merged.", + "Code": null, + "Path": null, + "Locations": [ + { + "Line": 13, + "Column": 29 + }, + { + "Line": 16, + "Column": 29 + } + ], + "Extensions": { + "declaringTypeA": "Query", + "declaringTypeB": "Query", + "fieldA": "a", + "fieldB": "b", + "typeA": "String", + "typeB": "String", + "responseNameA": "x", + "responseNameB": "x", + "specifiedBy": "http://spec.graphql.org/June2018/#sec-Field-Selection-Merging" + }, + "Exception": null + }, + { + "Message": "Encountered fields for the same object that cannot be merged.", + "Code": null, + "Path": null, + "Locations": [ + { + "Line": 16, + "Column": 29 + }, + { + "Line": 13, + "Column": 29 + } + ], + "Extensions": { + "declaringTypeA": "Query", + "declaringTypeB": "Query", + "fieldA": "b", + "fieldB": "a", + "typeA": "String", + "typeB": "String", + "responseNameA": "x", + "responseNameB": "x", + "specifiedBy": "http://spec.graphql.org/June2018/#sec-Field-Selection-Merging" + }, + "Exception": null + } +] diff --git a/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/FieldSelectionMergingRuleTests.ReportsEachConflictOnce.snap b/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/FieldSelectionMergingRuleTests.ReportsEachConflictOnce.snap new file mode 100644 index 00000000000..5e0f3917025 --- /dev/null +++ b/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/FieldSelectionMergingRuleTests.ReportsEachConflictOnce.snap @@ -0,0 +1,110 @@ +[ + { + "Message": "Encountered fields for the same object that cannot be merged.", + "Code": null, + "Path": null, + "Locations": [ + { + "Line": 19, + "Column": 21 + }, + { + "Line": 23, + "Column": 21 + } + ], + "Extensions": { + "declaringTypeA": "Query", + "declaringTypeB": "Query", + "fieldA": "a", + "fieldB": "b", + "typeA": "String", + "typeB": "String", + "responseNameA": "x", + "responseNameB": "x", + "specifiedBy": "http://spec.graphql.org/June2018/#sec-Field-Selection-Merging" + }, + "Exception": null + }, + { + "Message": "Encountered fields for the same object that cannot be merged.", + "Code": null, + "Path": null, + "Locations": [ + { + "Line": 23, + "Column": 21 + }, + { + "Line": 19, + "Column": 21 + } + ], + "Extensions": { + "declaringTypeA": "Query", + "declaringTypeB": "Query", + "fieldA": "b", + "fieldB": "a", + "typeA": "String", + "typeB": "String", + "responseNameA": "x", + "responseNameB": "x", + "specifiedBy": "http://spec.graphql.org/June2018/#sec-Field-Selection-Merging" + }, + "Exception": null + }, + { + "Message": "Encountered fields for the same object that cannot be merged.", + "Code": null, + "Path": null, + "Locations": [ + { + "Line": 19, + "Column": 21 + }, + { + "Line": 14, + "Column": 25 + } + ], + "Extensions": { + "declaringTypeA": "Query", + "declaringTypeB": "Query", + "fieldA": "a", + "fieldB": "c", + "typeA": "String", + "typeB": "String", + "responseNameA": "x", + "responseNameB": "x", + "specifiedBy": "http://spec.graphql.org/June2018/#sec-Field-Selection-Merging" + }, + "Exception": null + }, + { + "Message": "Encountered fields for the same object that cannot be merged.", + "Code": null, + "Path": null, + "Locations": [ + { + "Line": 23, + "Column": 21 + }, + { + "Line": 14, + "Column": 25 + } + ], + "Extensions": { + "declaringTypeA": "Query", + "declaringTypeB": "Query", + "fieldA": "b", + "fieldB": "c", + "typeA": "String", + "typeB": "String", + "responseNameA": "x", + "responseNameB": "x", + "specifiedBy": "http://spec.graphql.org/June2018/#sec-Field-Selection-Merging" + }, + "Exception": null + } +] diff --git a/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/FieldSelectionMergingRuleTests.SameAliasesAllowedOnNonOverlappingFields.snap b/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/FieldSelectionMergingRuleTests.SameAliasesAllowedOnNonOverlappingFields.snap new file mode 100644 index 00000000000..803318341cb --- /dev/null +++ b/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/FieldSelectionMergingRuleTests.SameAliasesAllowedOnNonOverlappingFields.snap @@ -0,0 +1,29 @@ +[ + { + "Message": "Encountered fields for the same object that cannot be merged.", + "Code": null, + "Path": null, + "Locations": [ + { + "Line": 10, + "Column": 25 + }, + { + "Line": 13, + "Column": 25 + } + ], + "Extensions": { + "declaringTypeA": "Dog", + "declaringTypeB": "Cat", + "fieldA": "name", + "fieldB": "nickname", + "typeA": "String!", + "typeB": "String", + "responseNameA": "name", + "responseNameB": "name", + "specifiedBy": "http://spec.graphql.org/June2018/#sec-Field-Selection-Merging" + }, + "Exception": null + } +] diff --git a/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/FieldSelectionMergingRuleTests.SameAliasesWithDifferentFieldTargets.snap b/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/FieldSelectionMergingRuleTests.SameAliasesWithDifferentFieldTargets.snap new file mode 100644 index 00000000000..45f6b4a4ad7 --- /dev/null +++ b/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/FieldSelectionMergingRuleTests.SameAliasesWithDifferentFieldTargets.snap @@ -0,0 +1,29 @@ +[ + { + "Message": "Encountered fields for the same object that cannot be merged.", + "Code": null, + "Path": null, + "Locations": [ + { + "Line": 9, + "Column": 21 + }, + { + "Line": 10, + "Column": 21 + } + ], + "Extensions": { + "declaringTypeA": "Dog", + "declaringTypeB": "Dog", + "fieldA": "name", + "fieldB": "nickname", + "typeA": "String!", + "typeB": "String", + "responseNameA": "fido", + "responseNameB": "fido", + "specifiedBy": "http://spec.graphql.org/June2018/#sec-Field-Selection-Merging" + }, + "Exception": null + } +] diff --git a/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/FieldSelectionMergingRuleTests.VeryDeepConflict.snap b/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/FieldSelectionMergingRuleTests.VeryDeepConflict.snap new file mode 100644 index 00000000000..83df27fbf1f --- /dev/null +++ b/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/FieldSelectionMergingRuleTests.VeryDeepConflict.snap @@ -0,0 +1,29 @@ +[ + { + "Message": "Encountered fields for the same object that cannot be merged.", + "Code": null, + "Path": null, + "Locations": [ + { + "Line": 5, + "Column": 29 + }, + { + "Line": 10, + "Column": 29 + } + ], + "Extensions": { + "declaringTypeA": "Query", + "declaringTypeB": "Query", + "fieldA": "a", + "fieldB": "b", + "typeA": "String", + "typeB": "String", + "responseNameA": "x", + "responseNameB": "x", + "specifiedBy": "http://spec.graphql.org/June2018/#sec-Field-Selection-Merging" + }, + "Exception": null + } +] diff --git a/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/FragmentSpreadsMustNotFormCyclesRuleTests.DoesNotInfiniteLoopOnImmediatelyRecursiveFragment.snap b/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/FragmentSpreadsMustNotFormCyclesRuleTests.DoesNotInfiniteLoopOnImmediatelyRecursiveFragment.snap new file mode 100644 index 00000000000..44bf506e383 --- /dev/null +++ b/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/FragmentSpreadsMustNotFormCyclesRuleTests.DoesNotInfiniteLoopOnImmediatelyRecursiveFragment.snap @@ -0,0 +1,26 @@ +[ + { + "Message": "The graph of fragment spreads must not form any cycles including spreading itself. Otherwise an operation could infinitely spread or infinitely execute on cycles in the underlying data.", + "Code": null, + "Path": { + "Parent": { + "Parent": null, + "Depth": 0, + "Name": "dogOrHuman" + }, + "Depth": 1, + "Name": "relatives" + }, + "Locations": [ + { + "Line": 12, + "Column": 25 + } + ], + "Extensions": { + "fragment": "fragA", + "specifiedBy": "http://spec.graphql.org/June2018/#sec-Fragment-spreads-must-not-form-cycles" + }, + "Exception": null + } +] diff --git a/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/FragmentSpreadsMustNotFormCyclesRuleTests.DoesNotInfiniteLoopOnRecursiveFragment.snap b/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/FragmentSpreadsMustNotFormCyclesRuleTests.DoesNotInfiniteLoopOnRecursiveFragment.snap new file mode 100644 index 00000000000..3a5a77ba1de --- /dev/null +++ b/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/FragmentSpreadsMustNotFormCyclesRuleTests.DoesNotInfiniteLoopOnRecursiveFragment.snap @@ -0,0 +1,22 @@ +[ + { + "Message": "The graph of fragment spreads must not form any cycles including spreading itself. Otherwise an operation could infinitely spread or infinitely execute on cycles in the underlying data.", + "Code": null, + "Path": { + "Parent": null, + "Depth": 0, + "Name": "dogOrHuman" + }, + "Locations": [ + { + "Line": 10, + "Column": 21 + } + ], + "Extensions": { + "fragment": "fragA", + "specifiedBy": "http://spec.graphql.org/June2018/#sec-Fragment-spreads-must-not-form-cycles" + }, + "Exception": null + } +] diff --git a/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/FragmentSpreadsMustNotFormCyclesRuleTests.DoesNotInfiniteLoopOnTransitivelyRecursiveFragment.snap b/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/FragmentSpreadsMustNotFormCyclesRuleTests.DoesNotInfiniteLoopOnTransitivelyRecursiveFragment.snap new file mode 100644 index 00000000000..9077997151e --- /dev/null +++ b/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/FragmentSpreadsMustNotFormCyclesRuleTests.DoesNotInfiniteLoopOnTransitivelyRecursiveFragment.snap @@ -0,0 +1,62 @@ +[ + { + "Message": "The graph of fragment spreads must not form any cycles including spreading itself. Otherwise an operation could infinitely spread or infinitely execute on cycles in the underlying data.", + "Code": null, + "Path": { + "Parent": null, + "Depth": 0, + "Name": "dogOrHuman" + }, + "Locations": [ + { + "Line": 12, + "Column": 49 + } + ], + "Extensions": { + "fragment": "fragA", + "specifiedBy": "http://spec.graphql.org/June2018/#sec-Fragment-spreads-must-not-form-cycles" + }, + "Exception": null + }, + { + "Message": "The graph of fragment spreads must not form any cycles including spreading itself. Otherwise an operation could infinitely spread or infinitely execute on cycles in the underlying data.", + "Code": null, + "Path": { + "Parent": null, + "Depth": 0, + "Name": "dogOrHuman" + }, + "Locations": [ + { + "Line": 10, + "Column": 49 + } + ], + "Extensions": { + "fragment": "fragB", + "specifiedBy": "http://spec.graphql.org/June2018/#sec-Fragment-spreads-must-not-form-cycles" + }, + "Exception": null + }, + { + "Message": "The graph of fragment spreads must not form any cycles including spreading itself. Otherwise an operation could infinitely spread or infinitely execute on cycles in the underlying data.", + "Code": null, + "Path": { + "Parent": null, + "Depth": 0, + "Name": "dogOrHuman" + }, + "Locations": [ + { + "Line": 11, + "Column": 49 + } + ], + "Extensions": { + "fragment": "fragC", + "specifiedBy": "http://spec.graphql.org/June2018/#sec-Fragment-spreads-must-not-form-cycles" + }, + "Exception": null + } +]