From f5a399bb038997e24ffaa3071eb77ebf53806115 Mon Sep 17 00:00:00 2001 From: Pascal Senn Date: Thu, 20 Feb 2025 14:45:32 +0100 Subject: [PATCH] Adds Tests case for Mutation Requirement issue in fusion v1 --- .../Fusion/test/Core.Tests/RequireTests.cs | 141 ++++++++++++++++++ ...RequireTests.Require_On_MutationPayload.md | 41 +++++ .../Fusion/test/Shared/DemoSubgraph.cs | 13 ++ 3 files changed, 195 insertions(+) create mode 100644 src/HotChocolate/Fusion/test/Core.Tests/RequireTests.cs create mode 100644 src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/RequireTests.Require_On_MutationPayload.md diff --git a/src/HotChocolate/Fusion/test/Core.Tests/RequireTests.cs b/src/HotChocolate/Fusion/test/Core.Tests/RequireTests.cs new file mode 100644 index 00000000000..0a9c202b168 --- /dev/null +++ b/src/HotChocolate/Fusion/test/Core.Tests/RequireTests.cs @@ -0,0 +1,141 @@ +using System.Diagnostics.CodeAnalysis; +using HotChocolate.Execution; +using HotChocolate.Execution.Processing; +using HotChocolate.Fusion.Composition; +using HotChocolate.Fusion.Composition.Features; +using HotChocolate.Fusion.Metadata; +using HotChocolate.Fusion.Planning; +using HotChocolate.Fusion.Shared; +using HotChocolate.Language; +using HotChocolate.Skimmed.Serialization; +using Microsoft.Extensions.DependencyInjection; +using Xunit.Abstractions; +using static HotChocolate.Fusion.Shared.DemoProjectSchemaExtensions; +using static HotChocolate.Language.Utf8GraphQLParser; +using HttpClientConfiguration = HotChocolate.Fusion.Composition.HttpClientConfiguration; + +namespace HotChocolate.Fusion; + +public class RequireTests +{ + [Fact] + public async Task Require_On_MutationPayload() + { + // arrange + var subgraphA = DemoSubgraph.CreateSubgraphConfig( + "subgraphA", + new Uri("http://localhost:5001"), + """ + type User { + id: ID! + someField: String! + } + + type Query { + userById(id: ID!): User + } + """ + ); + + var subgraphB = DemoSubgraph.CreateSubgraphConfig( + "subgraphB", + new Uri("http://localhost:5002"), + """ + type User { + id: ID! + nestedField(someField: String! @require(field: "someField")): NestedType! + } + + type NestedType { + otherField: Int! + } + + type Mutation { + createUser: CreateUserPayload + } + + type CreateUserPayload { + user: User! + } + + type Query { + userById(id: ID!): User @lookup @internal + } + """ + ); + + var fusionGraph = await FusionGraphComposer.ComposeAsync([subgraphA, subgraphB]); + + // act + var result = await CreateQueryPlanAsync( + fusionGraph, + """ + mutation { + createUser { + user { + nestedField { + otherField + } + } + } + } + + """ + ); + + // assert + var snapshot = new Snapshot(); + snapshot.Add(result.UserRequest, nameof(result.UserRequest)); + snapshot.Add(result.QueryPlan, nameof(result.QueryPlan)); + await snapshot.MatchMarkdownAsync(); + } + + private static async Task<( + DocumentNode UserRequest, + Execution.Nodes.QueryPlan QueryPlan + )> CreateQueryPlanAsync( + Skimmed.SchemaDefinition fusionGraph, + [StringSyntax("graphql")] string query + ) + { + var document = SchemaFormatter.FormatAsDocument(fusionGraph); + var context = FusionTypeNames.From(document); + var rewriter = new FusionGraphConfigurationToSchemaRewriter(); + var rewritten = rewriter.Rewrite(document, new(context))!; + + var services = new ServiceCollection() + .AddGraphQL() + .AddDocumentFromString(rewritten.ToString()) + .UseField(n => n); + + if (document.Definitions.Any(d => d is ScalarTypeDefinitionNode { Name.Value: "Upload" })) + { + services.AddUploadType(); + } + + var schema = await services.BuildSchemaAsync(); + var serviceConfig = FusionGraphConfiguration.Load(document); + + var request = Parse(query); + + var operationCompiler = new OperationCompiler(new()); + var operationDef = (OperationDefinitionNode)request.Definitions[0]; + var operation = operationCompiler.Compile( + new OperationCompilerRequest( + "abc", + request, + operationDef, + schema.GetOperationType(operationDef.Operation)!, + schema + ) + ); + + var queryPlanner = new QueryPlanner(serviceConfig, schema); + var queryPlan = queryPlanner.Plan(operation); + + return (request, queryPlan); + } + + private static IClientConfiguration[] CreateClients() => + [new HttpClientConfiguration(new Uri("http://nothing"))]; +} diff --git a/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/RequireTests.Require_On_MutationPayload.md b/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/RequireTests.Require_On_MutationPayload.md new file mode 100644 index 00000000000..1019b801978 --- /dev/null +++ b/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/RequireTests.Require_On_MutationPayload.md @@ -0,0 +1,41 @@ +# Require_On_MutationPayload + +## UserRequest + +```graphql +mutation { + createUser { + user { + nestedField { + otherField + } + } + } +} +``` + +## QueryPlan + +```json +{ + "document": "mutation { createUser { user { nestedField { otherField } } } }", + "rootNode": { + "type": "Sequence", + "nodes": [ + { + "type": "Resolve", + "subgraph": "subgraphB", + "document": "mutation fetch_createUser_1 { createUser { user { nestedField { otherField } } } }", + "selectionSetId": 0 + }, + { + "type": "Compose", + "selectionSetIds": [ + 0 + ] + } + ] + } +} +``` + diff --git a/src/HotChocolate/Fusion/test/Shared/DemoSubgraph.cs b/src/HotChocolate/Fusion/test/Shared/DemoSubgraph.cs index f5d1f345102..531426bb2cb 100644 --- a/src/HotChocolate/Fusion/test/Shared/DemoSubgraph.cs +++ b/src/HotChocolate/Fusion/test/Shared/DemoSubgraph.cs @@ -100,4 +100,17 @@ public SubgraphConfiguration ToConfiguration(bool onlyHttp = false) new WebSocketClientConfiguration(WebSocketEndpointUri), }, null); + + public static SubgraphConfiguration CreateSubgraphConfig( + string name, + Uri httpEndpointUri, + string schema) + => new( + name, + schema, + [], + [ + new HttpClientConfiguration(httpEndpointUri) + ], + null); }