From 8868901072b4f2cb78373799ff9ec6f06f80403c Mon Sep 17 00:00:00 2001 From: Tobias Tengler <45513122+tobias-tengler@users.noreply.github.com> Date: Sun, 17 Nov 2024 22:37:59 +0100 Subject: [PATCH] Support nodes field for automatic mocking (#7728) --- .../test/Core.Tests/AutomaticMockingTests.cs | 531 +++++++++++++++++- ...solve_Sequence_Skip_On_EntryField_False.md | 2 +- ...Fragment_EntryField_Selected_Separately.md | 2 +- ...uence_Skip_On_EntryField_Fragment_False.md | 2 +- ...eld_Fragment_Other_Field_Selected_False.md | 2 +- ...n_EntryField_Other_Field_Selected_False.md | 2 +- ...esolve_Sequence_Skip_On_RootField_False.md | 2 +- ...quence_Skip_On_RootField_Fragment_False.md | 2 +- ...Fragment_Other_RootField_Selected_False.md | 2 +- ...ootField_Other_RootField_Selected_False.md | 2 +- ...Resolve_Sequence_Skip_On_SubField_False.md | 2 +- ...equence_Skip_On_SubField_Fragment_False.md | 2 +- ...eld_Fragment_Other_Field_Selected_False.md | 2 +- ...d_Fragment_SubField_Selected_Separately.md | 2 +- ..._On_SubField_Other_Field_Selected_False.md | 2 +- ...p_On_SubField_Other_Field_Selected_True.md | 2 +- ..._Nullable_One_Service_Errors_EntryField.md | 2 +- ...nt_Nullable_One_Service_Errors_SubField.md | 2 +- ...ice_Returns_TopLevel_Error_Without_Data.md | 2 +- ...fline_SubField_Nullable_Parent_Nullable.md | 2 +- ...fline_SubField_Nullable_Parent_Nullable.md | 2 +- .../AutomaticMocking/MockFieldMiddleware.cs | 90 +-- 22 files changed, 594 insertions(+), 67 deletions(-) diff --git a/src/HotChocolate/Fusion/test/Core.Tests/AutomaticMockingTests.cs b/src/HotChocolate/Fusion/test/Core.Tests/AutomaticMockingTests.cs index c5ccee5f28d..560e79b4a04 100644 --- a/src/HotChocolate/Fusion/test/Core.Tests/AutomaticMockingTests.cs +++ b/src/HotChocolate/Fusion/test/Core.Tests/AutomaticMockingTests.cs @@ -195,6 +195,77 @@ type Object { """); } + [Fact] + public async Task Object_List_Twice() + { + // arrange + var schema = + """ + type Query { + objsA: [Object!]! + objsB: [Object!]! + } + + type Object { + id: ID! + str: String! + } + """; + var request = + """ + query { + objsA { + id + str + } + objsB { + id + str + } + } + """; + + // act + var result = await ExecuteRequestAgainstSchemaAsync(request, schema); + + // assert + result.MatchInlineSnapshot( + """ + { + "data": { + "objsA": [ + { + "id": "1", + "str": "string" + }, + { + "id": "2", + "str": "string" + }, + { + "id": "3", + "str": "string" + } + ], + "objsB": [ + { + "id": "4", + "str": "string" + }, + { + "id": "5", + "str": "string" + }, + { + "id": "6", + "str": "string" + } + ] + } + } + """); + } + [Fact] public async Task Object_List_NullAtIndex() { @@ -232,7 +303,7 @@ type Object { }, null, { - "id": "2" + "id": "3" } ] } @@ -292,7 +363,7 @@ type Object { }, null, { - "id": "2" + "id": "3" } ] } @@ -813,7 +884,7 @@ ... on Object { null, { "__typename": "Object", - "id": "2", + "id": "3", "str": "string", "num": 123 } @@ -891,7 +962,7 @@ ... on Object { null, { "__typename": "Object", - "id": "2", + "id": "3", "str": "string", "num": 123 } @@ -1416,7 +1487,7 @@ ... on Object { null, { "__typename": "Object", - "id": "2", + "id": "3", "str": "string" } ] @@ -1487,7 +1558,7 @@ ... on Object { null, { "__typename": "Object", - "id": "2", + "id": "3", "str": "string" } ] @@ -2562,6 +2633,454 @@ 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": "5", + "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": "5", + "name": "string" + }, + { + "id": "6", + "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": "5", + "name": "string" + }, + null + ] + } + } + """); + } + + [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": "5", + "name": "string" + }, + null + ] + } + } + """); + } + + #endregion + private static async Task ExecuteRequestAgainstSchemaAsync( string request, string schemaText) diff --git a/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SkipTests.Resolve_Sequence_Skip_On_EntryField_False.md b/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SkipTests.Resolve_Sequence_Skip_On_EntryField_False.md index a870f2c0e6e..033fc66519f 100644 --- a/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SkipTests.Resolve_Sequence_Skip_On_EntryField_False.md +++ b/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SkipTests.Resolve_Sequence_Skip_On_EntryField_False.md @@ -8,7 +8,7 @@ "product": { "id": "1", "brand": { - "id": "1", + "id": "2", "name": "string" } } diff --git a/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SkipTests.Resolve_Sequence_Skip_On_EntryField_Fragment_EntryField_Selected_Separately.md b/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SkipTests.Resolve_Sequence_Skip_On_EntryField_Fragment_EntryField_Selected_Separately.md index dad3e26b060..2483d68825e 100644 --- a/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SkipTests.Resolve_Sequence_Skip_On_EntryField_Fragment_EntryField_Selected_Separately.md +++ b/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SkipTests.Resolve_Sequence_Skip_On_EntryField_Fragment_EntryField_Selected_Separately.md @@ -8,7 +8,7 @@ "product": { "id": "1", "brand": { - "id": "1", + "id": "2", "name": "string" } } diff --git a/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SkipTests.Resolve_Sequence_Skip_On_EntryField_Fragment_False.md b/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SkipTests.Resolve_Sequence_Skip_On_EntryField_Fragment_False.md index 2b7ba7d9219..dbd64781d7e 100644 --- a/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SkipTests.Resolve_Sequence_Skip_On_EntryField_Fragment_False.md +++ b/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SkipTests.Resolve_Sequence_Skip_On_EntryField_Fragment_False.md @@ -8,7 +8,7 @@ "product": { "id": "1", "brand": { - "id": "1", + "id": "2", "name": "string" } } diff --git a/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SkipTests.Resolve_Sequence_Skip_On_EntryField_Fragment_Other_Field_Selected_False.md b/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SkipTests.Resolve_Sequence_Skip_On_EntryField_Fragment_Other_Field_Selected_False.md index e7a8c628f70..130c690c005 100644 --- a/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SkipTests.Resolve_Sequence_Skip_On_EntryField_Fragment_Other_Field_Selected_False.md +++ b/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SkipTests.Resolve_Sequence_Skip_On_EntryField_Fragment_Other_Field_Selected_False.md @@ -8,7 +8,7 @@ "product": { "id": "1", "brand": { - "id": "1", + "id": "2", "name": "string" }, "other": "string" diff --git a/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SkipTests.Resolve_Sequence_Skip_On_EntryField_Other_Field_Selected_False.md b/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SkipTests.Resolve_Sequence_Skip_On_EntryField_Other_Field_Selected_False.md index 59342fe350c..fbca62a4ad9 100644 --- a/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SkipTests.Resolve_Sequence_Skip_On_EntryField_Other_Field_Selected_False.md +++ b/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SkipTests.Resolve_Sequence_Skip_On_EntryField_Other_Field_Selected_False.md @@ -8,7 +8,7 @@ "product": { "id": "1", "brand": { - "id": "1", + "id": "2", "name": "string" }, "other": "string" diff --git a/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SkipTests.Resolve_Sequence_Skip_On_RootField_False.md b/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SkipTests.Resolve_Sequence_Skip_On_RootField_False.md index 59c6fc0d72a..49766b70a88 100644 --- a/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SkipTests.Resolve_Sequence_Skip_On_RootField_False.md +++ b/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SkipTests.Resolve_Sequence_Skip_On_RootField_False.md @@ -8,7 +8,7 @@ "product": { "id": "1", "brand": { - "id": "1", + "id": "2", "name": "string" } } diff --git a/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SkipTests.Resolve_Sequence_Skip_On_RootField_Fragment_False.md b/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SkipTests.Resolve_Sequence_Skip_On_RootField_Fragment_False.md index aa0c42c676c..05d3668e254 100644 --- a/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SkipTests.Resolve_Sequence_Skip_On_RootField_Fragment_False.md +++ b/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SkipTests.Resolve_Sequence_Skip_On_RootField_Fragment_False.md @@ -8,7 +8,7 @@ "product": { "id": "1", "brand": { - "id": "1", + "id": "2", "name": "string" } } diff --git a/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SkipTests.Resolve_Sequence_Skip_On_RootField_Fragment_Other_RootField_Selected_False.md b/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SkipTests.Resolve_Sequence_Skip_On_RootField_Fragment_Other_RootField_Selected_False.md index b7d5aba46f0..3d301d5aed3 100644 --- a/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SkipTests.Resolve_Sequence_Skip_On_RootField_Fragment_Other_RootField_Selected_False.md +++ b/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SkipTests.Resolve_Sequence_Skip_On_RootField_Fragment_Other_RootField_Selected_False.md @@ -8,7 +8,7 @@ "product": { "id": "1", "brand": { - "id": "1", + "id": "2", "name": "string" } }, diff --git a/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SkipTests.Resolve_Sequence_Skip_On_RootField_Other_RootField_Selected_False.md b/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SkipTests.Resolve_Sequence_Skip_On_RootField_Other_RootField_Selected_False.md index 431149bad3a..0bd42e7c758 100644 --- a/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SkipTests.Resolve_Sequence_Skip_On_RootField_Other_RootField_Selected_False.md +++ b/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SkipTests.Resolve_Sequence_Skip_On_RootField_Other_RootField_Selected_False.md @@ -8,7 +8,7 @@ "product": { "id": "1", "brand": { - "id": "1", + "id": "2", "name": "string" } }, diff --git a/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SkipTests.Resolve_Sequence_Skip_On_SubField_False.md b/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SkipTests.Resolve_Sequence_Skip_On_SubField_False.md index 807675840eb..57c8142f6c6 100644 --- a/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SkipTests.Resolve_Sequence_Skip_On_SubField_False.md +++ b/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SkipTests.Resolve_Sequence_Skip_On_SubField_False.md @@ -8,7 +8,7 @@ "product": { "id": "1", "brand": { - "id": "1", + "id": "2", "name": "string" } } diff --git a/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SkipTests.Resolve_Sequence_Skip_On_SubField_Fragment_False.md b/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SkipTests.Resolve_Sequence_Skip_On_SubField_Fragment_False.md index 246ed7a3ac1..e7cc0553f77 100644 --- a/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SkipTests.Resolve_Sequence_Skip_On_SubField_Fragment_False.md +++ b/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SkipTests.Resolve_Sequence_Skip_On_SubField_Fragment_False.md @@ -8,7 +8,7 @@ "product": { "id": "1", "brand": { - "id": "1", + "id": "2", "name": "string" } } diff --git a/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SkipTests.Resolve_Sequence_Skip_On_SubField_Fragment_Other_Field_Selected_False.md b/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SkipTests.Resolve_Sequence_Skip_On_SubField_Fragment_Other_Field_Selected_False.md index e0269b4c432..04923bb4e45 100644 --- a/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SkipTests.Resolve_Sequence_Skip_On_SubField_Fragment_Other_Field_Selected_False.md +++ b/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SkipTests.Resolve_Sequence_Skip_On_SubField_Fragment_Other_Field_Selected_False.md @@ -8,7 +8,7 @@ "product": { "id": "1", "brand": { - "id": "1", + "id": "2", "name": "string", "other": "string" } diff --git a/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SkipTests.Resolve_Sequence_Skip_On_SubField_Fragment_SubField_Selected_Separately.md b/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SkipTests.Resolve_Sequence_Skip_On_SubField_Fragment_SubField_Selected_Separately.md index 57bdc2d984f..39be711a557 100644 --- a/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SkipTests.Resolve_Sequence_Skip_On_SubField_Fragment_SubField_Selected_Separately.md +++ b/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SkipTests.Resolve_Sequence_Skip_On_SubField_Fragment_SubField_Selected_Separately.md @@ -8,7 +8,7 @@ "product": { "id": "1", "brand": { - "id": "1", + "id": "2", "name": "string" } } diff --git a/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SkipTests.Resolve_Sequence_Skip_On_SubField_Other_Field_Selected_False.md b/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SkipTests.Resolve_Sequence_Skip_On_SubField_Other_Field_Selected_False.md index d63f4d334c4..487880d07d4 100644 --- a/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SkipTests.Resolve_Sequence_Skip_On_SubField_Other_Field_Selected_False.md +++ b/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SkipTests.Resolve_Sequence_Skip_On_SubField_Other_Field_Selected_False.md @@ -8,7 +8,7 @@ "product": { "id": "1", "brand": { - "id": "1", + "id": "2", "name": "string", "other": "string" } diff --git a/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SkipTests.Resolve_Sequence_Skip_On_SubField_Other_Field_Selected_True.md b/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SkipTests.Resolve_Sequence_Skip_On_SubField_Other_Field_Selected_True.md index eb405b3b327..01cc70364d6 100644 --- a/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SkipTests.Resolve_Sequence_Skip_On_SubField_Other_Field_Selected_True.md +++ b/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SkipTests.Resolve_Sequence_Skip_On_SubField_Other_Field_Selected_True.md @@ -8,7 +8,7 @@ "product": { "id": "1", "brand": { - "id": "1", + "id": "2", "other": "string" } } diff --git a/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SubgraphErrorTests.Resolve_Sequence_SubField_Nullable_Parent_Nullable_One_Service_Errors_EntryField.md b/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SubgraphErrorTests.Resolve_Sequence_SubField_Nullable_Parent_Nullable_One_Service_Errors_EntryField.md index 1c4df83d44f..5f25db81835 100644 --- a/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SubgraphErrorTests.Resolve_Sequence_SubField_Nullable_Parent_Nullable_One_Service_Errors_EntryField.md +++ b/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SubgraphErrorTests.Resolve_Sequence_SubField_Nullable_Parent_Nullable_One_Service_Errors_EntryField.md @@ -23,7 +23,7 @@ "product": { "id": "1", "brand": { - "id": "1", + "id": "2", "name": null } } diff --git a/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SubgraphErrorTests.Resolve_Sequence_SubField_Nullable_Parent_Nullable_One_Service_Errors_SubField.md b/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SubgraphErrorTests.Resolve_Sequence_SubField_Nullable_Parent_Nullable_One_Service_Errors_SubField.md index 06f0976a7e9..c51b0a924c9 100644 --- a/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SubgraphErrorTests.Resolve_Sequence_SubField_Nullable_Parent_Nullable_One_Service_Errors_SubField.md +++ b/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SubgraphErrorTests.Resolve_Sequence_SubField_Nullable_Parent_Nullable_One_Service_Errors_SubField.md @@ -24,7 +24,7 @@ "product": { "id": "1", "brand": { - "id": "1", + "id": "2", "name": null } } diff --git a/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SubgraphErrorTests.Resolve_Sequence_SubField_Nullable_Parent_Nullable_Second_Service_Returns_TopLevel_Error_Without_Data.md b/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SubgraphErrorTests.Resolve_Sequence_SubField_Nullable_Parent_Nullable_Second_Service_Returns_TopLevel_Error_Without_Data.md index db0f7f7f4d2..9cee8f08ad0 100644 --- a/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SubgraphErrorTests.Resolve_Sequence_SubField_Nullable_Parent_Nullable_Second_Service_Returns_TopLevel_Error_Without_Data.md +++ b/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SubgraphErrorTests.Resolve_Sequence_SubField_Nullable_Parent_Nullable_Second_Service_Returns_TopLevel_Error_Without_Data.md @@ -13,7 +13,7 @@ "product": { "id": "1", "brand": { - "id": "1", + "id": "2", "name": null } } diff --git a/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/TransportErrorTests.Resolve_Sequence_Node_Second_Service_Offline_SubField_Nullable_Parent_Nullable.md b/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/TransportErrorTests.Resolve_Sequence_Node_Second_Service_Offline_SubField_Nullable_Parent_Nullable.md index cb16b1cd5fe..4231d73351f 100644 --- a/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/TransportErrorTests.Resolve_Sequence_Node_Second_Service_Offline_SubField_Nullable_Parent_Nullable.md +++ b/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/TransportErrorTests.Resolve_Sequence_Node_Second_Service_Offline_SubField_Nullable_Parent_Nullable.md @@ -24,7 +24,7 @@ "product": { "id": "1", "brand": { - "id": "1", + "id": "2", "name": null } } diff --git a/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/TransportErrorTests.Resolve_Sequence_Second_Service_Offline_SubField_Nullable_Parent_Nullable.md b/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/TransportErrorTests.Resolve_Sequence_Second_Service_Offline_SubField_Nullable_Parent_Nullable.md index 5029f85a0de..2e35f9f740f 100644 --- a/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/TransportErrorTests.Resolve_Sequence_Second_Service_Offline_SubField_Nullable_Parent_Nullable.md +++ b/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/TransportErrorTests.Resolve_Sequence_Second_Service_Offline_SubField_Nullable_Parent_Nullable.md @@ -24,7 +24,7 @@ "product": { "id": "1", "brand": { - "id": "1", + "id": "2", "name": null } } diff --git a/src/HotChocolate/Fusion/test/Shared/AutomaticMocking/MockFieldMiddleware.cs b/src/HotChocolate/Fusion/test/Shared/AutomaticMocking/MockFieldMiddleware.cs index 3caae3590a5..54ac054ac8f 100644 --- a/src/HotChocolate/Fusion/test/Shared/AutomaticMocking/MockFieldMiddleware.cs +++ b/src/HotChocolate/Fusion/test/Shared/AutomaticMocking/MockFieldMiddleware.cs @@ -1,3 +1,4 @@ +using HotChocolate.Language; using HotChocolate.Resolvers; using HotChocolate.Types; using HotChocolate.Utilities; @@ -9,10 +10,15 @@ internal sealed class MockFieldMiddleware { private const int DefaultListSize = 3; - private int _idCounter; - public ValueTask InvokeAsync(IMiddlewareContext context) { + var mockingContext = context.GetGlobalStateOrDefault(nameof(AutomaticMockingContext)); + if (mockingContext is null) + { + mockingContext = new AutomaticMockingContext(); + context.SetGlobalState(nameof(AutomaticMockingContext), mockingContext); + } + var field = context.Selection.Field; var fieldName = field.Name; var fieldType = field.Type; @@ -69,13 +75,16 @@ public ValueTask InvokeAsync(IMiddlewareContext context) } } - if (fieldName.EndsWith("ById")) + if (fieldName.EndsWith("ById") || fieldName is "node" or "nodes") { if (context.Selection.Arguments.ContainsName("id")) { var id = context.ArgumentValue("id"); - if (namedFieldType.IsObjectType()) + if (namedFieldType.IsCompositeType()) { + var possibleTypes = context.Schema.GetPossibleTypes(namedFieldType); + + context.ValueType = possibleTypes.First(); context.Result = CreateObject(id); return ValueTask.CompletedTask; } @@ -91,10 +100,16 @@ public ValueTask InvokeAsync(IMiddlewareContext context) nullableType = fieldType.InnerType(); } - if (nullableType.IsListType() && namedFieldType.IsObjectType()) + if (nullableType.IsListType()) { - context.Result = CreateListOfObjects(ids, nullIndex); - return ValueTask.CompletedTask; + if (namedFieldType.IsCompositeType()) + { + var possibleTypes = context.Schema.GetPossibleTypes(namedFieldType); + + context.ValueType = possibleTypes.First(); + context.Result = CreateListOfObjects(ids, nullIndex); + return ValueTask.CompletedTask; + } } } } @@ -109,56 +124,53 @@ public ValueTask InvokeAsync(IMiddlewareContext context) } } - if (fieldType.IsObjectType()) - { - context.Result = CreateObject(); - } - else if (fieldType.IsInterfaceType() || fieldType.IsUnionType()) + var hasIdFieldSelection = context.Select().IsSelected("id"); + + if (fieldType.IsCompositeType()) { + int? id = hasIdFieldSelection ? ++mockingContext.IdCounter : null; var possibleTypes = context.Schema.GetPossibleTypes(namedFieldType); context.ValueType = possibleTypes.First(); - context.Result = CreateObject(); + context.Result = CreateObject(id); } else if (fieldType.IsListType()) { - if (namedFieldType.IsObjectType()) - { - context.Result = CreateListOfObjects(null, nullIndex); - } - else if (namedFieldType.IsInterfaceType() || namedFieldType.IsUnionType()) + if (namedFieldType.IsCompositeType()) { + var ids = Enumerable.Range(0, DefaultListSize) + .Select(_ => (object?)(hasIdFieldSelection ? ++mockingContext.IdCounter : null)).ToArray(); var possibleTypes = context.Schema.GetPossibleTypes(namedFieldType); context.ValueType = possibleTypes.First(); - context.Result = CreateListOfObjects(null, nullIndex); + context.Result = CreateListOfObjects(ids, nullIndex); } - else if(namedFieldType is EnumType enumType) + else if (namedFieldType is EnumType enumType) { context.Result = CreateListOfEnums(enumType, nullIndex); } else { - context.Result = CreateListOfScalars(namedFieldType, nullIndex); + context.Result = CreateListOfScalars(namedFieldType, nullIndex, mockingContext); } } - else if(namedFieldType is EnumType enumType) + else if (namedFieldType is EnumType enumType) { context.Result = CreateEnumValue(enumType); } else { - context.Result = CreateScalarValue(namedFieldType); + context.Result = CreateScalarValue(namedFieldType, mockingContext); } return ValueTask.CompletedTask; } - private object? CreateScalarValue(INamedType scalarType) + private object? CreateScalarValue(INamedType scalarType, AutomaticMockingContext mockingContext) { return scalarType switch { - IdType => ++_idCounter, + IdType => ++mockingContext.IdCounter, StringType => "string", IntType => 123, FloatType => 123.456, @@ -172,17 +184,15 @@ 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) + private object?[] CreateListOfScalars(INamedType scalarType, int? nullIndex, AutomaticMockingContext mockingContext) { return Enumerable.Range(0, DefaultListSize) - .Select(index => nullIndex == index ? null : CreateScalarValue(scalarType)) + .Select(index => nullIndex == index ? null : CreateScalarValue(scalarType, mockingContext)) .ToArray(); } @@ -193,17 +203,10 @@ private object CreateObject(object? id = null, int? index = null) .ToArray(); } - private object?[] CreateListOfObjects(object[]? ids, int? nullIndex) + private object?[] CreateListOfObjects(object?[] ids, int? nullIndex) { - if (ids is not null) - { - return ids - .Select((itemId, index) => nullIndex == index ? null : CreateObject(itemId, index)) - .ToArray(); - } - - return Enumerable.Range(0, DefaultListSize) - .Select(index => nullIndex == index ? null : CreateObject(null, index)) + return ids + .Select((itemId, index) => nullIndex == index ? null : CreateObject(itemId, index)) .ToArray(); } @@ -224,4 +227,9 @@ private static IError CreateError(IResolverContext context, int? index = null) } private record ObjectTypeInst(object? Id = null, int? Index = null); + + private class AutomaticMockingContext + { + public int IdCounter { get; set; } + } }