diff --git a/src/HotChocolate/Fusion/test/Core.Tests/AutomaticMockingTests.cs b/src/HotChocolate/Fusion/test/Core.Tests/AutomaticMockingTests.cs index c5ccee5f28d..e0cbdc07e79 100644 --- a/src/HotChocolate/Fusion/test/Core.Tests/AutomaticMockingTests.cs +++ b/src/HotChocolate/Fusion/test/Core.Tests/AutomaticMockingTests.cs @@ -232,7 +232,7 @@ type Object { }, null, { - "id": "2" + "id": "3" } ] } @@ -292,7 +292,7 @@ type Object { }, null, { - "id": "2" + "id": "3" } ] } @@ -813,7 +813,7 @@ ... on Object { null, { "__typename": "Object", - "id": "2", + "id": "3", "str": "string", "num": 123 } @@ -891,7 +891,7 @@ ... on Object { null, { "__typename": "Object", - "id": "2", + "id": "3", "str": "string", "num": 123 } @@ -1416,7 +1416,7 @@ ... on Object { null, { "__typename": "Object", - "id": "2", + "id": "3", "str": "string" } ] @@ -1487,7 +1487,7 @@ ... on Object { null, { "__typename": "Object", - "id": "2", + "id": "3", "str": "string" } ] @@ -2562,6 +2562,466 @@ type Product { #endregion + #region node + + [Fact] + public async Task NodeField() + { + // arrange + var schema = + """ + type Query { + node(id: ID!): Node + } + + interface Node { + id: ID! + } + + type Product implements Node { + id: ID! + name: String! + } + """; + var request = + """ + query { + node(id: "5") { + id + ... on Product { + name + } + } + } + """; + + // act + var result = await ExecuteRequestAgainstSchemaAsync(request, schema); + + // assert + result.MatchInlineSnapshot( + """ + { + "data": { + "node": { + "id": "1", + "name": "string" + } + } + } + """); + } + + [Fact] + public async Task NodeField_Null() + { + // arrange + var schema = + """ + type Query { + node(id: ID!): Node @null + } + + interface Node { + id: ID! + } + + type Product implements Node { + id: ID! + name: String! + } + """; + var request = + """ + query { + node(id: "5") { + id + ... on Product { + name + } + } + } + """; + + // act + var result = await ExecuteRequestAgainstSchemaAsync(request, schema); + + // assert + result.MatchInlineSnapshot( + """ + { + "data": { + "node": null + } + } + """); + } + + [Fact] + public async Task NodeField_Error() + { + // arrange + var schema = + """ + type Query { + node(id: ID!): Node @error + } + + interface Node { + id: ID! + } + + type Product implements Node { + id: ID! + name: String! + } + """; + var request = + """ + query { + node(id: "5") { + id + ... on Product { + name + } + } + } + """; + + // act + var result = await ExecuteRequestAgainstSchemaAsync(request, schema); + + // assert + result.MatchInlineSnapshot( + """ + { + "errors": [ + { + "message": "Unexpected Execution Error", + "locations": [ + { + "line": 2, + "column": 3 + } + ], + "path": [ + "node" + ] + } + ], + "data": { + "node": null + } + } + """); + } + + #endregion + + #region nodes + + [Fact] + public async Task NodesField() + { + // arrange + var schema = + """ + type Query { + nodes(ids: [ID!]!): [Node]! + } + + interface Node { + id: ID! + } + + type Product implements Node { + id: ID! + name: String! + } + """; + var request = + """ + query { + nodes(ids: ["5", "6"]) { + id + ... on Product { + name + } + } + } + """; + + // act + var result = await ExecuteRequestAgainstSchemaAsync(request, schema); + + // assert + result.MatchInlineSnapshot( + """ + { + "data": { + "nodes": [ + { + "id": "1", + "name": "string" + }, + { + "id": "2", + "name": "string" + }, + { + "id": "3", + "name": "string" + } + ] + } + } + """); + } + + [Fact] + public async Task NodesField_Null() + { + // arrange + var schema = + """ + type Query { + nodes(ids: [ID!]!): [Node]! @null + } + + interface Node { + id: ID! + } + + type Product implements Node { + id: ID! + name: String! + } + """; + var request = + """ + query { + nodes(ids: ["5", "6"]) { + id + ... on Product { + name + } + } + } + """; + + // act + var result = await ExecuteRequestAgainstSchemaAsync(request, schema); + + // assert + result.MatchInlineSnapshot( + """ + { + "errors": [ + { + "message": "Cannot return null for non-nullable field.", + "locations": [ + { + "line": 2, + "column": 3 + } + ], + "path": [ + "nodes" + ], + "extensions": { + "code": "HC0018" + } + } + ], + "data": null + } + """); + } + + [Fact] + public async Task NodesField_Error() + { + // arrange + var schema = + """ + type Query { + nodes(ids: [ID!]!): [Node]! @error + } + + interface Node { + id: ID! + } + + type Product implements Node { + id: ID! + name: String! + } + """; + var request = + """ + query { + nodes(ids: ["5", "6"]) { + id + ... on Product { + name + } + } + } + """; + + // act + var result = await ExecuteRequestAgainstSchemaAsync(request, schema); + + // assert + result.MatchInlineSnapshot( + """ + { + "errors": [ + { + "message": "Unexpected Execution Error", + "locations": [ + { + "line": 2, + "column": 3 + } + ], + "path": [ + "nodes" + ] + } + ], + "data": null + } + """); + } + + [Fact] + public async Task NodesField_NullAtIndex() + { + // arrange + var schema = + """ + type Query { + nodes(ids: [ID!]!): [Node]! @null(atIndex: 1) + } + + interface Node { + id: ID! + } + + type Product implements Node { + id: ID! + name: String! + } + """; + var request = + """ + query { + nodes(ids: ["5", "6"]) { + id + ... on Product { + name + } + } + } + """; + + // act + var result = await ExecuteRequestAgainstSchemaAsync(request, schema); + + // assert + result.MatchInlineSnapshot( + """ + { + "data": { + "nodes": [ + { + "id": "1", + "name": "string" + }, + null, + { + "id": "3", + "name": "string" + } + ] + } + } + """); + } + + [Fact] + public async Task NodesField_ErrorAtIndex() + { + // arrange + var schema = + """ + type Query { + nodes(ids: [ID!]!): [Node]! @error(atIndex: 1) + } + + interface Node { + id: ID! + } + + type Product implements Node { + id: ID! + name: String! + } + """; + var request = + """ + query { + nodes(ids: ["5", "6"]) { + id + ... on Product { + name + } + } + } + """; + + // act + var result = await ExecuteRequestAgainstSchemaAsync(request, schema); + + // assert + result.MatchInlineSnapshot( + """ + { + "errors": [ + { + "message": "Unexpected Execution Error", + "locations": [ + { + "line": 2, + "column": 3 + } + ], + "path": [ + "nodes", + 1 + ] + } + ], + "data": { + "nodes": [ + { + "id": "1", + "name": "string" + }, + null, + { + "id": "3", + "name": "string" + } + ] + } + } + """); + } + + #endregion + private static async Task ExecuteRequestAgainstSchemaAsync( string request, string schemaText) diff --git a/src/HotChocolate/Fusion/test/Shared/AutomaticMocking/MockFieldMiddleware.cs b/src/HotChocolate/Fusion/test/Shared/AutomaticMocking/MockFieldMiddleware.cs index 3caae3590a5..b88b2ce2da4 100644 --- a/src/HotChocolate/Fusion/test/Shared/AutomaticMocking/MockFieldMiddleware.cs +++ b/src/HotChocolate/Fusion/test/Shared/AutomaticMocking/MockFieldMiddleware.cs @@ -69,7 +69,7 @@ public ValueTask InvokeAsync(IMiddlewareContext context) } } - if (fieldName.EndsWith("ById")) + if (fieldName.EndsWith("ById") || fieldName is "node" or "nodes") { if (context.Selection.Arguments.ContainsName("id")) { @@ -111,14 +111,14 @@ public ValueTask InvokeAsync(IMiddlewareContext context) if (fieldType.IsObjectType()) { - context.Result = CreateObject(); + context.Result = CreateObject(++_idCounter); } else if (fieldType.IsInterfaceType() || fieldType.IsUnionType()) { var possibleTypes = context.Schema.GetPossibleTypes(namedFieldType); context.ValueType = possibleTypes.First(); - context.Result = CreateObject(); + context.Result = CreateObject(++_idCounter); } else if (fieldType.IsListType()) { @@ -172,11 +172,9 @@ public ValueTask InvokeAsync(IMiddlewareContext context) return enumType.Values.FirstOrDefault()?.Value; } - private object CreateObject(object? id = null, int? index = null) + private object CreateObject(object id, int? index = null) { - var finalId = id ?? ++_idCounter; - - return new ObjectTypeInst(finalId, index); + return new ObjectTypeInst(id, index); } private object?[] CreateListOfScalars(INamedType scalarType, int? nullIndex) @@ -203,7 +201,11 @@ private object CreateObject(object? id = null, int? index = null) } return Enumerable.Range(0, DefaultListSize) - .Select(index => nullIndex == index ? null : CreateObject(null, index)) + .Select(index => + { + var id = ++_idCounter; + return nullIndex == index ? null : CreateObject(id, index); + }) .ToArray(); }