diff --git a/dictionary.txt b/dictionary.txt index dd1be91d592..344189d6d45 100644 --- a/dictionary.txt +++ b/dictionary.txt @@ -13,8 +13,6 @@ anthropics Antiforgery apphost appsettings -ASPDEPR004 -ASPDEPR008 aspnet aspnetcore Astromech diff --git a/src/Directory.Build.props b/src/Directory.Build.props index ac52199e192..8c04731c650 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -17,7 +17,7 @@ true - NU1901;NU1902;NU1903;NU1904;ASPDEPR004;ASPDEPR008 + NU1901;NU1902;NU1903;NU1904 diff --git a/src/HotChocolate/Adapters/test/Adapters.Mcp.Tests/CoreIntegrationTests.cs b/src/HotChocolate/Adapters/test/Adapters.Mcp.Tests/CoreIntegrationTests.cs index 53d9a48ecf6..6a4a4c8583a 100644 --- a/src/HotChocolate/Adapters/test/Adapters.Mcp.Tests/CoreIntegrationTests.cs +++ b/src/HotChocolate/Adapters/test/Adapters.Mcp.Tests/CoreIntegrationTests.cs @@ -8,9 +8,9 @@ using HotChocolate.Types.Descriptors; using HotChocolate.Types.Descriptors.Configurations; using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.TestHost; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; using Microsoft.IdentityModel.Tokens; using ModelContextProtocol.Client; using ModelContextProtocol.Protocol; @@ -29,26 +29,24 @@ await storage.AddOrUpdateToolAsync( new OperationToolDefinition( Utf8GraphQLParser.Parse("query GetBooks { books { title } }")), TestContext.Current.CancellationToken); - var builder = new WebHostBuilder() - .ConfigureServices( - services => services - .AddRouting() - .AddGraphQL("NamedSchema") - .AddAuthorization() - .AddQueryType() - .AddMutationType() - .AddInterfaceType() - .AddUnionType() - .AddObjectType() - .AddObjectType() - .AddMcp() - .AddMcpStorage(storage)) - .Configure( - app => app - .UseRouting() - .UseEndpoints(endpoints => endpoints.MapGraphQLMcp())); - var server = new TestServer(builder); - var mcpClient = await CreateMcpClientAsync(server.CreateClient()); + var webAppBuilder = WebApplication.CreateSlimBuilder(); + webAppBuilder.WebHost.UseTestServer(); + webAppBuilder.Services + .AddRouting() + .AddGraphQL("NamedSchema") + .AddAuthorization() + .AddQueryType() + .AddMutationType() + .AddInterfaceType() + .AddUnionType() + .AddObjectType() + .AddObjectType() + .AddMcp() + .AddMcpStorage(storage); + await using var webApp = webAppBuilder.Build(); + webApp.MapGraphQLMcp(); + webApp.Start(); + var mcpClient = await CreateMcpClientAsync(webApp.GetTestClient()); // act var tools = await mcpClient.ListToolsAsync(cancellationToken: TestContext.Current.CancellationToken); @@ -66,31 +64,26 @@ await storage.AddOrUpdateToolAsync( new OperationToolDefinition( Utf8GraphQLParser.Parse("query GetBooks { books { title } }")), TestContext.Current.CancellationToken); - var builder = new WebHostBuilder() - .ConfigureServices( - services => - { - services - .AddRouting() - .AddGraphQL("NamedSchema") - .AddAuthorization() - .AddQueryType() - .AddMutationType() - .AddInterfaceType() - .AddUnionType() - .AddObjectType() - .AddObjectType() - .AddMcp(); - services - .AddOptions("NamedSchema") - .PostConfigure(setup => setup.StorageFactory = _ => storage); - }) - .Configure( - app => app - .UseRouting() - .UseEndpoints(endpoints => endpoints.MapGraphQLMcp())); - using var server = new TestServer(builder); - var mcpClient = await CreateMcpClientAsync(server.CreateClient()); + var webAppBuilder = WebApplication.CreateSlimBuilder(); + webAppBuilder.WebHost.UseTestServer(); + webAppBuilder.Services + .AddRouting() + .AddGraphQL("NamedSchema") + .AddAuthorization() + .AddQueryType() + .AddMutationType() + .AddInterfaceType() + .AddUnionType() + .AddObjectType() + .AddObjectType() + .AddMcp(); + webAppBuilder.Services + .AddOptions("NamedSchema") + .PostConfigure(setup => setup.StorageFactory = _ => storage); + await using var webApp = webAppBuilder.Build(); + webApp.MapGraphQLMcp(); + webApp.Start(); + var mcpClient = await CreateMcpClientAsync(webApp.GetTestClient()); // act var tools = await mcpClient.ListToolsAsync(cancellationToken: TestContext.Current.CancellationToken); @@ -103,19 +96,16 @@ await storage.AddOrUpdateToolAsync( public void MapGraphQLMcp_Should_Throw_When_AddMcpNotCalled() { // arrange - var builder = new WebHostBuilder() - .ConfigureServices( - services => services - .AddRouting() - .AddGraphQL() - .AddQueryType()) - .Configure( - app => app - .UseRouting() - .UseEndpoints(endpoints => endpoints.MapGraphQLMcp())); + var webAppBuilder = WebApplication.CreateSlimBuilder(); + webAppBuilder.WebHost.UseTestServer(); + webAppBuilder.Services + .AddRouting() + .AddGraphQL() + .AddQueryType(); + var webApp = webAppBuilder.Build(); // act - var exception = Assert.Throws(() => new TestServer(builder)); + var exception = Assert.Throws(() => webApp.MapGraphQLMcp()); // assert Assert.Equal( @@ -128,37 +118,28 @@ public void MapGraphQLMcp_Should_NotBuildSchema_When_Mapping() { // arrange var schemaBuilt = false; - var schemaBuiltWhileMapping = false; - var builder = new WebHostBuilder() - .ConfigureServices( - services => services - .AddRouting() - .AddGraphQLServer() - .ConfigureSchemaServices(_ => schemaBuilt = true) - .AddAuthorization() - .AddQueryType() - .AddMutationType() - .AddInterfaceType() - .AddUnionType() - .AddObjectType() - .AddObjectType() - .AddMcp() - .AddMcpStorage(new TestMcpStorage())) - .Configure( - app => app - .UseRouting() - .UseEndpoints( - endpoints => - { - endpoints.MapGraphQLMcp(); - schemaBuiltWhileMapping = schemaBuilt; - })); + var webAppBuilder = WebApplication.CreateSlimBuilder(); + webAppBuilder.WebHost.UseTestServer(); + webAppBuilder.Services + .AddRouting() + .AddGraphQLServer() + .ConfigureSchemaServices(_ => schemaBuilt = true) + .AddAuthorization() + .AddQueryType() + .AddMutationType() + .AddInterfaceType() + .AddUnionType() + .AddObjectType() + .AddObjectType() + .AddMcp() + .AddMcpStorage(new TestMcpStorage()); + var webApp = webAppBuilder.Build(); // act - _ = new TestServer(builder); + webApp.MapGraphQLMcp(); // assert - Assert.False(schemaBuiltWhileMapping); + Assert.False(schemaBuilt); } [Fact] @@ -174,21 +155,19 @@ await File.ReadAllTextAsync( TestContext.Current.CancellationToken))), TestContext.Current.CancellationToken); var typeModule = new TestTypeModule(); - var builder = new WebHostBuilder() - .ConfigureServices( - services => services - .AddRouting() - .AddGraphQL() - .AddTypeModule(_ => typeModule) - .AddMcp() - .AddMcpStorage(storage)) - .Configure( - app => app - .UseRouting() - .UseEndpoints(endpoints => endpoints.MapGraphQLMcp())); - var server = new TestServer(builder); - var mcpClient1 = await CreateMcpClientAsync(server.CreateClient()); - var mcpClient2 = await CreateMcpClientAsync(server.CreateClient()); + var webAppBuilder = WebApplication.CreateSlimBuilder(); + webAppBuilder.WebHost.UseTestServer(); + webAppBuilder.Services + .AddRouting() + .AddGraphQL() + .AddTypeModule(_ => typeModule) + .AddMcp() + .AddMcpStorage(storage); + await using var webApp = webAppBuilder.Build(); + webApp.MapGraphQLMcp(); + webApp.Start(); + var mcpClient1 = await CreateMcpClientAsync(webApp.GetTestClient()); + var mcpClient2 = await CreateMcpClientAsync(webApp.GetTestClient()); var listChangedResetEvent1 = new ManualResetEventSlim(false); var listChangedResetEvent2 = new ManualResetEventSlim(false); mcpClient1.RegisterNotificationHandler( @@ -213,7 +192,7 @@ await File.ReadAllTextAsync( if (listChangedResetEvent1.Wait(TimeSpan.FromSeconds(5), TestContext.Current.CancellationToken)) { - var mcpClient3 = await CreateMcpClientAsync(server.CreateClient()); + var mcpClient3 = await CreateMcpClientAsync(webApp.GetTestClient()); updatedTools = await mcpClient3.ListToolsAsync(cancellationToken: TestContext.Current.CancellationToken); } @@ -247,54 +226,52 @@ protected override Task CreateTestServerAsync( Action? configureMcpServerOptions = null, Action? configureMcpServer = null) { - var builder = new WebHostBuilder() - .ConfigureServices( - services => - { - services - .AddLogging() - .AddRouting() - .AddAuthentication() - .AddJwtBearer( - o => o.TokenValidationParameters = - new TokenValidationParameters - { - ValidIssuer = TokenIssuer, - ValidAudience = TokenAudience, - IssuerSigningKey = TokenKey - }); + var webAppBuilder = WebApplication.CreateSlimBuilder(); + webAppBuilder.WebHost.UseTestServer(); + webAppBuilder.Services + .AddLogging() + .AddRouting() + .AddAuthentication() + .AddJwtBearer( + o => o.TokenValidationParameters = + new TokenValidationParameters + { + ValidIssuer = TokenIssuer, + ValidAudience = TokenAudience, + IssuerSigningKey = TokenKey + }); - var builder = - services - .AddGraphQLServer() - .ModifyCostOptions(o => o.DefaultListSize = 1) - .AddAuthorization() - .AddMcp(configureMcpServerOptions, configureMcpServer) - .AddMcpStorage(storage) - .AddQueryType() - .AddMutationType() - .AddInterfaceType() - .AddUnionType() - .AddObjectType() - .AddObjectType(); + var graphqlBuilder = + webAppBuilder.Services + .AddGraphQLServer() + .ModifyCostOptions(o => o.DefaultListSize = 1) + .AddAuthorization() + .AddMcp(configureMcpServerOptions, configureMcpServer) + .AddMcpStorage(storage) + .AddQueryType() + .AddMutationType() + .AddInterfaceType() + .AddUnionType() + .AddObjectType() + .AddObjectType(); - if (additionalTypes is not null) - { - builder.AddTypes(additionalTypes); - } + if (additionalTypes is not null) + { + graphqlBuilder.AddTypes(additionalTypes); + } - if (diagnosticEventListener is not null) - { - builder.AddDiagnosticEventListener(_ => diagnosticEventListener); - } - }) - .Configure( - app => app - .UseRouting() - .UseAuthentication() - .UseEndpoints(endpoints => endpoints.MapGraphQLMcp())); + if (diagnosticEventListener is not null) + { + graphqlBuilder.AddDiagnosticEventListener(_ => diagnosticEventListener); + } + + var app = webAppBuilder.Build(); + app.UseRouting(); + app.UseAuthentication(); + app.MapGraphQLMcp(); + app.Start(); - return Task.FromResult(new TestServer(builder)); + return Task.FromResult(app.GetTestServer()); } private sealed class TestTypeModule : TypeModule diff --git a/src/HotChocolate/Adapters/test/Adapters.Mcp.Tests/FusionIntegrationTests.cs b/src/HotChocolate/Adapters/test/Adapters.Mcp.Tests/FusionIntegrationTests.cs index 402aa9fa554..71d6a9ea922 100644 --- a/src/HotChocolate/Adapters/test/Adapters.Mcp.Tests/FusionIntegrationTests.cs +++ b/src/HotChocolate/Adapters/test/Adapters.Mcp.Tests/FusionIntegrationTests.cs @@ -13,9 +13,9 @@ using HotChocolate.Types; using HotChocolate.Types.Mutable; using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.TestHost; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; using Microsoft.IdentityModel.Tokens; using ModelContextProtocol.Client; using ModelContextProtocol.Protocol; @@ -34,7 +34,7 @@ await storage.AddOrUpdateToolAsync( new OperationToolDefinition( Utf8GraphQLParser.Parse("query GetBooks { books { title } }")), TestContext.Current.CancellationToken); - var subgraph = CreateSubgraph([]); + await using var subgraph = StartSubgraph([]); var schemaDocument = await subgraph.Services.GetSchemaAsync( cancellationToken: TestContext.Current.CancellationToken); var schemaComposer = @@ -43,25 +43,20 @@ [new SourceSchemaText(schemaDocument.Name, schemaDocument.ToString())], new SchemaComposerOptions(), new CompositionLog()); var result = schemaComposer.Compose(); - var builder = new WebHostBuilder() - .ConfigureServices( - services => - { - services - .AddRouting() - .AddGraphQLGatewayServer("NamedSchema") - .AddInMemoryConfiguration(result.Value.ToSyntaxNode()) - .AddMcp(); - services - .AddOptions("NamedSchema") - .PostConfigure(setup => setup.StorageFactory = _ => storage); - }) - .Configure( - app => app - .UseRouting() - .UseEndpoints(endpoints => endpoints.MapGraphQLMcp())); - using var server = new TestServer(builder); - var mcpClient = await CreateMcpClientAsync(server.CreateClient()); + var webAppBuilder = WebApplication.CreateSlimBuilder(); + webAppBuilder.WebHost.UseTestServer(); + webAppBuilder.Services + .AddRouting() + .AddGraphQLGatewayServer("NamedSchema") + .AddInMemoryConfiguration(result.Value.ToSyntaxNode()) + .AddMcp(); + webAppBuilder.Services + .AddOptions("NamedSchema") + .PostConfigure(setup => setup.StorageFactory = _ => storage); + await using var webApp = webAppBuilder.Build(); + webApp.MapGraphQLMcp(); + webApp.Start(); + var mcpClient = await CreateMcpClientAsync(webApp.GetTestClient()); // act var tools = await mcpClient.ListToolsAsync(cancellationToken: TestContext.Current.CancellationToken); @@ -82,7 +77,7 @@ await File.ReadAllTextAsync( "__resources__/GetBooksWithTitle1.graphql", TestContext.Current.CancellationToken))), TestContext.Current.CancellationToken); - var subgraph = CreateSubgraph([]); + await using var subgraph = StartSubgraph([]); var schemaDocument = await subgraph.Services.GetSchemaAsync( cancellationToken: TestContext.Current.CancellationToken); var schemaComposer = @@ -97,21 +92,19 @@ [new SourceSchemaText(schemaDocument.Name, schemaDocument.ToString())], schema.ToSyntaxNode(), new JsonDocumentOwner(JsonDocument.Parse("{ }"), new EmptyMemoryOwner())); var configProvider = new TestFusionConfigurationProvider(initialConfig); - var builder = new WebHostBuilder() - .ConfigureServices( - services => services - .AddRouting() - .AddGraphQLGatewayServer() - .AddConfigurationProvider(_ => configProvider) - .AddMcp() - .AddMcpStorage(storage)) - .Configure( - app => app - .UseRouting() - .UseEndpoints(endpoints => endpoints.MapGraphQLMcp())); - var server = new TestServer(builder); - var mcpClient1 = await CreateMcpClientAsync(server.CreateClient()); - var mcpClient2 = await CreateMcpClientAsync(server.CreateClient()); + var webAppBuilder = WebApplication.CreateSlimBuilder(); + webAppBuilder.WebHost.UseTestServer(); + webAppBuilder.Services + .AddRouting() + .AddGraphQLGatewayServer() + .AddConfigurationProvider(_ => configProvider) + .AddMcp() + .AddMcpStorage(storage); + await using var webApp = webAppBuilder.Build(); + webApp.MapGraphQLMcp(); + webApp.Start(); + var mcpClient1 = await CreateMcpClientAsync(webApp.GetTestClient()); + var mcpClient2 = await CreateMcpClientAsync(webApp.GetTestClient()); var listChangedResetEvent1 = new ManualResetEventSlim(false); var listChangedResetEvent2 = new ManualResetEventSlim(false); mcpClient1.RegisterNotificationHandler( @@ -141,7 +134,7 @@ [new SourceSchemaText(schemaDocument.Name, schemaDocument.ToString())], if (listChangedResetEvent1.Wait(TimeSpan.FromSeconds(5), TestContext.Current.CancellationToken)) { - var mcpClient3 = await CreateMcpClientAsync(server.CreateClient()); + var mcpClient3 = await CreateMcpClientAsync(webApp.GetTestClient()); updatedTools = await mcpClient3.ListToolsAsync(cancellationToken: TestContext.Current.CancellationToken); } @@ -175,7 +168,7 @@ protected override async Task CreateTestServerAsync( Action? configureMcpServerOptions = null, Action? configureMcpServer = null) { - var subgraph = CreateSubgraph(additionalTypes); + var subgraph = StartSubgraph(additionalTypes); var schemaDocument = await subgraph.Services.GetSchemaAsync(); var schemaComposer = new SchemaComposer( @@ -184,91 +177,87 @@ [new SourceSchemaText(schemaDocument.Name, schemaDocument.ToString())], new CompositionLog()); var result = schemaComposer.Compose(); - var builder = new WebHostBuilder() - .ConfigureServices( - services => - { - services - .AddHeaderPropagation(options => options.Headers.Add("Authorization")) - .AddLogging() - .AddRouting() - .AddAuthentication(); - - services - .AddHttpClient(schemaDocument.Name) - .AddHeaderPropagation() - .ConfigurePrimaryHttpMessageHandler(() => subgraph.CreateHandler()); - - var builder = - services - .AddGraphQLGatewayServer() - .ModifyRequestOptions(o => o.IncludeExceptionDetails = true) - .AddInMemoryConfiguration(result.Value.ToSyntaxNode()) - .AddHttpClientConfiguration( - schemaDocument.Name, - new Uri("http://localhost:5000/graphql")) - .AddMcp(configureMcpServerOptions, configureMcpServer) - .AddMcpStorage(storage); - - if (diagnosticEventListener is not null) - { - builder.AddDiagnosticEventListener(_ => diagnosticEventListener); - } - }) - .Configure( - app => app - .UseRouting() - .UseHeaderPropagation() - .UseAuthentication() - .UseEndpoints(endpoints => endpoints.MapGraphQLMcp())); - - return new TestServer(builder); + var webAppBuilder = WebApplication.CreateSlimBuilder(); + webAppBuilder.WebHost.UseTestServer(); + webAppBuilder.Services + .AddHeaderPropagation(options => options.Headers.Add("Authorization")) + .AddLogging() + .AddRouting() + .AddAuthentication(); + + webAppBuilder.Services + .AddHttpClient(schemaDocument.Name) + .AddHeaderPropagation() + .ConfigurePrimaryHttpMessageHandler(subgraph.GetTestServer().CreateHandler); + + var gatewayBuilder = + webAppBuilder.Services + .AddGraphQLGatewayServer() + .ModifyRequestOptions(o => o.IncludeExceptionDetails = true) + .AddInMemoryConfiguration(result.Value.ToSyntaxNode()) + .AddHttpClientConfiguration( + schemaDocument.Name, + new Uri("http://localhost:5000/graphql")) + .AddMcp(configureMcpServerOptions, configureMcpServer) + .AddMcpStorage(storage); + + if (diagnosticEventListener is not null) + { + gatewayBuilder.AddDiagnosticEventListener(_ => diagnosticEventListener); + } + + var app = webAppBuilder.Build(); + app.UseRouting(); + app.UseHeaderPropagation(); + app.UseAuthentication(); + app.MapGraphQLMcp(); + app.Start(); + + return app.GetTestServer(); } - private static TestServer CreateSubgraph(ITypeDefinition[]? additionalTypes) + private static WebApplication StartSubgraph(ITypeDefinition[]? additionalTypes) { - var builder = new WebHostBuilder() - .ConfigureServices( - services => - { - services - .AddLogging() - .AddRouting() - .AddAuthentication() - .AddJwtBearer( - o => o.TokenValidationParameters = - new TokenValidationParameters - { - ValidIssuer = TokenIssuer, - ValidAudience = TokenAudience, - IssuerSigningKey = TokenKey - }); - - var builder = - services - .AddGraphQLServer() - .AddSourceSchemaDefaults() - .ModifyCostOptions(o => o.DefaultListSize = 1) - .AddAuthorization() - .AddQueryType() - .AddMutationType() - .AddInterfaceType() - .AddUnionType() - .AddObjectType() - .AddObjectType(); - - if (additionalTypes is not null) + var webAppBuilder = WebApplication.CreateSlimBuilder(); + webAppBuilder.WebHost.UseTestServer(); + webAppBuilder.Services + .AddLogging() + .AddRouting() + .AddAuthentication() + .AddJwtBearer( + o => o.TokenValidationParameters = + new TokenValidationParameters { - builder.AddTypes(additionalTypes); - } - }) - .Configure( - app => app - .UseRouting() - .UseAuthentication() - .UseEndpoints(endpoints => endpoints.MapGraphQL())); - - return new TestServer(builder); + ValidIssuer = TokenIssuer, + ValidAudience = TokenAudience, + IssuerSigningKey = TokenKey + }); + + var graphqlBuilder = + webAppBuilder.Services + .AddGraphQLServer() + .AddSourceSchemaDefaults() + .ModifyCostOptions(o => o.DefaultListSize = 1) + .AddAuthorization() + .AddQueryType() + .AddMutationType() + .AddInterfaceType() + .AddUnionType() + .AddObjectType() + .AddObjectType(); + + if (additionalTypes is not null) + { + graphqlBuilder.AddTypes(additionalTypes); + } + + var app = webAppBuilder.Build(); + app.UseRouting(); + app.UseAuthentication(); + app.MapGraphQL(); + app.Start(); + + return app; } private sealed class TestFusionConfigurationProvider(FusionConfiguration initialConfig) diff --git a/src/HotChocolate/Adapters/test/Adapters.OpenApi.Tests/Endpoints/FusionHttpEndpointIntegrationTests.cs b/src/HotChocolate/Adapters/test/Adapters.OpenApi.Tests/Endpoints/FusionHttpEndpointIntegrationTests.cs index 64b2656402c..2fb15c8dcfa 100644 --- a/src/HotChocolate/Adapters/test/Adapters.OpenApi.Tests/Endpoints/FusionHttpEndpointIntegrationTests.cs +++ b/src/HotChocolate/Adapters/test/Adapters.OpenApi.Tests/Endpoints/FusionHttpEndpointIntegrationTests.cs @@ -8,9 +8,9 @@ using HotChocolate.Fusion.Options; using HotChocolate.Language; using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.TestHost; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; using Xunit.Sdk; namespace HotChocolate.Adapters.OpenApi; @@ -90,31 +90,26 @@ query GetUsers @http(method: GET, route: "/users") { } } """); - var builder = new WebHostBuilder() - .ConfigureServices(services => - { - services.AddRouting(); - services.AddHttpClient("A") - .ConfigurePrimaryHttpMessageHandler(_subgraph.CreateHandler); - services.AddGraphQLGatewayServer("NamedSchema") - .AddInMemoryConfiguration(_compositeSchema) - .AddHttpClientConfiguration("A", new Uri("http://localhost:5000/graphql")) - .AddOpenApi(); - services.AddOptions("NamedSchema") - .PostConfigure(setup => setup.StorageFactory = _ => storage); - services.AddOpenApi(options => options.AddGraphQLTransformer()); - }) - .Configure(app => - { - app.UseRouting(); - app.UseEndpoints(endpoints => - { - endpoints.MapOpenApi(); - endpoints.MapOpenApiEndpoints(); - }); - }); - using var server = new TestServer(builder); - var client = server.CreateClient(); + var webAppBuilder = WebApplication.CreateSlimBuilder(); + webAppBuilder.WebHost.UseTestServer(); + webAppBuilder.Services.AddRouting(); + webAppBuilder.Services + .AddHttpClient("A") + .ConfigurePrimaryHttpMessageHandler(_subgraph.CreateHandler); + webAppBuilder.Services + .AddGraphQLGatewayServer("NamedSchema") + .AddInMemoryConfiguration(_compositeSchema) + .AddHttpClientConfiguration("A", new Uri("http://localhost:5000/graphql")) + .AddOpenApi(); + webAppBuilder.Services + .AddOptions("NamedSchema") + .PostConfigure(setup => setup.StorageFactory = _ => storage); + webAppBuilder.Services.AddOpenApi(options => options.AddGraphQLTransformer()); + await using var webApp = webAppBuilder.Build(); + webApp.MapOpenApi(); + webApp.MapOpenApiEndpoints(); + webApp.Start(); + var client = webApp.GetTestClient(); // act var response = await client.GetAsync("/users", TestContext.Current.CancellationToken); @@ -131,28 +126,25 @@ query GetUsers @http(method: GET, route: "/users") { public void MapOpenApiEndpoints_Should_NotResolveSchemaName_When_MultipleNamedSchemasRegistered() { // arrange - var builder = new WebHostBuilder() - .ConfigureServices(services => - { - services.AddRouting(); - - foreach (var schemaName in new[] { "alpha", "beta" }) - { - services.AddGraphQLGatewayServer(schemaName) - .AddInMemoryConfiguration(_compositeSchema) - .AddOpenApi(); - services.AddOptions(schemaName) - .PostConfigure(setup => setup.StorageFactory = _ => new TestOpenApiDefinitionStorage()); - } - }) - .Configure(app => - { - app.UseRouting(); - app.UseEndpoints(endpoints => endpoints.MapOpenApiEndpoints()); - }); + var webAppBuilder = WebApplication.CreateSlimBuilder(); + webAppBuilder.WebHost.UseTestServer(); + webAppBuilder.Services.AddRouting(); + + foreach (var schemaName in new[] { "alpha", "beta" }) + { + webAppBuilder.Services + .AddGraphQLGatewayServer(schemaName) + .AddInMemoryConfiguration(_compositeSchema) + .AddOpenApi(); + webAppBuilder.Services + .AddOptions(schemaName) + .PostConfigure(setup => setup.StorageFactory = _ => new TestOpenApiDefinitionStorage()); + } + + var webApp = webAppBuilder.Build(); // act - var exception = Assert.Throws(() => new TestServer(builder)); + var exception = Assert.Throws(() => webApp.MapOpenApiEndpoints()); // assert Assert.Equal( diff --git a/src/HotChocolate/Adapters/test/Adapters.OpenApi.Tests/Endpoints/HttpEndpointIntegrationTests.cs b/src/HotChocolate/Adapters/test/Adapters.OpenApi.Tests/Endpoints/HttpEndpointIntegrationTests.cs index 435f2712c62..011849d0d18 100644 --- a/src/HotChocolate/Adapters/test/Adapters.OpenApi.Tests/Endpoints/HttpEndpointIntegrationTests.cs +++ b/src/HotChocolate/Adapters/test/Adapters.OpenApi.Tests/Endpoints/HttpEndpointIntegrationTests.cs @@ -3,9 +3,9 @@ using System.Text.Json; using HotChocolate.Adapters.OpenApi.Configuration; using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.TestHost; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; namespace HotChocolate.Adapters.OpenApi; @@ -40,23 +40,19 @@ query GetUsers @http(method: GET, route: "/users") { } } """); - var builder = new WebHostBuilder() - .ConfigureServices(services => - { - services.AddRouting(); - services.AddGraphQLServer("NamedSchema") - .ModifyCostOptions(o => o.DefaultListSize = 1) - .AddOpenApi() - .AddOpenApiDefinitionStorage(storage) - .AddBasicServer(); - }) - .Configure(app => - { - app.UseRouting(); - app.UseEndpoints(endpoints => endpoints.MapOpenApiEndpoints()); - }); - var server = new TestServer(builder); - var client = server.CreateClient(); + var webAppBuilder = WebApplication.CreateSlimBuilder(); + webAppBuilder.WebHost.UseTestServer(); + webAppBuilder.Services.AddRouting(); + webAppBuilder.Services + .AddGraphQLServer("NamedSchema") + .ModifyCostOptions(o => o.DefaultListSize = 1) + .AddOpenApi() + .AddOpenApiDefinitionStorage(storage) + .AddBasicServer(); + await using var webApp = webAppBuilder.Build(); + webApp.MapOpenApiEndpoints(); + webApp.Start(); + var client = webApp.GetTestClient(); // act var response = await client.GetAsync("/users", TestContext.Current.CancellationToken); @@ -77,29 +73,23 @@ query GetUsers @http(method: GET, route: "/users") { } } """); - var builder = new WebHostBuilder() - .ConfigureServices(services => - { - services.AddRouting(); - services.AddGraphQLServer("NamedSchema") - .ModifyCostOptions(o => o.DefaultListSize = 1) - .AddOpenApi() - .AddBasicServer(); - services.AddOptions("NamedSchema") - .PostConfigure(setup => setup.StorageFactory = _ => storage); - services.AddOpenApi(options => options.AddGraphQLTransformer()); - }) - .Configure(app => - { - app.UseRouting(); - app.UseEndpoints(endpoints => - { - endpoints.MapOpenApi(); - endpoints.MapOpenApiEndpoints(); - }); - }); - using var server = new TestServer(builder); - var client = server.CreateClient(); + var webAppBuilder = WebApplication.CreateSlimBuilder(); + webAppBuilder.WebHost.UseTestServer(); + webAppBuilder.Services.AddRouting(); + webAppBuilder.Services + .AddGraphQLServer("NamedSchema") + .ModifyCostOptions(o => o.DefaultListSize = 1) + .AddOpenApi() + .AddBasicServer(); + webAppBuilder.Services + .AddOptions("NamedSchema") + .PostConfigure(setup => setup.StorageFactory = _ => storage); + webAppBuilder.Services.AddOpenApi(options => options.AddGraphQLTransformer()); + await using var webApp = webAppBuilder.Build(); + webApp.MapOpenApi(); + webApp.MapOpenApiEndpoints(); + webApp.Start(); + var client = webApp.GetTestClient(); // act var response = await client.GetAsync("/users", TestContext.Current.CancellationToken); @@ -116,28 +106,25 @@ query GetUsers @http(method: GET, route: "/users") { public void MapOpenApiEndpoints_Should_NotResolveSchemaName_When_MultipleNamedSchemasRegistered() { // arrange - var builder = new WebHostBuilder() - .ConfigureServices(services => - { - services.AddRouting(); - - foreach (var schemaName in new[] { "alpha", "beta" }) - { - services.AddGraphQLServer(schemaName) - .AddOpenApi() - .AddBasicServer(); - services.AddOptions(schemaName) - .PostConfigure(setup => setup.StorageFactory = _ => new TestOpenApiDefinitionStorage()); - } - }) - .Configure(app => - { - app.UseRouting(); - app.UseEndpoints(endpoints => endpoints.MapOpenApiEndpoints()); - }); + var webAppBuilder = WebApplication.CreateSlimBuilder(); + webAppBuilder.WebHost.UseTestServer(); + webAppBuilder.Services.AddRouting(); + + foreach (var schemaName in new[] { "alpha", "beta" }) + { + webAppBuilder.Services + .AddGraphQLServer(schemaName) + .AddOpenApi() + .AddBasicServer(); + webAppBuilder.Services + .AddOptions(schemaName) + .PostConfigure(setup => setup.StorageFactory = _ => new TestOpenApiDefinitionStorage()); + } + + var webApp = webAppBuilder.Build(); // act - var exception = Assert.Throws(() => new TestServer(builder)); + var exception = Assert.Throws(() => webApp.MapOpenApiEndpoints()); // assert Assert.Equal( @@ -150,20 +137,14 @@ public void MapOpenApiEndpoints_Should_NotResolveSchemaName_When_MultipleNamedSc public void MapOpenApiEndpoints_Should_Throw_When_AddOpenApiNotCalled() { // arrange - var builder = new WebHostBuilder() - .ConfigureServices(services => - { - services.AddRouting(); - services.AddGraphQLServer(); - }) - .Configure(app => - { - app.UseRouting(); - app.UseEndpoints(endpoints => endpoints.MapOpenApiEndpoints()); - }); + var webAppBuilder = WebApplication.CreateSlimBuilder(); + webAppBuilder.WebHost.UseTestServer(); + webAppBuilder.Services.AddRouting(); + webAppBuilder.Services.AddGraphQLServer(); + var webApp = webAppBuilder.Build(); // act - var exception = Assert.Throws(() => new TestServer(builder)); + var exception = Assert.Throws(() => webApp.MapOpenApiEndpoints()); // assert Assert.Equal( diff --git a/src/HotChocolate/Adapters/test/Adapters.OpenApi.Tests/Endpoints/KestrelHttpEndpointIntegrationTests.cs b/src/HotChocolate/Adapters/test/Adapters.OpenApi.Tests/Endpoints/KestrelHttpEndpointIntegrationTests.cs index 4334152e140..73181a638f0 100644 --- a/src/HotChocolate/Adapters/test/Adapters.OpenApi.Tests/Endpoints/KestrelHttpEndpointIntegrationTests.cs +++ b/src/HotChocolate/Adapters/test/Adapters.OpenApi.Tests/Endpoints/KestrelHttpEndpointIntegrationTests.cs @@ -3,7 +3,6 @@ using System.Text.Json; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Hosting.Server.Features; using Microsoft.Extensions.DependencyInjection; namespace HotChocolate.Adapters.OpenApi; @@ -51,7 +50,7 @@ public async Task Http_Post_Body_Succeeds_On_Kestrel() finally { await host.StopAsync(cancellationToken); - host.Dispose(); + await host.DisposeAsync(); } } @@ -79,47 +78,35 @@ public async Task Http_Put_Body_Succeeds_On_Kestrel() finally { await host.StopAsync(cancellationToken); - host.Dispose(); + await host.DisposeAsync(); } } - private async Task StartKestrelHostAsync( + private async Task StartKestrelHostAsync( IOpenApiDefinitionStorage storage, CancellationToken cancellationToken) { - var host = new WebHostBuilder() - .UseKestrel() - .UseUrls("http://127.0.0.1:0") - .ConfigureServices(services => - { - services.AddRouting(); - - services.AddGraphQLServer() - .AddOpenApi() - .AddOpenApiDefinitionStorage(storage) - .AddBasicServer(); - }) - .Configure(app => - { - app.UseRouting(); - app.UseEndpoints(endpoints => - { - endpoints.MapOpenApiEndpoints(); - endpoints.MapGraphQL(); - }); - }) - .Build(); + var builder = WebApplication.CreateSlimBuilder(); + builder.WebHost.UseKestrel(); + builder.WebHost.UseUrls("http://127.0.0.1:0"); + builder.Services.AddRouting(); + builder.Services + .AddGraphQLServer() + .AddOpenApi() + .AddOpenApiDefinitionStorage(storage) + .AddBasicServer(); + + var host = builder.Build(); + host.MapOpenApiEndpoints(); + host.MapGraphQL(); await host.StartAsync(cancellationToken); return host; } - private static Uri GetBaseAddress(IWebHost host) + private static Uri GetBaseAddress(WebApplication host) { - var addresses = host.ServerFeatures.Get()?.Addresses - ?? throw new InvalidOperationException("Kestrel did not expose any server addresses."); - - var address = addresses.FirstOrDefault() + var address = host.Urls.FirstOrDefault() ?? throw new InvalidOperationException("Kestrel did not bind to any address."); return new Uri(address); diff --git a/src/HotChocolate/Adapters/test/Adapters.OpenApi.Tests/OpenApi/OpenApiIntegrationTests.cs b/src/HotChocolate/Adapters/test/Adapters.OpenApi.Tests/OpenApi/OpenApiIntegrationTests.cs index 064b66faaa1..e28953661be 100644 --- a/src/HotChocolate/Adapters/test/Adapters.OpenApi.Tests/OpenApi/OpenApiIntegrationTests.cs +++ b/src/HotChocolate/Adapters/test/Adapters.OpenApi.Tests/OpenApi/OpenApiIntegrationTests.cs @@ -1,7 +1,7 @@ using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.TestHost; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; namespace HotChocolate.Adapters.OpenApi; @@ -30,23 +30,19 @@ query GetUsers @http(method: GET, route: "/users") { } } """); - var builder = new WebHostBuilder() - .ConfigureServices(services => - { - services.AddRouting(); - services.AddGraphQLServer("NamedSchema") - .AddOpenApi() - .AddOpenApiDefinitionStorage(storage) - .AddBasicServer(); - services.AddOpenApi(options => options.AddGraphQLTransformer()); - }) - .Configure(app => - { - app.UseRouting(); - app.UseEndpoints(endpoints => endpoints.MapOpenApi()); - }); - using var server = new TestServer(builder); - var client = server.CreateClient(); + var webAppBuilder = WebApplication.CreateSlimBuilder(); + webAppBuilder.WebHost.UseTestServer(); + webAppBuilder.Services.AddRouting(); + webAppBuilder.Services + .AddGraphQLServer("NamedSchema") + .AddOpenApi() + .AddOpenApiDefinitionStorage(storage) + .AddBasicServer(); + webAppBuilder.Services.AddOpenApi(options => options.AddGraphQLTransformer()); + await using var webApp = webAppBuilder.Build(); + webApp.MapOpenApi(); + webApp.Start(); + var client = webApp.GetTestClient(); // act var document = await GetOpenApiDocumentAsync(client); diff --git a/src/HotChocolate/Adapters/test/Adapters.OpenApi.Tests/OpenApiTestBase.cs b/src/HotChocolate/Adapters/test/Adapters.OpenApi.Tests/OpenApiTestBase.cs index 0c2093c52b1..54b0144ccfa 100644 --- a/src/HotChocolate/Adapters/test/Adapters.OpenApi.Tests/OpenApiTestBase.cs +++ b/src/HotChocolate/Adapters/test/Adapters.OpenApi.Tests/OpenApiTestBase.cs @@ -165,11 +165,7 @@ public ValueTask InitializeAsync() protected virtual ValueTask InitializeAsync(TestServerSession serverSession) => ValueTask.CompletedTask; - public ValueTask DisposeAsync() - { - _testServerSession.Dispose(); - return ValueTask.CompletedTask; - } + public ValueTask DisposeAsync() => _testServerSession.DisposeAsync(); protected abstract void ConfigureStorage( IServiceCollection services, diff --git a/src/HotChocolate/Adapters/test/Adapters.OpenApi.Tests/TestServerSession.cs b/src/HotChocolate/Adapters/test/Adapters.OpenApi.Tests/TestServerSession.cs index 2269d1039fb..c4e410094d1 100644 --- a/src/HotChocolate/Adapters/test/Adapters.OpenApi.Tests/TestServerSession.cs +++ b/src/HotChocolate/Adapters/test/Adapters.OpenApi.Tests/TestServerSession.cs @@ -1,37 +1,44 @@ using System.Threading.Channels; using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.TestHost; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; namespace HotChocolate.Adapters.OpenApi; -public sealed class TestServerSession : IDisposable +public sealed class TestServerSession : IAsyncDisposable { - private readonly Channel _cleanupPipeline = Channel.CreateUnbounded(); + private readonly Channel _cleanupPipeline = Channel.CreateUnbounded(); private bool _disposed; public TestServer CreateServer( Action configureServices, Action configureApplication) { - var builder = new WebHostBuilder() - .Configure(configureApplication) - .ConfigureServices(configureServices); + var options = new WebApplicationOptions + { + ApplicationName = typeof(TestServerSession).Assembly.GetName().Name! + }; + var builder = WebApplication.CreateEmptyBuilder(options); + builder.WebHost.UseTestServer(); + configureServices(builder.Services); + + var app = builder.Build(); + configureApplication(app); - var server = new TestServer(builder); + app.Start(); - if (!_cleanupPipeline.Writer.TryWrite(server)) + if (!_cleanupPipeline.Writer.TryWrite(app)) { - server.Dispose(); + ((IHost)app).Dispose(); throw new InvalidOperationException( "Failed to add test server to cleanup pipeline."); } - return server; + return app.GetTestServer(); } - public void Dispose() + public async ValueTask DisposeAsync() { if (_disposed) { @@ -42,9 +49,10 @@ public void Dispose() _cleanupPipeline.Writer.TryComplete(); - while (_cleanupPipeline.Reader.TryRead(out var server)) + while (_cleanupPipeline.Reader.TryRead(out var app)) { - server.Dispose(); + await app.StopAsync(); + await app.DisposeAsync(); } } } diff --git a/src/HotChocolate/AspNetCore/test/AspNetCore.Authorization.Tests/AuthorizationPolicyProviderTests.cs b/src/HotChocolate/AspNetCore/test/AspNetCore.Authorization.Tests/AuthorizationPolicyProviderTests.cs index ce99e26376f..64d24f04c66 100644 --- a/src/HotChocolate/AspNetCore/test/AspNetCore.Authorization.Tests/AuthorizationPolicyProviderTests.cs +++ b/src/HotChocolate/AspNetCore/test/AspNetCore.Authorization.Tests/AuthorizationPolicyProviderTests.cs @@ -130,7 +130,7 @@ public class CustomAuthorizationPolicyProvider(bool allowsCaching) : IAuthorizat public Task GetDefaultPolicyAsync() => throw new NotImplementedException(); - public Task GetFallbackPolicyAsync() => throw new NotImplementedException(); + public Task GetFallbackPolicyAsync() => Task.FromResult(null); public virtual bool AllowsCachingPolicies => allowsCaching; } diff --git a/src/HotChocolate/AspNetCore/test/AspNetCore.Tests.Utilities/ServerTestBase.cs b/src/HotChocolate/AspNetCore/test/AspNetCore.Tests.Utilities/ServerTestBase.cs index 82b50306dff..ce803f28814 100644 --- a/src/HotChocolate/AspNetCore/test/AspNetCore.Tests.Utilities/ServerTestBase.cs +++ b/src/HotChocolate/AspNetCore/test/AspNetCore.Tests.Utilities/ServerTestBase.cs @@ -169,12 +169,7 @@ protected virtual TestServer CreateServer( /// /// Disposes the servers the current test created. /// - public virtual ValueTask DisposeAsync() - { - ServerFactory.DisposeServers(); - - return ValueTask.CompletedTask; - } + public virtual ValueTask DisposeAsync() => ServerFactory.DisposeServersAsync(); [DirectiveType(DirectiveLocation.Subscription)] public class Foo diff --git a/src/HotChocolate/AspNetCore/test/AspNetCore.Tests.Utilities/Subscriptions/Apollo/SubscriptionTestBase.cs b/src/HotChocolate/AspNetCore/test/AspNetCore.Tests.Utilities/Subscriptions/Apollo/SubscriptionTestBase.cs index 9b63ed51451..ce447d08430 100644 --- a/src/HotChocolate/AspNetCore/test/AspNetCore.Tests.Utilities/Subscriptions/Apollo/SubscriptionTestBase.cs +++ b/src/HotChocolate/AspNetCore/test/AspNetCore.Tests.Utilities/Subscriptions/Apollo/SubscriptionTestBase.cs @@ -19,7 +19,7 @@ public SubscriptionTestBase(TestServerFactory serverFactory) WebSocket webSocket, string type, CancellationToken cancellationToken) - => WaitForMessage(webSocket, type, TimeSpan.FromMilliseconds(500), cancellationToken); + => WaitForMessage(webSocket, type, TimeSpan.FromSeconds(5), cancellationToken); protected async Task WaitForMessage( WebSocket webSocket, @@ -110,39 +110,28 @@ protected static WebSocketClient CreateWebSocketClient(TestServer testServer) return client; } - protected static async Task TryTest(Func action) + protected static async Task RunTest(Func action) { - // we will try four times .... - using var cts = new CancellationTokenSource(Debugger.IsAttached ? 600_000_000 : 60_000); - var ct = cts.Token; - var count = 0; - var wait = 50; + using var cts = new CancellationTokenSource(Debugger.IsAttached ? 600_000_000 : 30_000); + await action(cts.Token); + } + + protected static async Task WaitForServerClose( + WebSocket webSocket, + CancellationToken cancellationToken) + { + var buffer = new byte[256]; - while (true) + while (webSocket.State is WebSocketState.Open or WebSocketState.CloseSent) { - ct.ThrowIfCancellationRequested(); + var result = await webSocket.ReceiveAsync( + new ArraySegment(buffer), + cancellationToken); - if (count < 3) - { - try - { - await action(ct).ConfigureAwait(false); - break; - } - catch - { - // try again - } - } - else + if (result.MessageType == WebSocketMessageType.Close) { - await action(ct).ConfigureAwait(false); - break; + return; } - - await Task.Delay(wait, ct).ConfigureAwait(false); - wait *= 2; - count++; } } } diff --git a/src/HotChocolate/AspNetCore/test/AspNetCore.Tests.Utilities/Subscriptions/GraphQLOverWebSocket/SubscriptionTestBase.cs b/src/HotChocolate/AspNetCore/test/AspNetCore.Tests.Utilities/Subscriptions/GraphQLOverWebSocket/SubscriptionTestBase.cs index 0bc70614506..84ac6f81819 100644 --- a/src/HotChocolate/AspNetCore/test/AspNetCore.Tests.Utilities/Subscriptions/GraphQLOverWebSocket/SubscriptionTestBase.cs +++ b/src/HotChocolate/AspNetCore/test/AspNetCore.Tests.Utilities/Subscriptions/GraphQLOverWebSocket/SubscriptionTestBase.cs @@ -21,7 +21,7 @@ public class SubscriptionTestBase(TestServerFactory serverFactory) WebSocket webSocket, string type, CancellationToken cancellationToken) - => WaitForMessage(webSocket, type, TimeSpan.FromSeconds(1), cancellationToken); + => WaitForMessage(webSocket, type, TimeSpan.FromSeconds(5), cancellationToken); protected async Task WaitForMessage( WebSocket webSocket, @@ -159,39 +159,28 @@ protected static WebSocketClient CreateWebSocketClient(TestServer testServer) return client; } - protected static async Task TryTest(Func action) + protected static async Task RunTest(Func action) { - // we will try four times ... - using var cts = new CancellationTokenSource(Debugger.IsAttached ? 600_000_000 : 15_000); - var ct = cts.Token; - var count = 0; - var wait = 50; + using var cts = new CancellationTokenSource(Debugger.IsAttached ? 600_000_000 : 30_000); + await action(cts.Token); + } + + protected static async Task WaitForServerClose( + WebSocket webSocket, + CancellationToken cancellationToken) + { + var buffer = new byte[256]; - while (true) + while (webSocket.State is WebSocketState.Open or WebSocketState.CloseSent) { - ct.ThrowIfCancellationRequested(); + var result = await webSocket.ReceiveAsync( + new ArraySegment(buffer), + cancellationToken); - if (count < 3) - { - try - { - await action(ct).ConfigureAwait(false); - break; - } - catch - { - // try again - } - } - else + if (result.MessageType == WebSocketMessageType.Close) { - await action(ct).ConfigureAwait(false); - break; + return; } - - await Task.Delay(wait, ct).ConfigureAwait(false); - wait *= 2; - count++; } } } diff --git a/src/HotChocolate/AspNetCore/test/AspNetCore.Tests.Utilities/TestServerFactory.cs b/src/HotChocolate/AspNetCore/test/AspNetCore.Tests.Utilities/TestServerFactory.cs index 794ccb00d90..8dbd5a24a63 100644 --- a/src/HotChocolate/AspNetCore/test/AspNetCore.Tests.Utilities/TestServerFactory.cs +++ b/src/HotChocolate/AspNetCore/test/AspNetCore.Tests.Utilities/TestServerFactory.cs @@ -1,41 +1,42 @@ using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.TestHost; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; namespace HotChocolate.AspNetCore.Tests.Utilities; -public class TestServerFactory : IDisposable +public class TestServerFactory : IAsyncDisposable { - private readonly List _instances = []; + private readonly List _instances = []; public TestServer Create( Action configureServices, Action configureApplication) { - var builder = new WebHostBuilder() - .Configure(configureApplication) - .ConfigureServices(services => - { - services.AddHttpContextAccessor(); - configureServices(services); - }); - - var server = new TestServer(builder); - _instances.Add(server); - return server; + var builder = WebApplication.CreateEmptyBuilder(new WebApplicationOptions()); + builder.WebHost.UseTestServer(); + builder.Services.AddHttpContextAccessor(); + configureServices(builder.Services); + + var app = builder.Build(); + configureApplication(app); + + app.Start(); + _instances.Add(app); + return app.GetTestServer(); } - public void Dispose() => DisposeServers(); + public ValueTask DisposeAsync() => DisposeServersAsync(); /// - /// Disposes and releases every server this factory has created so far. + /// Stops, disposes, and releases every server this factory has created so far. /// - public void DisposeServers() + public async ValueTask DisposeServersAsync() { - foreach (var testServer in _instances) + foreach (var app in _instances) { - testServer.Dispose(); + await app.StopAsync(); + await app.DisposeAsync(); } _instances.Clear(); diff --git a/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/GraphQLOverHttpSpecTests.cs b/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/GraphQLOverHttpSpecTests.cs index d977f35c6fe..9d1c84ce7b1 100644 --- a/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/GraphQLOverHttpSpecTests.cs +++ b/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/GraphQLOverHttpSpecTests.cs @@ -448,7 +448,7 @@ public async Task EventStream_When_Accept_Is_All() // arrange var server = CreateStarWarsServer(); var client = server.CreateClient(); - client.Timeout = TimeSpan.FromSeconds(30); + client.Timeout = TimeSpan.FromSeconds(60); // act using var request = new HttpRequestMessage(HttpMethod.Post, s_url); @@ -459,39 +459,25 @@ public async Task EventStream_When_Accept_Is_All() }); request.Headers.Add("Accept", "*/*"); - using var response = await client.SendAsync( + using var httpResponse = await client.SendAsync( request, ResponseHeadersRead, TestContext.Current.CancellationToken); + using var response = new GraphQLHttpResponse(httpResponse); // assert - Snapshot - .Create() - .Add(response) - .MatchInline( - """ - Headers: - Vary: Accept - Cache-Control: no-cache - Content-Type: text/event-stream; charset=utf-8 - --------------------------> - Status Code: OK - --------------------------> - event: next - data: {"data":{"delay":"next"}} - - : - - event: next - data: {"data":{"delay":"next"}} - - : + Assert.Equal(OK, response.StatusCode); + Assert.Equal("text/event-stream", httpResponse.Content.Headers.ContentType?.MediaType); - event: complete - data: + var results = new List(); + await foreach (var result in response.ReadAsResultStreamAsync()) + { + results.Add(result.Data.ToString()); + } - """); + Assert.Equal(2, results.Count); + Assert.All(results, r => Assert.Equal("{\"delay\":\"next\"}", r)); } [Fact] @@ -500,7 +486,7 @@ public async Task EventStream_When_Accept_Is_All_And_Subscription_Directive() // arrange var server = CreateStarWarsServer(); var client = server.CreateClient(); - client.Timeout = TimeSpan.FromSeconds(30); + client.Timeout = TimeSpan.FromSeconds(60); // act using var request = new HttpRequestMessage(HttpMethod.Post, s_url); @@ -511,39 +497,25 @@ public async Task EventStream_When_Accept_Is_All_And_Subscription_Directive() }); request.Headers.Add("Accept", "*/*"); - using var response = await client.SendAsync( + using var httpResponse = await client.SendAsync( request, ResponseHeadersRead, TestContext.Current.CancellationToken); + using var response = new GraphQLHttpResponse(httpResponse); // assert - Snapshot - .Create() - .Add(response) - .MatchInline( - """ - Headers: - Vary: Accept - Cache-Control: no-cache - Content-Type: text/event-stream; charset=utf-8 - --------------------------> - Status Code: OK - --------------------------> - event: next - data: {"data":{"delay":"next"}} - - : - - event: next - data: {"data":{"delay":"next"}} - - : + Assert.Equal(OK, response.StatusCode); + Assert.Equal("text/event-stream", httpResponse.Content.Headers.ContentType?.MediaType); - event: complete - data: + var results = new List(); + await foreach (var result in response.ReadAsResultStreamAsync()) + { + results.Add(result.Data.ToString()); + } - """); + Assert.Equal(2, results.Count); + Assert.All(results, r => Assert.Equal("{\"delay\":\"next\"}", r)); } [Fact] diff --git a/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/Subscriptions/Apollo/WebSocketProtocolTests.cs b/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/Subscriptions/Apollo/WebSocketProtocolTests.cs index 21c6b50653f..61fe537cb46 100644 --- a/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/Subscriptions/Apollo/WebSocketProtocolTests.cs +++ b/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/Subscriptions/Apollo/WebSocketProtocolTests.cs @@ -19,7 +19,7 @@ public class WebSocketProtocolTests(TestServerFactory serverFactory) { [Fact] public Task Send_Connect_AcceptAndKeepAlive() - => TryTest(async ct => + => RunTest(async ct => { // arrange using var testServer = CreateStarWarsServer(); @@ -37,7 +37,7 @@ public Task Send_Connect_AcceptAndKeepAlive() [Fact] public Task Send_Connect_To_Many_Init_Messages() - => TryTest(async ct => + => RunTest(async ct => { // arrange using var testServer = CreateStarWarsServer(); @@ -51,14 +51,14 @@ public Task Send_Connect_To_Many_Init_Messages() await webSocket.SendConnectionInitializeAsync(ct); // assert - await webSocket.ReceiveServerMessageAsync(ct); + await WaitForServerClose(webSocket, ct); Assert.True(webSocket.CloseStatus.HasValue); Assert.Equal(WebSocketCloseStatus.ProtocolError, webSocket.CloseStatus.Value); }); [Fact] public Task No_ConnectionInit_Timeout() - => TryTest(async ct => + => RunTest(async ct => { // arrange using var testServer = CreateStarWarsServer( @@ -75,14 +75,14 @@ public Task No_ConnectionInit_Timeout() using var webSocket = await client.ConnectAsync(SubscriptionUri, ct); // assert - await webSocket.ReceiveServerMessageAsync(ct); + await WaitForServerClose(webSocket, ct); Assert.True(webSocket.CloseStatus.HasValue, "Connection is closed."); Assert.Equal(WebSocketCloseStatus.ProtocolError, webSocket.CloseStatus.Value); }); [Fact] public Task Send_Connect_With_Auth_Accept() - => TryTest(async ct => + => RunTest(async ct => { // arrange var interceptor = new AuthInterceptor(); @@ -104,7 +104,7 @@ public Task Send_Connect_With_Auth_Accept() [Fact] public Task Send_Connect_With_Auth_Reject() - => TryTest(async ct => + => RunTest(async ct => { // arrange var interceptor = new AuthInterceptor(); @@ -120,14 +120,14 @@ public Task Send_Connect_With_Auth_Reject() // assert await WaitForMessage(webSocket, "connection_error", ct); - await webSocket.ReceiveServerMessageAsync(ct); + await WaitForServerClose(webSocket, ct); Assert.True(webSocket.CloseStatus.HasValue, "Connection is closed."); Assert.Equal(WebSocketCloseStatus.NormalClosure, webSocket.CloseStatus.Value); }); [Fact] public Task Send_Connect_AcceptAndKeepAlive_Explicit_Route() - => TryTest(async ct => + => RunTest(async ct => { // arrange using var testServer = CreateServer(b => b.MapGraphQLWebSocket()); @@ -147,7 +147,7 @@ public Task Send_Connect_AcceptAndKeepAlive_Explicit_Route() [Fact] public Task Send_Connect_AcceptAndKeepAlive_Explicit_Route_Explicit_Path() - => TryTest(async ct => + => RunTest(async ct => { // arrange using var testServer = CreateServer(b => b.MapGraphQLWebSocket("/foo/bar")); @@ -167,7 +167,7 @@ public Task Send_Connect_AcceptAndKeepAlive_Explicit_Route_Explicit_Path() [Fact] public Task Send_Terminate() - => TryTest(async ct => + => RunTest(async ct => { // arrange using var testServer = CreateStarWarsServer(); @@ -186,7 +186,7 @@ public Task Send_Terminate() [Fact] public Task Connect_With_Invalid_Protocol() - => TryTest(async ct => + => RunTest(async ct => { // arrange using var testServer = CreateStarWarsServer(); @@ -196,10 +196,9 @@ public Task Connect_With_Invalid_Protocol() // act var socket = await client.ConnectAsync(SubscriptionUri, ct); - var buffer = new byte[1024]; - await socket.ReceiveAsync(buffer, ct); // assert + await WaitForServerClose(socket, ct); Assert.True(socket.CloseStatus.HasValue); Assert.Equal(WebSocketCloseStatus.ProtocolError, socket.CloseStatus.Value); }); @@ -210,7 +209,7 @@ public Task Send_Start_ReceiveDataOnMutation() { var snapshot = new Snapshot(); - return TryTest(async ct => + return RunTest(async ct => { // arrange using var testServer = CreateStarWarsServer(); @@ -250,7 +249,7 @@ await testServer.SendPostRequestAsync( [Fact] public Task Send_Start_Id_Not_Unique() - => TryTest(async ct => + => RunTest(async ct => { // arrange using var testServer = CreateStarWarsServer(); @@ -268,14 +267,14 @@ public Task Send_Start_Id_Not_Unique() await webSocket.SendSubscriptionStartAsync(subscriptionId, request); // assert - await webSocket.ReceiveServerMessageAsync(ct); + await WaitForServerClose(webSocket, ct); Assert.True(webSocket.CloseStatus.HasValue); Assert.Equal(WebSocketCloseStatus.InternalServerError, webSocket.CloseStatus.Value); }); [Fact] public Task Send_Subscribe_No_Id() - => TryTest(async ct => + => RunTest(async ct => { // arrange using var testServer = CreateStarWarsServer(); @@ -287,14 +286,14 @@ public Task Send_Subscribe_No_Id() await webSocket.SendMessageAsync("{ \"type\": \"subscribe\" }", ct); // assert - await webSocket.ReceiveServerMessageAsync(ct); + await WaitForServerClose(webSocket, ct); Assert.True(webSocket.CloseStatus.HasValue); Assert.Equal(CloseReasons.InvalidMessage, (int)webSocket.CloseStatus.Value); }); [Fact] public Task Send_Subscribe_Empty_Id() - => TryTest(async ct => + => RunTest(async ct => { // arrange using var testServer = CreateStarWarsServer(); @@ -306,7 +305,7 @@ public Task Send_Subscribe_Empty_Id() await webSocket.SendMessageAsync("{ \"type\": \"subscribe\", \"id\": \"\" }", ct); // assert - await webSocket.ReceiveServerMessageAsync(ct); + await WaitForServerClose(webSocket, ct); Assert.True(webSocket.CloseStatus.HasValue); Assert.Equal(CloseReasons.InvalidMessage, (int)webSocket.CloseStatus.Value); }); @@ -316,7 +315,7 @@ public Task Send_Subscribe_SyntaxError() { var snapshot = new Snapshot(); - return TryTest( + return RunTest( async ct => { // arrange @@ -348,7 +347,7 @@ await webSocket.SendMessageAsync( [Fact] public Task Send_Subscribe_SyntaxError_No_Id() - => TryTest( + => RunTest( async ct => { // arrange @@ -372,17 +371,21 @@ await webSocket.SendMessageAsync( ct); // assert - await webSocket.ReceiveServerMessageAsync(ct); + await WaitForServerClose(webSocket, ct); Assert.True(webSocket.CloseStatus.HasValue); Assert.Equal(CloseReasons.InvalidMessage, (int)webSocket.CloseStatus.Value); }); [Fact] public Task Send_Start_Stop() - => TryTest(async ct => + => RunTest(async ct => { // arrange - using var testServer = CreateStarWarsServer(); + var diagnostics = new SubscriptionTestDiagnostics(); + using var testServer = CreateStarWarsServer( + configureServices: s => s + .AddGraphQL() + .AddDiagnosticEventListener(_ => diagnostics)); var client = CreateWebSocketClient(testServer); var webSocket = await ConnectToServerAsync(client, ct); @@ -394,6 +397,7 @@ public Task Send_Start_Stop() const string subscriptionId = "abc"; await webSocket.SendSubscriptionStartAsync(subscriptionId, request); + await diagnostics.WaitForSubscribedAsync(ct); await testServer.SendPostRequestAsync(new ClientQueryRequest { @@ -417,6 +421,7 @@ await testServer.SendPostRequestAsync(new ClientQueryRequest // act await webSocket.SendSubscriptionStopAsync(subscriptionId, ct); + await diagnostics.WaitForUnsubscribeAsync(ct); await testServer.SendPostRequestAsync(new ClientQueryRequest { @@ -446,7 +451,7 @@ public Task Send_Start_ValidationError() { var snapshot = new Snapshot(); - return TryTest(async ct => + return RunTest(async ct => { // arrange using var testServer = CreateStarWarsServer(); @@ -470,7 +475,7 @@ public Task Send_Start_ValidationError() [Fact] public Task Send_Invalid_Message_String() - => TryTest(async ct => + => RunTest(async ct => { // arrange using var testServer = CreateStarWarsServer(); @@ -481,14 +486,14 @@ public Task Send_Invalid_Message_String() await webSocket.SendMessageAsync("hello", ct); // assert - await webSocket.ReceiveServerMessageAsync(ct); + await WaitForServerClose(webSocket, ct); Assert.True(webSocket.CloseStatus.HasValue, "Connection is closed."); Assert.Equal(WebSocketCloseStatus.InternalServerError, webSocket.CloseStatus.Value); }); [Fact] public Task Send_Invalid_Message_No_Type() - => TryTest(async ct => + => RunTest(async ct => { // arrange using var testServer = CreateStarWarsServer(); @@ -499,14 +504,14 @@ public Task Send_Invalid_Message_No_Type() await webSocket.SendMessageAsync("{ }", ct); // assert - await webSocket.ReceiveServerMessageAsync(ct); + await WaitForServerClose(webSocket, ct); Assert.True(webSocket.CloseStatus.HasValue, "Connection is closed."); Assert.Equal(CloseReasons.InvalidMessage, (int)webSocket.CloseStatus.Value); }); [Fact] public Task Send_Invalid_Message_Invalid_Type() - => TryTest(async ct => + => RunTest(async ct => { // arrange using var testServer = CreateStarWarsServer(); @@ -517,14 +522,14 @@ public Task Send_Invalid_Message_Invalid_Type() await webSocket.SendMessageAsync("{ \"type\": \"abc\" }", ct); // assert - await webSocket.ReceiveServerMessageAsync(ct); + await WaitForServerClose(webSocket, ct); Assert.True(webSocket.CloseStatus.HasValue, "Connection is closed."); Assert.Equal(CloseReasons.InvalidMessage, (int)webSocket.CloseStatus.Value); }); [Fact] public Task Send_Invalid_Message_Not_An_Object() - => TryTest(async ct => + => RunTest(async ct => { // arrange using var testServer = CreateStarWarsServer(); @@ -535,7 +540,7 @@ public Task Send_Invalid_Message_Not_An_Object() await webSocket.SendMessageAsync("[]", ct); // assert - await webSocket.ReceiveServerMessageAsync(ct); + await WaitForServerClose(webSocket, ct); Assert.True(webSocket.CloseStatus.HasValue, "Connection is closed."); Assert.Equal(CloseReasons.InvalidMessage, (int)webSocket.CloseStatus.Value); }); @@ -543,7 +548,7 @@ public Task Send_Invalid_Message_Not_An_Object() // TODO : FIX Flaky Test [Fact(Skip = "Flaky")] public Task Send_Start_ReceiveDataOnMutation_StripNull() => - TryTest( + RunTest( async ct => { // arrange @@ -598,7 +603,7 @@ await testServer.SendPostRequestAsync( [Fact] public Task Connection_Reject_With_Long_Message_Should_Close_Cleanly() - => TryTest(async ct => + => RunTest(async ct => { // arrange // a rejection message longer than the 123 UTF-8 byte close-frame limit. @@ -620,14 +625,14 @@ public Task Connection_Reject_With_Long_Message_Should_Close_Cleanly() Assert.Equal( longMessage, error.RootElement.GetProperty("payload").GetProperty("message").GetString()); - await webSocket.ReceiveServerMessageAsync(ct); + await WaitForServerClose(webSocket, ct); Assert.True(webSocket.CloseStatus.HasValue, "Connection is closed."); Assert.Equal(WebSocketCloseStatus.NormalClosure, webSocket.CloseStatus.Value); }); [Fact] public Task Connection_Terminate_Should_Invoke_OnClose_Once() - => TryTest(async ct => + => RunTest(async ct => { // arrange var interceptor = new OnCloseCountingInterceptor(); @@ -651,7 +656,7 @@ public Task Connection_Terminate_Should_Invoke_OnClose_Once() [Fact] public Task Client_KeepAlive_Should_Not_Close_Connection() - => TryTest(async ct => + => RunTest(async ct => { // arrange using var testServer = CreateStarWarsServer(); diff --git a/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/Subscriptions/GraphQLOverWebSocket/WebSocketProtocolTests.cs b/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/Subscriptions/GraphQLOverWebSocket/WebSocketProtocolTests.cs index 8a4df40ce2e..947f42024b8 100644 --- a/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/Subscriptions/GraphQLOverWebSocket/WebSocketProtocolTests.cs +++ b/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/Subscriptions/GraphQLOverWebSocket/WebSocketProtocolTests.cs @@ -11,7 +11,6 @@ using HotChocolate.Execution; using HotChocolate.Language; using HotChocolate.PersistedOperations; -using HotChocolate.Subscriptions.Diagnostics; using HotChocolate.Text.Json; using HotChocolate.Transport.Formatters; using HotChocolate.Transport.Sockets.Client; @@ -27,7 +26,7 @@ public class WebSocketProtocolTests(TestServerFactory serverFactory, ITestOutput { [Fact] public Task Send_Connect_Accept() - => TryTest( + => RunTest( async ct => { // arrange @@ -46,7 +45,7 @@ public Task Send_Connect_Accept() [Fact] public Task Send_Multiple_Connect_Messages_Close_Connection() - => TryTest( + => RunTest( async ct => { // arrange @@ -61,14 +60,14 @@ public Task Send_Multiple_Connect_Messages_Close_Connection() await webSocket.SendConnectionInitAsync(ct); // assert - await webSocket.ReceiveServerMessageAsync(ct); + await WaitForServerClose(webSocket, ct); Assert.True(webSocket.CloseStatus.HasValue); Assert.Equal(CloseReasons.TooManyInitAttempts, (int)webSocket.CloseStatus.Value); }); [Fact] public Task Send_Connect_Accept_Ping() - => TryTest( + => RunTest( async ct => { // arrange @@ -100,7 +99,7 @@ public Task Send_Connect_Accept_Ping() [Fact] public Task No_ConnectionInit_Timeout() - => TryTest( + => RunTest( async ct => { // arrange @@ -119,7 +118,7 @@ public Task No_ConnectionInit_Timeout() using var webSocket = await client.ConnectAsync(SubscriptionUri, ct); // assert - await webSocket.ReceiveServerMessageAsync(ct); + await WaitForServerClose(webSocket, ct); Assert.True(webSocket.CloseStatus.HasValue, "Connection is closed."); Assert.Equal( CloseReasons.ConnectionInitWaitTimeout, @@ -128,7 +127,7 @@ public Task No_ConnectionInit_Timeout() [Fact] public Task Send_Connect_With_Auth_Accept() - => TryTest( + => RunTest( async ct => { // arrange @@ -151,7 +150,7 @@ public Task Send_Connect_With_Auth_Accept() [Fact] public Task Send_Connect_With_Auth_Reject() - => TryTest( + => RunTest( async ct => { // arrange @@ -167,14 +166,14 @@ public Task Send_Connect_With_Auth_Reject() await webSocket.SendConnectionInitAsync(ct); // assert - await webSocket.ReceiveServerMessageAsync(ct); + await WaitForServerClose(webSocket, ct); Assert.True(webSocket.CloseStatus.HasValue, "Connection is closed."); Assert.Equal(CloseReasons.Forbidden, (int)webSocket.CloseStatus.Value); }); [Fact] public Task Connection_Rejected_Should_Close_With_Forbidden() - => TryTest( + => RunTest( async ct => { // arrange @@ -190,7 +189,7 @@ public Task Connection_Rejected_Should_Close_With_Forbidden() await webSocket.SendConnectionInitAsync(ct); // assert - await webSocket.ReceiveServerMessageAsync(ct); + await WaitForServerClose(webSocket, ct); Assert.True(webSocket.CloseStatus.HasValue, "Connection is closed."); Assert.Equal(CloseReasons.Forbidden, (int)webSocket.CloseStatus.Value); Assert.Equal("You shall not pass.", webSocket.CloseStatusDescription); @@ -198,7 +197,7 @@ public Task Connection_Rejected_Should_Close_With_Forbidden() [Fact] public Task Send_Connect_Accept_Explicit_Route() - => TryTest( + => RunTest( async ct => { // arrange @@ -219,7 +218,7 @@ public Task Send_Connect_Accept_Explicit_Route() [Fact] public Task Send_Connect_Accept_Explicit_Route_Explicit_Path() - => TryTest( + => RunTest( async ct => { // arrange @@ -240,7 +239,7 @@ public Task Send_Connect_Accept_Explicit_Route_Explicit_Path() [Fact] public Task Connect_With_Invalid_Protocol() - => TryTest( + => RunTest( async ct => { // arrange @@ -252,7 +251,7 @@ public Task Connect_With_Invalid_Protocol() using var socket = await client.ConnectAsync(SubscriptionUri, ct); // assert - await socket.ReceiveServerMessageAsync(ct); + await WaitForServerClose(socket, ct); Assert.True(socket.CloseStatus.HasValue); Assert.Equal(ProtocolError, socket.CloseStatus.Value); }); @@ -262,7 +261,7 @@ public Task Subscribe_ReceiveDataOnMutation() { var snapshot = new Snapshot(); - return TryTest( + return RunTest( async ct => { // arrange @@ -314,7 +313,7 @@ await testServer.SendPostRequestAsync( [Fact] public Task Subscribe_With_PersistedQuery_Extension_Only_Works() - => TryTest( + => RunTest( async ct => { // arrange @@ -381,7 +380,7 @@ await testServer.SendPostRequestAsync( [Fact] public Task Subscribe_Id_Not_Unique() { - return TryTest( + return RunTest( async ct => { // arrange @@ -398,7 +397,7 @@ public Task Subscribe_Id_Not_Unique() await webSocket.SendSubscribeAsync(subscriptionId, payload, ct); // assert - await webSocket.ReceiveServerMessageAsync(ct); + await WaitForServerClose(webSocket, ct); Assert.True(webSocket.CloseStatus.HasValue); Assert.Equal(CloseReasons.SubscriberNotUnique, (int)webSocket.CloseStatus.Value); }); @@ -406,7 +405,7 @@ public Task Subscribe_Id_Not_Unique() [Fact] public Task Send_Subscribe_No_Auth_Close() - => TryTest( + => RunTest( async ct => { // arrange @@ -421,14 +420,14 @@ public Task Send_Subscribe_No_Auth_Close() await webSocket.SendSubscribeAsync(subscriptionId, payload, ct); // assert - await webSocket.ReceiveServerMessageAsync(ct); + await WaitForServerClose(webSocket, ct); Assert.True(webSocket.CloseStatus.HasValue); Assert.Equal(CloseReasons.Unauthorized, (int)webSocket.CloseStatus.Value); }); [Fact] public Task Send_Subscribe_No_Id() - => TryTest( + => RunTest( async ct => { // arrange @@ -441,14 +440,14 @@ public Task Send_Subscribe_No_Id() await webSocket.SendMessageAsync("{ \"type\": \"subscribe\" }", ct); // assert - await webSocket.ReceiveServerMessageAsync(ct); + await WaitForServerClose(webSocket, ct); Assert.True(webSocket.CloseStatus.HasValue); Assert.Equal(CloseReasons.ProtocolError, (int)webSocket.CloseStatus.Value); }); [Fact] public Task Send_Subscribe_Empty_Id() - => TryTest( + => RunTest( async ct => { // arrange @@ -461,7 +460,7 @@ public Task Send_Subscribe_Empty_Id() await webSocket.SendMessageAsync("{ \"type\": \"subscribe\", \"id\": \"\" }", ct); // assert - await webSocket.ReceiveServerMessageAsync(ct); + await WaitForServerClose(webSocket, ct); Assert.True(webSocket.CloseStatus.HasValue); Assert.Equal(CloseReasons.ProtocolError, (int)webSocket.CloseStatus.Value); }); @@ -469,7 +468,7 @@ public Task Send_Subscribe_Empty_Id() [Fact] public async Task Send_Subscribe_Complete() { - await TryTest( + await RunTest( async ct => { // arrange @@ -513,6 +512,7 @@ await testServer.SendPostRequestAsync( // act await webSocket.SendCompleteAsync(subscriptionId, ct); + await diagnostics.WaitForUnsubscribeAsync(ct); await testServer.SendPostRequestAsync( new ClientQueryRequest @@ -532,7 +532,6 @@ await testServer.SendPostRequestAsync( // assert Assert.True(await AssertNoMessage(webSocket, Messages.Next, ct)); - await diagnostics.WaitForUnsubscribeAsync(ct); await diagnostics.WaitForCloseAsync(ct); }); } @@ -540,7 +539,7 @@ await testServer.SendPostRequestAsync( [Fact] public async Task Send_Subscribe_Complete_From_Server() { - await TryTest( + await RunTest( async ct => { // arrange @@ -602,7 +601,7 @@ await testServer.SendPostRequestAsync( [Fact] public async Task Send_Subscribe_100x_Complete_From_Server() { - await TryTest( + await RunTest( async ct => { // arrange @@ -674,7 +673,7 @@ public Task Send_Subscribe_SyntaxError() { var snapshot = new Snapshot(); - return TryTest( + return RunTest( async ct => { // arrange @@ -701,7 +700,7 @@ public Task Send_Subscribe_ValidationError() { var snapshot = new Snapshot(); - return TryTest( + return RunTest( async ct => { // arrange @@ -725,7 +724,7 @@ public Task Send_Subscribe_ValidationError() [Fact] public Task Subscribe_With_Error_Should_Not_Send_Complete_After_Error() - => TryTest( + => RunTest( async ct => { // arrange @@ -754,7 +753,7 @@ await AssertNoMessage(webSocket, Messages.Complete, ct), [Fact] public Task Connection_Init_Received_In_Time_Should_Not_Timeout_When_OnConnect_Is_Slow() - => TryTest( + => RunTest( async ct => { // arrange @@ -794,7 +793,7 @@ public Task Send_Ping() { var snapshot = new Snapshot(); - return TryTest( + return RunTest( async ct => { // arrange @@ -821,7 +820,7 @@ public Task Send_Ping_With_Payload() { var snapshot = new Snapshot(); - return TryTest( + return RunTest( async ct => { // arrange @@ -847,7 +846,7 @@ await webSocket.SendPingAsync( [Fact] public Task Send_Pong() - => TryTest( + => RunTest( async ct => { // arrange @@ -872,7 +871,7 @@ public Task Send_Pong_With_Payload() { var snapshot = new Snapshot(); - return TryTest( + return RunTest( async ct => { // arrange @@ -898,7 +897,7 @@ await webSocket.SendPongAsync( [Fact] public Task Send_Invalid_Message_String() - => TryTest( + => RunTest( async ct => { // arrange @@ -910,14 +909,14 @@ public Task Send_Invalid_Message_String() await webSocket.SendMessageAsync("hello", ct); // assert - await webSocket.ReceiveServerMessageAsync(ct); + await WaitForServerClose(webSocket, ct); Assert.True(webSocket.CloseStatus.HasValue, "Connection is closed."); Assert.Equal(CloseReasons.ProtocolError, (int)webSocket.CloseStatus.Value); }); [Fact] public Task Malformed_Message_Should_Close_With_BadRequest() - => TryTest( + => RunTest( async ct => { // arrange @@ -929,14 +928,14 @@ public Task Malformed_Message_Should_Close_With_BadRequest() await webSocket.SendMessageAsync("{ \"type\": \"connection_init\"", ct); // assert - await webSocket.ReceiveServerMessageAsync(ct); + await WaitForServerClose(webSocket, ct); Assert.True(webSocket.CloseStatus.HasValue, "Connection is closed."); Assert.Equal(CloseReasons.ProtocolError, (int)webSocket.CloseStatus.Value); }); [Fact] public Task Send_Invalid_Message_No_Type() - => TryTest( + => RunTest( async ct => { // arrange @@ -948,14 +947,14 @@ public Task Send_Invalid_Message_No_Type() await webSocket.SendMessageAsync("{ }", ct); // assert - await webSocket.ReceiveServerMessageAsync(ct); + await WaitForServerClose(webSocket, ct); Assert.True(webSocket.CloseStatus.HasValue, "Connection is closed."); Assert.Equal(CloseReasons.ProtocolError, (int)webSocket.CloseStatus.Value); }); [Fact] public Task Send_Invalid_Message_Invalid_Type() - => TryTest( + => RunTest( async ct => { // arrange @@ -967,14 +966,14 @@ public Task Send_Invalid_Message_Invalid_Type() await webSocket.SendMessageAsync("{ \"type\": \"abc\" }", ct); // assert - await webSocket.ReceiveServerMessageAsync(ct); + await WaitForServerClose(webSocket, ct); Assert.True(webSocket.CloseStatus.HasValue, "Connection is closed."); Assert.Equal(CloseReasons.ProtocolError, (int)webSocket.CloseStatus.Value); }); [Fact] public Task Send_Invalid_Message_Not_An_Object() - => TryTest( + => RunTest( async ct => { // arrange @@ -986,14 +985,14 @@ public Task Send_Invalid_Message_Not_An_Object() await webSocket.SendMessageAsync("[]", ct); // assert - await webSocket.ReceiveServerMessageAsync(ct); + await WaitForServerClose(webSocket, ct); Assert.True(webSocket.CloseStatus.HasValue, "Connection is closed."); Assert.Equal(CloseReasons.ProtocolError, (int)webSocket.CloseStatus.Value); }); [Fact] public Task Client_Close_Should_Receive_Server_Close_Frame() - => TryTest( + => RunTest( async ct => { // arrange @@ -1016,7 +1015,7 @@ public Task Client_Close_Should_Receive_Server_Close_Frame() [Fact] public Task Subscribe_Cancel() { - return TryTest( + return RunTest( async ct => { // arrange @@ -1051,7 +1050,7 @@ public Task Subscribe_Cancel() [Fact] public Task Subscribe_ReceiveDataOnMutation_StripNull() - => TryTest( + => RunTest( async ct => { // arrange @@ -1218,109 +1217,4 @@ public override ValueTask OnPongAsync( return base.OnPongAsync(session, pongMessage, cancellationToken); } } - - public sealed class SubscriptionTestDiagnostics : SubscriptionDiagnosticEventsListener - { - private readonly object _sync = new(); - private readonly List<(int Count, TaskCompletionSource Signal)> _subscribedSignals = []; - private readonly List<(int Count, TaskCompletionSource Signal)> _unsubscribeSignals = []; - private readonly List<(int Count, TaskCompletionSource Signal)> _closeSignals = []; - private int _subscribed; - private int _unsubscribeCount; - private int _closeCount; - - public int Subscribed => _subscribed; - - public bool UnsubscribeInvoked { get; private set; } - - public bool CloseInvoked { get; private set; } - - public override void SubscribeSuccess(string topicName) - { - var subscribed = Interlocked.Increment(ref _subscribed); - SignalCompleted(_subscribedSignals, subscribed); - } - - public override void Unsubscribe(string topicName, int shard, int subscribers) - { - UnsubscribeInvoked = true; - var unsubscribeCount = Interlocked.Increment(ref _unsubscribeCount); - SignalCompleted(_unsubscribeSignals, unsubscribeCount); - } - - public override void Close(string topicName) - { - CloseInvoked = true; - var closeCount = Interlocked.Increment(ref _closeCount); - SignalCompleted(_closeSignals, closeCount); - } - - public Task WaitForSubscribedAsync(CancellationToken cancellationToken) - => WaitForSubscribedAsync(1, cancellationToken); - - public Task WaitForSubscribedAsync(int count, CancellationToken cancellationToken) - => WaitForSignalAsync(_subscribedSignals, count, _subscribed, cancellationToken); - - public Task WaitForUnsubscribeAsync(CancellationToken cancellationToken) - => WaitForUnsubscribeAsync(1, cancellationToken); - - public Task WaitForUnsubscribeAsync(int count, CancellationToken cancellationToken) - => WaitForSignalAsync(_unsubscribeSignals, count, _unsubscribeCount, cancellationToken); - - public Task WaitForCloseAsync(CancellationToken cancellationToken) - => WaitForCloseAsync(1, cancellationToken); - - public Task WaitForCloseAsync(int count, CancellationToken cancellationToken) - => WaitForSignalAsync(_closeSignals, count, _closeCount, cancellationToken); - - private Task WaitForSignalAsync( - List<(int Count, TaskCompletionSource Signal)> signals, - int count, - int currentCount, - CancellationToken cancellationToken) - { - if (currentCount >= count) - { - return Task.CompletedTask; - } - - TaskCompletionSource signal; - - lock (_sync) - { - if (GetCurrentCount(signals) >= count) - { - return Task.CompletedTask; - } - - signal = new(TaskCreationOptions.RunContinuationsAsynchronously); - signals.Add((count, signal)); - } - - return signal.Task.WaitAsync(cancellationToken); - } - - private int GetCurrentCount(List<(int Count, TaskCompletionSource Signal)> signals) - => ReferenceEquals(signals, _subscribedSignals) - ? _subscribed - : ReferenceEquals(signals, _unsubscribeSignals) - ? _unsubscribeCount - : _closeCount; - - private void SignalCompleted(List<(int Count, TaskCompletionSource Signal)> signals, int count) - { - TaskCompletionSource[] ready; - - lock (_sync) - { - ready = [.. signals.Where(t => t.Count <= count).Select(t => t.Signal)]; - signals.RemoveAll(t => t.Count <= count); - } - - foreach (var signal in ready) - { - signal.TrySetResult(); - } - } - } } diff --git a/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/Subscriptions/SubscriptionTestDiagnostics.cs b/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/Subscriptions/SubscriptionTestDiagnostics.cs new file mode 100644 index 00000000000..b473d0b2feb --- /dev/null +++ b/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/Subscriptions/SubscriptionTestDiagnostics.cs @@ -0,0 +1,108 @@ +using HotChocolate.Subscriptions.Diagnostics; + +namespace HotChocolate.AspNetCore.Subscriptions; + +public sealed class SubscriptionTestDiagnostics : SubscriptionDiagnosticEventsListener +{ + private readonly object _sync = new(); + private readonly List<(int Count, TaskCompletionSource Signal)> _subscribedSignals = []; + private readonly List<(int Count, TaskCompletionSource Signal)> _unsubscribeSignals = []; + private readonly List<(int Count, TaskCompletionSource Signal)> _closeSignals = []; + private int _subscribed; + private int _unsubscribeCount; + private int _closeCount; + + public int Subscribed => _subscribed; + + public bool UnsubscribeInvoked { get; private set; } + + public bool CloseInvoked { get; private set; } + + public override void SubscribeSuccess(string topicName) + { + var subscribed = Interlocked.Increment(ref _subscribed); + SignalCompleted(_subscribedSignals, subscribed); + } + + public override void Unsubscribe(string topicName, int shard, int subscribers) + { + UnsubscribeInvoked = true; + var unsubscribeCount = Interlocked.Increment(ref _unsubscribeCount); + SignalCompleted(_unsubscribeSignals, unsubscribeCount); + } + + public override void Close(string topicName) + { + CloseInvoked = true; + var closeCount = Interlocked.Increment(ref _closeCount); + SignalCompleted(_closeSignals, closeCount); + } + + public Task WaitForSubscribedAsync(CancellationToken cancellationToken) + => WaitForSubscribedAsync(1, cancellationToken); + + public Task WaitForSubscribedAsync(int count, CancellationToken cancellationToken) + => WaitForSignalAsync(_subscribedSignals, count, _subscribed, cancellationToken); + + public Task WaitForUnsubscribeAsync(CancellationToken cancellationToken) + => WaitForUnsubscribeAsync(1, cancellationToken); + + public Task WaitForUnsubscribeAsync(int count, CancellationToken cancellationToken) + => WaitForSignalAsync(_unsubscribeSignals, count, _unsubscribeCount, cancellationToken); + + public Task WaitForCloseAsync(CancellationToken cancellationToken) + => WaitForCloseAsync(1, cancellationToken); + + public Task WaitForCloseAsync(int count, CancellationToken cancellationToken) + => WaitForSignalAsync(_closeSignals, count, _closeCount, cancellationToken); + + private Task WaitForSignalAsync( + List<(int Count, TaskCompletionSource Signal)> signals, + int count, + int currentCount, + CancellationToken cancellationToken) + { + if (currentCount >= count) + { + return Task.CompletedTask; + } + + TaskCompletionSource signal; + + lock (_sync) + { + if (GetCurrentCount(signals) >= count) + { + return Task.CompletedTask; + } + + signal = new(TaskCreationOptions.RunContinuationsAsynchronously); + signals.Add((count, signal)); + } + + return signal.Task.WaitAsync(cancellationToken); + } + + private int GetCurrentCount(List<(int Count, TaskCompletionSource Signal)> signals) + => ReferenceEquals(signals, _subscribedSignals) + ? _subscribed + : ReferenceEquals(signals, _unsubscribeSignals) + ? _unsubscribeCount + : _closeCount; + + private void SignalCompleted(List<(int Count, TaskCompletionSource Signal)> signals, int count) + { + TaskCompletionSource[] ready; + + lock (_sync) + { + ready = [.. signals.Where(t => t.Count <= count).Select(t => t.Signal)]; + signals.RemoveAll(t => t.Count <= count); + } + + foreach (var signal in ready) + { + signal.TrySetResult(); + } + } +} diff --git a/src/HotChocolate/AspNetCore/test/Transport.Sockets.Client.Tests/GraphQLOverWebSocket/WebSocketClientBatchTests.cs b/src/HotChocolate/AspNetCore/test/Transport.Sockets.Client.Tests/GraphQLOverWebSocket/WebSocketClientBatchTests.cs index 71c7d193b5b..a5934addc66 100644 --- a/src/HotChocolate/AspNetCore/test/Transport.Sockets.Client.Tests/GraphQLOverWebSocket/WebSocketClientBatchTests.cs +++ b/src/HotChocolate/AspNetCore/test/Transport.Sockets.Client.Tests/GraphQLOverWebSocket/WebSocketClientBatchTests.cs @@ -10,7 +10,7 @@ public class WebSocketClientBatchTests(TestServerFactory serverFactory, ITestOut { [Fact] public Task ExecuteBatch_ReceivesAllResults() - => TryTest(async ct => + => RunTest(async ct => { // arrange using var testServer = CreateStarWarsServer(output: output); @@ -44,7 +44,7 @@ public Task ExecuteBatch_ReceivesAllResults() [Fact] public Task ExecuteBatch_SingleRequest_WorksLikeSingleExecute() - => TryTest(async ct => + => RunTest(async ct => { // arrange using var testServer = CreateStarWarsServer(output: output); @@ -75,7 +75,7 @@ public Task ExecuteBatch_SingleRequest_WorksLikeSingleExecute() [Fact] public Task ExecuteBatch_Should_Throw_When_Socket_Aborted_Without_Close_Frame() { - return TryTest(async ct => + return RunTest(async ct => { // arrange var subscriptionRequest = new OperationBatchRequest( diff --git a/src/HotChocolate/AspNetCore/test/Transport.Sockets.Client.Tests/GraphQLOverWebSocket/WebSocketClientProtocolTests.cs b/src/HotChocolate/AspNetCore/test/Transport.Sockets.Client.Tests/GraphQLOverWebSocket/WebSocketClientProtocolTests.cs index 2f24ca3ce08..27c16d1888d 100644 --- a/src/HotChocolate/AspNetCore/test/Transport.Sockets.Client.Tests/GraphQLOverWebSocket/WebSocketClientProtocolTests.cs +++ b/src/HotChocolate/AspNetCore/test/Transport.Sockets.Client.Tests/GraphQLOverWebSocket/WebSocketClientProtocolTests.cs @@ -94,7 +94,7 @@ public Task Subscribe_ReceiveDataOnMutation() [Fact] public Task Subscribe_Should_Throw_When_Socket_Aborted_Without_Close_Frame() { - return TryTest(async ct => + return RunTest(async ct => { // arrange var subscriptionRequest = new OperationRequest( @@ -129,7 +129,7 @@ async Task ReadResults() [Fact] public Task ReadResultsAsync_Should_Complete_When_Execute_Token_Is_Canceled() { - return TryTest(async ct => + return RunTest(async ct => { // arrange var subscriptionRequest = new OperationRequest( @@ -169,7 +169,7 @@ public Task ReadResultsAsync_Should_Complete_When_Execute_Token_Is_Canceled() [Fact] public Task ReadResultsAsync_Should_Throw_SocketClosedException_When_Server_Closes_Connection() { - return TryTest(async ct => + return RunTest(async ct => { // arrange var subscriptionRequest = new OperationRequest( @@ -199,7 +199,7 @@ async Task ReadResults() [Fact] public Task ReadResultsAsync_Should_Throw_SocketClosedException_When_Server_Closes_Connection_After_Next_Messages() { - return TryTest(async ct => + return RunTest(async ct => { // arrange var subscriptionRequest = new OperationRequest( @@ -232,7 +232,7 @@ async Task ReadResults() [Fact] public Task ReadResultsAsync_Should_Throw_SocketClosedException_When_Connection_Is_Lost_Without_Close_Frame() { - return TryTest(async ct => + return RunTest(async ct => { // arrange var subscriptionRequest = new OperationRequest( @@ -276,7 +276,7 @@ public Task ReadResultsAsync_Should_Throw_SocketClosedException_When_Connection_ [Fact] public Task DisposeAsync_Should_Complete_Stream_Cleanly() { - return TryTest(async ct => + return RunTest(async ct => { // arrange var subscriptionRequest = new OperationRequest( @@ -318,7 +318,7 @@ public Task DisposeAsync_Should_Complete_Stream_Cleanly() [Fact] public Task ReadResultsAsync_Should_Throw_SocketClosedException_When_Server_Sends_Structurally_Invalid_Message() { - return TryTest(async ct => + return RunTest(async ct => { // arrange var subscriptionRequest = new OperationRequest( @@ -352,7 +352,7 @@ async Task ReadResults() [Fact] public Task ReadResultsAsync_Should_Throw_SocketClosedException_When_Server_Sends_Unknown_Message_Type() { - return TryTest(async ct => + return RunTest(async ct => { // arrange var subscriptionRequest = new OperationRequest( @@ -386,7 +386,7 @@ async Task ReadResults() [Fact] public Task ReadResultsAsync_Should_Yield_Result_When_Next_Message_Type_Follows_Payload() { - return TryTest(async ct => + return RunTest(async ct => { // arrange var subscriptionRequest = new OperationRequest( @@ -430,7 +430,7 @@ public Task Send_Subscribe_SyntaxError() { var snapshot = new Snapshot(); - return TryTest(async ct => + return RunTest(async ct => { // arrange var subscriptionRequest = new OperationRequest( @@ -462,7 +462,7 @@ public Task Send_Subscribe_ValidationError() { var snapshot = new Snapshot(); - return TryTest(async ct => + return RunTest(async ct => { // arrange var subscriptionRequest = new OperationRequest( @@ -491,7 +491,7 @@ public Task Send_Subscribe_ValidationError() [Fact] public Task Send_Connect_With_Auth_Accept() - => TryTest(async ct => + => RunTest(async ct => { // arrange var interceptor = new AuthInterceptor(); @@ -515,7 +515,7 @@ await SocketClient.ConnectAsync( [Fact] public Task Send_Connect_With_Auth_Reject() - => TryTest(async ct => + => RunTest(async ct => { // arrange var interceptor = new AuthInterceptor(); @@ -538,7 +538,7 @@ public Task Send_Connect_With_Auth_Reject() [Fact] public Task ConnectAsync_Should_TearDown_Socket_When_Handshake_Is_Canceled() { - return TryTest(async ct => + return RunTest(async ct => { // arrange // the server accepts the socket but never sends a connection_ack, so the client's diff --git a/src/HotChocolate/Caching/test/Caching.Tests/TestServerFactory.cs b/src/HotChocolate/Caching/test/Caching.Tests/TestServerFactory.cs index 9f2cc5e598a..2fb3b2d2956 100644 --- a/src/HotChocolate/Caching/test/Caching.Tests/TestServerFactory.cs +++ b/src/HotChocolate/Caching/test/Caching.Tests/TestServerFactory.cs @@ -1,32 +1,36 @@ using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.TestHost; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; namespace HotChocolate.Caching.Http.Tests; -public class TestServerFactory : IDisposable +public class TestServerFactory : IAsyncDisposable { - private readonly List _instances = []; + private readonly List _instances = []; public TestServer Create( Action configureServices, Action configureApplication) { - var builder = new WebHostBuilder() - .Configure(configureApplication) - .ConfigureServices(configureServices); + var builder = WebApplication.CreateEmptyBuilder(new WebApplicationOptions()); + builder.WebHost.UseTestServer(); + configureServices(builder.Services); - var server = new TestServer(builder); - _instances.Add(server); - return server; + var app = builder.Build(); + configureApplication(app); + + app.Start(); + _instances.Add(app); + return app.GetTestServer(); } - public void Dispose() + public async ValueTask DisposeAsync() { - foreach (var testServer in _instances) + foreach (var app in _instances) { - testServer.Dispose(); + await app.StopAsync(); + await app.DisposeAsync(); } } } diff --git a/src/HotChocolate/Data/test/Data.Sorting.InMemory.Tests/QueryableSortVisitorVariablesTests.cs b/src/HotChocolate/Data/test/Data.Sorting.InMemory.Tests/QueryableSortVisitorVariablesTests.cs index 3b1a3b2e178..40a114ec01a 100644 --- a/src/HotChocolate/Data/test/Data.Sorting.InMemory.Tests/QueryableSortVisitorVariablesTests.cs +++ b/src/HotChocolate/Data/test/Data.Sorting.InMemory.Tests/QueryableSortVisitorVariablesTests.cs @@ -1,8 +1,8 @@ using HotChocolate.AspNetCore.Tests.Utilities; using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.TestHost; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; using HotChocolate.Execution; using HotChocolate.Types; @@ -221,33 +221,32 @@ private TestServer CreateServer(TEntity?[] entities) where TEntity : class where T : SortInputType { - var builder = new WebHostBuilder() - .ConfigureServices( - (_, services) => + var builder = WebApplication.CreateSlimBuilder(); + builder.WebHost.UseTestServer(); + builder.Services.AddRouting(); + builder.Services.AddGraphQLServer() + .ModifyCostOptions(o => o.DefaultListSize = 1) + .AddSorting() + .AddQueryType( + c => { - services.AddRouting(); - services.AddGraphQLServer() - .ModifyCostOptions(o => o.DefaultListSize = 1) - .AddSorting() - .AddQueryType( - c => - { - c - .Name("Query") - .Field("root") - .Resolve(entities) - .UseSorting(); - - c - .Name("Query") - .Field("rootExecutable") - .Resolve(entities.AsExecutable()) - .UseSorting(); - }) - .BuildRequestExecutorAsync(); + c + .Name("Query") + .Field("root") + .Resolve(entities) + .UseSorting(); + + c + .Name("Query") + .Field("rootExecutable") + .Resolve(entities.AsExecutable()) + .UseSorting(); }) - .Configure(x => x.UseRouting().UseEndpoints(c => c.MapGraphQL())); - return new TestServer(builder); + .BuildRequestExecutorAsync(); + var app = builder.Build(); + app.MapGraphQL(); + app.Start(); + return app.GetTestServer(); } private ValueTask CreateSchema(TEntity?[] entities) diff --git a/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/McpAdapterActivityTests.cs b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/McpAdapterActivityTests.cs index b0407fc13d8..6d7f44ed072 100644 --- a/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/McpAdapterActivityTests.cs +++ b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/McpAdapterActivityTests.cs @@ -4,9 +4,9 @@ using HotChocolate.Language; using HotChocolate.Resolvers; using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.TestHost; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; using ModelContextProtocol.Client; using Newtonsoft.Json; using Newtonsoft.Json.Converters; @@ -23,8 +23,8 @@ public partial class McpAdapterActivityTests public async Task Mcp_CallTool() { // arrange - using var server = CreateServer("query GetBook { book { title } }"); - var mcpClient = await CreateMcpClientAsync(server.CreateClient()); + await using var app = StartApp("query GetBook { book { title } }"); + var mcpClient = await CreateMcpClientAsync(app.GetTestClient()); await mcpClient.ListToolsAsync(cancellationToken: TestContext.Current.CancellationToken); using (CaptureActivities(out var activities)) @@ -41,8 +41,8 @@ public async Task Mcp_CallTool() public async Task Mcp_CallTool_Field_Does_Not_Exist() { // arrange - using var server = CreateServer("query InvalidGraphqlQuery { doesNotExist }"); - var mcpClient = await CreateMcpClientAsync(server.CreateClient()); + await using var app = StartApp("query InvalidGraphqlQuery { doesNotExist }"); + var mcpClient = await CreateMcpClientAsync(app.GetTestClient()); await mcpClient.ListToolsAsync(cancellationToken: TestContext.Current.CancellationToken); using (CaptureActivities(out var activities)) @@ -61,8 +61,8 @@ await mcpClient.CallToolAsync( public async Task Mcp_CallTool_Tool_Does_Not_Exist() { // arrange - using var server = CreateServer("query GetBook { book { title } }"); - var mcpClient = await CreateMcpClientAsync(server.CreateClient()); + await using var app = StartApp("query GetBook { book { title } }"); + var mcpClient = await CreateMcpClientAsync(app.GetTestClient()); await mcpClient.ListToolsAsync(cancellationToken: TestContext.Current.CancellationToken); using (CaptureActivities(out var activities)) @@ -79,8 +79,8 @@ public async Task Mcp_CallTool_Tool_Does_Not_Exist() public async Task Mcp_CallTool_GraphQL_Field_Error() { // arrange - using var server = CreateServer("query GetFaultyBook { faultyBook { title } }"); - var mcpClient = await CreateMcpClientAsync(server.CreateClient()); + await using var app = StartApp("query GetFaultyBook { faultyBook { title } }"); + var mcpClient = await CreateMcpClientAsync(app.GetTestClient()); await mcpClient.ListToolsAsync(cancellationToken: TestContext.Current.CancellationToken); using (CaptureActivities(out var activities)) @@ -97,8 +97,8 @@ public async Task Mcp_CallTool_GraphQL_Field_Error() public async Task Mcp_GetPrompt() { // arrange - using var server = CreateServer(prompt: CreateGreetingPrompt()); - var mcpClient = await CreateMcpClientAsync(server.CreateClient()); + await using var app = StartApp(prompt: CreateGreetingPrompt()); + var mcpClient = await CreateMcpClientAsync(app.GetTestClient()); await mcpClient.ListPromptsAsync(cancellationToken: TestContext.Current.CancellationToken); using (CaptureActivities(out var activities)) @@ -159,28 +159,25 @@ private static PromptDefinition CreateGreetingPrompt() ] }; - private static TestServer CreateServer(string? toolDocument = null, PromptDefinition? prompt = null) + private static WebApplication StartApp(string? toolDocument = null, PromptDefinition? prompt = null) { var storage = new InMemoryMcpStorage(toolDocument, prompt); - var builder = new WebHostBuilder() - .ConfigureServices(services => - { - services.AddRouting(); - services - .AddGraphQLServer() - .AddInstrumentation() - .AddMcp() - .AddMcpStorage(storage) - .AddQueryType(); - }) - .Configure(app => - { - app.UseRouting(); - app.UseEndpoints(endpoints => endpoints.MapGraphQLMcp()); - }); - - return new TestServer(builder); + var builder = WebApplication.CreateSlimBuilder(); + builder.WebHost.UseTestServer(); + builder.Services.AddRouting(); + builder.Services + .AddGraphQLServer() + .AddInstrumentation() + .AddMcp() + .AddMcpStorage(storage) + .AddQueryType(); + + var app = builder.Build(); + app.MapGraphQLMcp(); + app.Start(); + + return app; } private static async Task CreateMcpClientAsync(HttpClient httpClient) diff --git a/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/OpenApiAdapterActivityTests.cs b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/OpenApiAdapterActivityTests.cs index 3aac96089d0..d43ae14377f 100644 --- a/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/OpenApiAdapterActivityTests.cs +++ b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/OpenApiAdapterActivityTests.cs @@ -4,9 +4,9 @@ using HotChocolate.Language; using HotChocolate.Resolvers; using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.TestHost; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; using static CookieCrumble.TestEnvironment; using static HotChocolate.Diagnostics.ActivityTestHelper; @@ -21,7 +21,7 @@ public async Task Http_Get_OpenApi() using (CaptureActivities(out var activities)) { // arrange - using var server = CreateServer( + await using var app = StartApp( """ query GetBook @http(method: GET, route: "/book") { book { @@ -29,7 +29,7 @@ query GetBook @http(method: GET, route: "/book") { } } """); - using var client = server.CreateClient(); + using var client = app.GetTestClient(); // act using var response = await client.GetAsync("/book", TestContext.Current.CancellationToken); @@ -46,13 +46,13 @@ public async Task Http_Get_OpenApi_Field_Does_Not_Exist() using (CaptureActivities(out var activities)) { // arrange - using var server = CreateServer( + await using var app = StartApp( """ query GetMissing @http(method: GET, route: "/invalid-graphql-query") { doesNotExist } """); - using var client = server.CreateClient(); + using var client = app.GetTestClient(); // act using var response = await client.GetAsync("/invalid-graphql-query", TestContext.Current.CancellationToken); @@ -69,7 +69,7 @@ public async Task Http_Get_OpenApi_Endpoint_Does_Not_Exist() using (CaptureActivities(out var activities)) { // arrange - using var server = CreateServer( + await using var app = StartApp( """ query GetBook @http(method: GET, route: "/book") { book { @@ -77,7 +77,7 @@ query GetBook @http(method: GET, route: "/book") { } } """); - using var client = server.CreateClient(); + using var client = app.GetTestClient(); // act using var response = await client.GetAsync("/does-not-exist", TestContext.Current.CancellationToken); @@ -95,7 +95,7 @@ public async Task Http_Get_OpenApi_GraphQL_Field_Error() using (CaptureActivities(out var activities)) { // arrange - using var server = CreateServer( + await using var app = StartApp( """ query GetFaultyBook @http(method: GET, route: "/faulty-book") { faultyBook { @@ -103,7 +103,7 @@ query GetFaultyBook @http(method: GET, route: "/faulty-book") { } } """); - using var client = server.CreateClient(); + using var client = app.GetTestClient(); // act using var response = await client.GetAsync("/faulty-book", TestContext.Current.CancellationToken); @@ -114,28 +114,25 @@ query GetFaultyBook @http(method: GET, route: "/faulty-book") { } } - private static TestServer CreateServer(params string[] documents) + private static WebApplication StartApp(params string[] documents) { var storage = new InMemoryOpenApiDefinitionStorage(documents); - var builder = new WebHostBuilder() - .ConfigureServices(services => - { - services.AddRouting(); - services - .AddGraphQLServer() - .AddInstrumentation() - .AddOpenApi() - .AddOpenApiDefinitionStorage(storage) - .AddQueryType(); - }) - .Configure(app => - { - app.UseRouting(); - app.UseEndpoints(endpoints => endpoints.MapOpenApiEndpoints()); - }); - - return new TestServer(builder); + var builder = WebApplication.CreateSlimBuilder(); + builder.WebHost.UseTestServer(); + builder.Services.AddRouting(); + builder.Services + .AddGraphQLServer() + .AddInstrumentation() + .AddOpenApi() + .AddOpenApiDefinitionStorage(storage) + .AddQueryType(); + + var app = builder.Build(); + app.MapOpenApiEndpoints(); + app.Start(); + + return app; } public class Query diff --git a/src/HotChocolate/Fusion/test/Fusion.AspNetCore.Tests/FusionTestBase.cs b/src/HotChocolate/Fusion/test/Fusion.AspNetCore.Tests/FusionTestBase.cs index 8df479647b3..24a8657716e 100644 --- a/src/HotChocolate/Fusion/test/Fusion.AspNetCore.Tests/FusionTestBase.cs +++ b/src/HotChocolate/Fusion/test/Fusion.AspNetCore.Tests/FusionTestBase.cs @@ -28,7 +28,7 @@ namespace HotChocolate.Fusion; -public abstract partial class FusionTestBase : IDisposable +public abstract partial class FusionTestBase : IAsyncDisposable { private readonly TestServerSession _testServerSession = new(); private bool _disposed; @@ -265,7 +265,7 @@ SourceSchemaInteraction GetSourceSchemaInteraction(OperationPlanContext context, } } - public void Dispose() + public async ValueTask DisposeAsync() { if (_disposed) { @@ -273,18 +273,10 @@ public void Dispose() } _disposed = true; - Dispose(true); + await _testServerSession.DisposeAsync(); GC.SuppressFinalize(this); } - protected virtual void Dispose(bool disposing) - { - if (disposing) - { - _testServerSession.Dispose(); - } - } - protected class Gateway : IDisposable { internal Gateway( diff --git a/src/HotChocolate/Fusion/test/Fusion.AspNetCore.Tests/SpecOnlyCompositionGatewayTests.cs b/src/HotChocolate/Fusion/test/Fusion.AspNetCore.Tests/SpecOnlyCompositionGatewayTests.cs index eb2340b5d6a..1066b1482bf 100644 --- a/src/HotChocolate/Fusion/test/Fusion.AspNetCore.Tests/SpecOnlyCompositionGatewayTests.cs +++ b/src/HotChocolate/Fusion/test/Fusion.AspNetCore.Tests/SpecOnlyCompositionGatewayTests.cs @@ -53,7 +53,7 @@ public async Task SpecOnlySources_Should_LoadAndPriceFoldedDirectives_When_Gatew var compositeDocument = compositeSchema.ToSyntaxNode(); await using var executionSchema = FusionSchemaDefinition.Create(compositeDocument); - using var session = new TestServerSession(); + await using var session = new TestServerSession(); using var gateway = CreateGateway(session, compositeDocument); var request = new OperationRequest("{ items { value } }"); diff --git a/src/HotChocolate/Fusion/test/Fusion.AspNetCore.Tests/TestServerSession.cs b/src/HotChocolate/Fusion/test/Fusion.AspNetCore.Tests/TestServerSession.cs index 89794bd9fed..6a23cf7f480 100644 --- a/src/HotChocolate/Fusion/test/Fusion.AspNetCore.Tests/TestServerSession.cs +++ b/src/HotChocolate/Fusion/test/Fusion.AspNetCore.Tests/TestServerSession.cs @@ -1,14 +1,14 @@ using System.Threading.Channels; using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.TestHost; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; namespace HotChocolate.Fusion; -internal sealed class TestServerSession : IDisposable +internal sealed class TestServerSession : IAsyncDisposable { - private readonly Channel _cleanupPipeline = Channel.CreateUnbounded(); + private readonly Channel _cleanupPipeline = Channel.CreateUnbounded(); private bool _disposed; public TestServer CreateServer( @@ -16,28 +16,27 @@ public TestServer CreateServer( Action configureApplication, string? environmentName = null) { - var builder = new WebHostBuilder() - .Configure(configureApplication) - .ConfigureServices(configureServices); + var builder = WebApplication.CreateEmptyBuilder( + new WebApplicationOptions { EnvironmentName = environmentName }); + builder.WebHost.UseTestServer(); + configureServices(builder.Services); - if (environmentName is not null) - { - builder.UseEnvironment(environmentName); - } + var app = builder.Build(); + configureApplication(app); - var server = new TestServer(builder); + app.Start(); - if (!_cleanupPipeline.Writer.TryWrite(server)) + if (!_cleanupPipeline.Writer.TryWrite(app)) { - server.Dispose(); + ((IHost)app).Dispose(); throw new InvalidOperationException( "Failed to add test server to cleanup pipeline."); } - return server; + return app.GetTestServer(); } - public void Dispose() + public async ValueTask DisposeAsync() { if (_disposed) { @@ -48,9 +47,10 @@ public void Dispose() _cleanupPipeline.Writer.TryComplete(); - while (_cleanupPipeline.Reader.TryRead(out var server)) + while (_cleanupPipeline.Reader.TryRead(out var app)) { - server.Dispose(); + await app.StopAsync(); + await app.DisposeAsync(); } } } diff --git a/src/HotChocolate/Fusion/test/Fusion.Connectors.InMemory.Tests/FileUploadTests.cs b/src/HotChocolate/Fusion/test/Fusion.Connectors.InMemory.Tests/FileUploadTests.cs index 9e0ce9d60ea..c5904453e6c 100644 --- a/src/HotChocolate/Fusion/test/Fusion.Connectors.InMemory.Tests/FileUploadTests.cs +++ b/src/HotChocolate/Fusion/test/Fusion.Connectors.InMemory.Tests/FileUploadTests.cs @@ -5,23 +5,21 @@ using HotChocolate.Transport.Http; using HotChocolate.Types; using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.TestHost; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; using OperationRequest = HotChocolate.Transport.OperationRequest; namespace HotChocolate.Fusion; -public sealed class FileUploadTests : IDisposable +public sealed class FileUploadTests { - private TestServer? _server; - [Fact] public async Task Upload_Single_File() { // arrange - _server = await CreateServerAsync(); - using var client = GraphQLHttpClient.Create(_server.CreateClient()); + await using var app = StartApp(); + using var client = GraphQLHttpClient.Create(app.GetTestClient()); var stream = new MemoryStream("abc"u8.ToArray()); @@ -102,8 +100,8 @@ public async Task Upload_Single_File_Should_KeepText_When_TextSpansArenaSegments public async Task Upload_Single_File_In_Input_Object() { // arrange - _server = await CreateServerAsync(); - using var client = GraphQLHttpClient.Create(_server.CreateClient()); + await using var app = StartApp(); + using var client = GraphQLHttpClient.Create(app.GetTestClient()); var stream = new MemoryStream("abc"u8.ToArray()); @@ -143,8 +141,8 @@ public async Task Upload_Single_File_In_Input_Object() public async Task Upload_Single_File_In_Input_Object_Inline() { // arrange - _server = await CreateServerAsync(); - using var client = GraphQLHttpClient.Create(_server.CreateClient()); + await using var app = StartApp(); + using var client = GraphQLHttpClient.Create(app.GetTestClient()); var stream = new MemoryStream("abc"u8.ToArray()); @@ -181,8 +179,8 @@ public async Task Upload_Single_File_In_Input_Object_Inline() public async Task Upload_List_Of_Files() { // arrange - _server = await CreateServerAsync(); - using var client = GraphQLHttpClient.Create(_server.CreateClient()); + await using var app = StartApp(); + using var client = GraphQLHttpClient.Create(app.GetTestClient()); var stream1 = new MemoryStream("abc"u8.ToArray()); var stream2 = new MemoryStream("def"u8.ToArray()); @@ -224,8 +222,8 @@ public async Task Upload_List_Of_Files() public async Task Upload_List_Of_Files_In_Input_Object() { // arrange - _server = await CreateServerAsync(); - using var client = GraphQLHttpClient.Create(_server.CreateClient()); + await using var app = StartApp(); + using var client = GraphQLHttpClient.Create(app.GetTestClient()); var stream1 = new MemoryStream("abc"u8.ToArray()); var stream2 = new MemoryStream("abc"u8.ToArray()); @@ -270,8 +268,8 @@ public async Task Upload_List_Of_Files_In_Input_Object() public async Task Upload_List_Of_Files_In_Input_Object_Inline() { // arrange - _server = await CreateServerAsync(); - using var client = GraphQLHttpClient.Create(_server.CreateClient()); + await using var app = StartApp(); + using var client = GraphQLHttpClient.Create(app.GetTestClient()); var stream1 = new MemoryStream("abc"u8.ToArray()); var stream2 = new MemoryStream("def"u8.ToArray()); @@ -309,34 +307,28 @@ public async Task Upload_List_Of_Files_In_Input_Object_Inline() body.MatchMarkdownSnapshot(); } - private static Task CreateServerAsync() + private static WebApplication StartApp() { - var builder = new WebHostBuilder() - .ConfigureServices(services => - { - services.AddRouting(); - - services.AddGraphQLServer("uploads") - .AddQueryType() - .AddUploadType() - .ModifyCostOptions(o => o.DefaultListSize = 1) - .AddSourceSchemaDefaults(); - - services.AddGraphQLGatewayServer() - .AddInMemorySchema("uploads"); - }) - .Configure(app => - { - app.UseRouting(); - app.UseEndpoints(endpoints => endpoints.MapGraphQL()); - }); + var builder = WebApplication.CreateSlimBuilder(); + builder.WebHost.UseTestServer(); + builder.Services.AddRouting(); - return Task.FromResult(new TestServer(builder)); - } + builder.Services + .AddGraphQLServer("uploads") + .AddQueryType() + .AddUploadType() + .ModifyCostOptions(o => o.DefaultListSize = 1) + .AddSourceSchemaDefaults(); - public void Dispose() - { - _server?.Dispose(); + builder.Services + .AddGraphQLGatewayServer() + .AddInMemorySchema("uploads"); + + var app = builder.Build(); + app.MapGraphQL(); + app.Start(); + + return app; } private static class UploadSchema diff --git a/src/HotChocolate/Fusion/test/Fusion.Execution.Tests/FusionTestBase.cs b/src/HotChocolate/Fusion/test/Fusion.Execution.Tests/FusionTestBase.cs index bf0cb5349e0..f1bbaef4a31 100644 --- a/src/HotChocolate/Fusion/test/Fusion.Execution.Tests/FusionTestBase.cs +++ b/src/HotChocolate/Fusion/test/Fusion.Execution.Tests/FusionTestBase.cs @@ -19,7 +19,7 @@ namespace HotChocolate.Fusion; -public abstract class FusionTestBase : IDisposable +public abstract class FusionTestBase : IAsyncDisposable { private readonly TestServerSession _testServerSession = new(); private bool _disposed; @@ -545,7 +545,7 @@ protected static void MatchSnapshot( actual.MatchSnapshot(extension: ".yaml"); } - public void Dispose() + public async ValueTask DisposeAsync() { if (_disposed) { @@ -553,18 +553,10 @@ public void Dispose() } _disposed = true; - Dispose(true); + await _testServerSession.DisposeAsync(); GC.SuppressFinalize(this); } - protected virtual void Dispose(bool disposing) - { - if (disposing) - { - _testServerSession.Dispose(); - } - } - private static List CreateSourceSchemaTexts(IEnumerable schemas) { var sourceSchemas = new List(); diff --git a/src/HotChocolate/Fusion/test/Fusion.Execution.Tests/TestServerSession.cs b/src/HotChocolate/Fusion/test/Fusion.Execution.Tests/TestServerSession.cs index 4c17a23c69f..d9b32b2a1f2 100644 --- a/src/HotChocolate/Fusion/test/Fusion.Execution.Tests/TestServerSession.cs +++ b/src/HotChocolate/Fusion/test/Fusion.Execution.Tests/TestServerSession.cs @@ -1,37 +1,40 @@ using System.Threading.Channels; using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.TestHost; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; namespace HotChocolate.Fusion; -internal sealed class TestServerSession : IDisposable +internal sealed class TestServerSession : IAsyncDisposable { - private readonly Channel _cleanupPipeline = Channel.CreateUnbounded(); + private readonly Channel _cleanupPipeline = Channel.CreateUnbounded(); private bool _disposed; public TestServer CreateServer( Action configureServices, Action configureApplication) { - var builder = new WebHostBuilder() - .Configure(configureApplication) - .ConfigureServices(configureServices); + var builder = WebApplication.CreateEmptyBuilder(new WebApplicationOptions()); + builder.WebHost.UseTestServer(); + configureServices(builder.Services); - var server = new TestServer(builder); + var app = builder.Build(); + configureApplication(app); - if (!_cleanupPipeline.Writer.TryWrite(server)) + app.Start(); + + if (!_cleanupPipeline.Writer.TryWrite(app)) { - server.Dispose(); + ((IHost)app).Dispose(); throw new InvalidOperationException( "Failed to add test server to cleanup pipeline."); } - return server; + return app.GetTestServer(); } - public void Dispose() + public async ValueTask DisposeAsync() { if (_disposed) { @@ -42,9 +45,10 @@ public void Dispose() _cleanupPipeline.Writer.TryComplete(); - while (_cleanupPipeline.Reader.TryRead(out var server)) + while (_cleanupPipeline.Reader.TryRead(out var app)) { - server.Dispose(); + await app.StopAsync(); + await app.DisposeAsync(); } } } diff --git a/src/Nitro/CommandLine/src/CommandLine/Commands/Fusion/FusionRunCommand.cs b/src/Nitro/CommandLine/src/CommandLine/Commands/Fusion/FusionRunCommand.cs index de6f7c39aa3..d466c218e61 100644 --- a/src/Nitro/CommandLine/src/CommandLine/Commands/Fusion/FusionRunCommand.cs +++ b/src/Nitro/CommandLine/src/CommandLine/Commands/Fusion/FusionRunCommand.cs @@ -51,42 +51,40 @@ private static async Task ExecuteAsync( port ??= GetRandomUnusedPort(); - // Type or member is obsolete -#pragma warning disable ASPDEPR008 - var host = new WebHostBuilder() - .UseKestrel() - .UseUrls(new UriBuilder("http", "localhost", port.Value).ToString()) - .ConfigureServices(services => + var builder = WebApplication.CreateEmptyBuilder( + new WebApplicationOptions { - services - .AddCors() - .AddHeaderPropagation(c => - { - c.Headers.Add("GraphQL-Preflight"); - c.Headers.Add("Authorization"); - }); - - services - .AddHttpClient("fusion") - .AddHeaderPropagation(); - - services.AddRouting() - .AddGraphQLGatewayServer() - .AddFileSystemConfiguration(archiveFilePath) - .ModifyRequestOptions(o => o.CollectOperationPlanTelemetry = true) - .ModifyServerOptions(o => o.Tool.ServeMode = App.ServeMode.Insider); - }) - .Configure(app => + EnvironmentName = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") + }); + builder.WebHost.UseKestrelCore(); + builder.WebHost.UseUrls(new UriBuilder("http", "localhost", port.Value).ToString()); + + builder.Services + .AddCors() + .AddHeaderPropagation(c => { - app.UseRouting(); - app.UseHeaderPropagation(); - app.UseCors(c => c.AllowAnyHeader().AllowAnyMethod().AllowAnyOrigin()); - app.UseEndpoints(e => e.MapGraphQL()); - }) - .Build(); -#pragma warning restore ASPDEPR008 + c.Headers.Add("GraphQL-Preflight"); + c.Headers.Add("Authorization"); + }); - var lifetime = host.Services.GetRequiredService(); + builder.Services + .AddHttpClient("fusion") + .AddHeaderPropagation(); + + builder.Services + .AddRouting() + .AddGraphQLGatewayServer() + .AddFileSystemConfiguration(archiveFilePath) + .ModifyRequestOptions(o => o.CollectOperationPlanTelemetry = true) + .ModifyServerOptions(o => o.Tool.ServeMode = App.ServeMode.Insider); + + var app = builder.Build(); + app.UseRouting(); + app.UseHeaderPropagation(); + app.UseCors(c => c.AllowAnyHeader().AllowAnyMethod().AllowAnyOrigin()); + app.MapGraphQL(); + + var lifetime = app.Services.GetRequiredService(); lifetime.ApplicationStarted.Register(() => { @@ -119,7 +117,7 @@ private static async Task ExecuteAsync( } }); - await host.RunAsync(cancellationToken); + await app.RunAsync(cancellationToken); return ExitCodes.Success; } diff --git a/src/Nitro/CommandLine/src/CommandLine/Helpers/SystemBrowser.cs b/src/Nitro/CommandLine/src/CommandLine/Helpers/SystemBrowser.cs index 151e799caa6..fdb71275e19 100644 --- a/src/Nitro/CommandLine/src/CommandLine/Helpers/SystemBrowser.cs +++ b/src/Nitro/CommandLine/src/CommandLine/Helpers/SystemBrowser.cs @@ -7,6 +7,7 @@ using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Hosting; using static ChilliCream.Nitro.CommandLine.Helpers.OidcConfiguration; namespace ChilliCream.Nitro.CommandLine.Helpers; @@ -122,18 +123,18 @@ private static int GetRandomUnusedPort() { private const int DefaultTimeout = 60 * 10; // 10 mins (in seconds) - private readonly IWebHost _host; + private readonly WebApplication _app; private readonly TaskCompletionSource _source = new(); public LoopbackHttpListener(int port, string? path = null) { - _host = new WebHostBuilder() - .UseKestrel() - .UseUrls(new UriBuilder("http", "localhost", port, path).ToString()) - .Configure(app => app.Run(Execute)) - .Build(); + var builder = WebApplication.CreateEmptyBuilder(new WebApplicationOptions()); + builder.WebHost.UseKestrelCore(); + builder.WebHost.UseUrls(new UriBuilder("http", "localhost", port, path).ToString()); - _host.Start(); + _app = builder.Build(); + _app.Run(Execute); + _app.Start(); } public async ValueTask DisposeAsync() @@ -145,7 +146,8 @@ public async ValueTask DisposeAsync() } _source.Task.Dispose(); - _host.Dispose(); + await _app.StopAsync(); + await _app.DisposeAsync(); } private async Task Execute(HttpContext ctx) diff --git a/src/StrawberryShake/Client/test/Transport.WebSocket.Tests/IntegrationTests.cs b/src/StrawberryShake/Client/test/Transport.WebSocket.Tests/IntegrationTests.cs index a0adf2ca7c3..a8f7d6eb7e7 100644 --- a/src/StrawberryShake/Client/test/Transport.WebSocket.Tests/IntegrationTests.cs +++ b/src/StrawberryShake/Client/test/Transport.WebSocket.Tests/IntegrationTests.cs @@ -24,7 +24,7 @@ public async Task Simple_Request() .Create( async (snapshot, ct) => { - using var host = TestServerHelper.CreateServer( + await using var app = TestServerHelper.StartApp( x => x.AddTypeExtension(), out var port); @@ -60,7 +60,7 @@ public async Task Execution_Error() .Create( async (snapshot, ct) => { - using var host = TestServerHelper.CreateServer( + await using var app = TestServerHelper.StartApp( x => x.AddTypeExtension(), out var port); @@ -96,7 +96,7 @@ public async Task Validation_Error() .Create( async (snapshot, ct) => { - using var host = TestServerHelper.CreateServer( + await using var app = TestServerHelper.StartApp( x => x.AddTypeExtension(), out var port); @@ -134,7 +134,7 @@ public async Task Request_With_ConnectionPayload() { var payload = new Dictionary { ["Key"] = "Value" }; var sessionInterceptor = new StubSessionInterceptor(); - using var host = TestServerHelper.CreateServer( + await using var app = TestServerHelper.StartApp( builder => builder .AddTypeExtension() .AddSocketSessionInterceptor( @@ -180,8 +180,8 @@ public async Task Parallel_Request_SameSocket() async (snapshot, ct) => { // arrange - using var host = TestServerHelper - .CreateServer( + await using var app = TestServerHelper + .StartApp( x => x.AddTypeExtension(), out var port); diff --git a/src/StrawberryShake/Client/test/Transport.WebSocket.Tests/TestHelper/TestServerHelper.cs b/src/StrawberryShake/Client/test/Transport.WebSocket.Tests/TestHelper/TestServerHelper.cs index 7f58c3df65d..8921bedda9b 100644 --- a/src/StrawberryShake/Client/test/Transport.WebSocket.Tests/TestHelper/TestServerHelper.cs +++ b/src/StrawberryShake/Client/test/Transport.WebSocket.Tests/TestHelper/TestServerHelper.cs @@ -5,106 +5,99 @@ using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; -using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; namespace StrawberryShake.Transport.WebSockets; public static class TestServerHelper { - public static IWebHost CreateServer(Action configure, out int port) + public static WebApplication StartApp(Action configure, out int port) { for (port = 5500; port < 6000; port++) { try { - var configBuilder = new ConfigurationBuilder(); - configBuilder.AddInMemoryCollection(); - var config = configBuilder.Build(); - config["server.urls"] = $"http://localhost:{port}"; - var host = new WebHostBuilder() - .UseConfiguration(config) - .UseKestrel() - .ConfigureServices( - services => - { - var builder = services - .AddRouting() - .AddGraphQLServer() - .ModifyCostOptions(o => o.DefaultListSize = 1); - - configure(builder); + var builder = WebApplication.CreateEmptyBuilder(new WebApplicationOptions()); + builder.WebHost.UseKestrelCore(); + builder.WebHost.UseUrls($"http://localhost:{port}"); - builder - .AddStarWarsTypes() - .DisableIntrospection(disable: false) - .AddStarWarsRepositories() - .AddInMemorySubscriptions() - .ModifyOptions( - o => - { - o.EnableDefer = true; - o.EnableStream = true; - }) - .UseDefaultPipeline() - .UseRequest( - next => async context => - { - if (context.ContextData.TryGetValue( - nameof(HttpContext), - out var value) - && value is HttpContext httpContext - && context.Result is OperationResult result) - { - var headers = httpContext.Request.Headers; - if (headers.ContainsKey("sendErrorStatusCode")) - { - result.ContextData = - result.ContextData.SetItem( - ExecutionContextData.HttpStatusCode, - 403); - } + var gqlBuilder = builder.Services + .AddRouting() + .AddGraphQLServer() + .ModifyCostOptions(o => o.DefaultListSize = 1); - if (headers.ContainsKey("sendError")) - { - result.Errors = result.Errors.Add( - new Error { Message = "Some error!" }); - } - } + configure(gqlBuilder); - await next(context); - }); + gqlBuilder + .AddStarWarsTypes() + .DisableIntrospection(disable: false) + .AddStarWarsRepositories() + .AddInMemorySubscriptions() + .ModifyOptions( + o => + { + o.EnableDefer = true; + o.EnableStream = true; }) - .Configure( - app => - app.Use(async (ct, next) => + .UseDefaultPipeline() + .UseRequest( + next => async context => + { + if (context.ContextData.TryGetValue( + nameof(HttpContext), + out var value) + && value is HttpContext httpContext + && context.Result is OperationResult result) { - try + var headers = httpContext.Request.Headers; + if (headers.ContainsKey("sendErrorStatusCode")) { - // Kestrel does not return proper error responses: - // https://github.com/aspnet/KestrelHttpServer/issues/43 - await next(); + result.ContextData = + result.ContextData.SetItem( + ExecutionContextData.HttpStatusCode, + 403); } - catch (Exception ex) - { - if (ct.Response.HasStarted) - { - throw; - } - ct.Response.StatusCode = 500; - ct.Response.Headers.Clear(); - await ct.Response.WriteAsync(ex.ToString()); + if (headers.ContainsKey("sendError")) + { + result.Errors = result.Errors.Add( + new Error { Message = "Some error!" }); } - }) - .UseWebSockets() - .UseRouting() - .UseEndpoints(e => e.MapGraphQL())) - .Build(); + } + + await next(context); + }); + + var app = builder.Build(); + + app.Use(async (ct, next) => + { + try + { + // Kestrel does not return proper error responses: + // https://github.com/aspnet/KestrelHttpServer/issues/43 + await next(); + } + catch (Exception ex) + { + if (ct.Response.HasStarted) + { + throw; + } + + ct.Response.StatusCode = 500; + ct.Response.Headers.Clear(); + await ct.Response.WriteAsync(ex.ToString()); + } + }); + app.UseWebSockets(); + app.UseRouting(); + app.MapGraphQL(); - host.Start(); + app.Start(); - return host; + return app; } catch { diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/AnyScalarDefaultSerializationTest.cs b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/AnyScalarDefaultSerializationTest.cs index f81b11d920e..c903712952c 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/AnyScalarDefaultSerializationTest.cs +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/AnyScalarDefaultSerializationTest.cs @@ -17,7 +17,7 @@ public async Task Execute_AnyScalarDefaultSerialization_Test() { // arrange using var cts = new CancellationTokenSource(20_000); - using var host = TestServerHelper.CreateServer( + await using var app = TestServerHelper.StartApp( builder => builder.AddTypeExtension().AddJsonTypeConverter(), out var port); var serviceCollection = new ServiceCollection(); diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/LocalTypesTest.cs b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/LocalTypesTest.cs index 82d9ddbc87c..01e43a21297 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/LocalTypesTest.cs +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/LocalTypesTest.cs @@ -17,7 +17,7 @@ public async Task Execute_LocalTypes_Test() { // arrange var ct = new CancellationTokenSource(20_000).Token; - using var host = TestServerHelper.CreateServer( + await using var app = TestServerHelper.StartApp( builder => builder.AddTypeExtension(typeof(QueryType)), out var port); var serviceCollection = new ServiceCollection(); diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/MultiProfileTest.cs b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/MultiProfileTest.cs index bc749b664af..b8f6d08aa8d 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/MultiProfileTest.cs +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/MultiProfileTest.cs @@ -14,7 +14,7 @@ public MultiProfileTest(TestServerFactory serverFactory) : base(serverFactory) public void Execute_MultiProfile_Test() { // arrange - using var host = TestServerHelper.CreateServer( + using var app = TestServerHelper.StartApp( _ => { }, out var port); var serviceCollection = new ServiceCollection(); diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/StarWarsGetFriendsDeferInListTest.cs b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/StarWarsGetFriendsDeferInListTest.cs index 6879353ea6c..ee52fcb1433 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/StarWarsGetFriendsDeferInListTest.cs +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/StarWarsGetFriendsDeferInListTest.cs @@ -14,7 +14,7 @@ public async Task Execute_StarWarsGetFriendsDeferInList_Test() { // arrange CancellationToken ct = new CancellationTokenSource(20_000).Token; - using IWebHost host = TestServerHelper.CreateServer( + using var app = TestServerHelper.StartApp( _ => { }, out var port); var serviceCollection = new ServiceCollection(); diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/StarWarsGetFriendsDeferredTest.cs b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/StarWarsGetFriendsDeferredTest.cs index 8b48f7258f4..ebc548da258 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/StarWarsGetFriendsDeferredTest.cs +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/StarWarsGetFriendsDeferredTest.cs @@ -14,7 +14,7 @@ public async Task Execute_StarWarsGetFriendsDeferred_Test() { // arrange CancellationToken ct = new CancellationTokenSource(20_000).Token; - using IWebHost host = TestServerHelper.CreateServer( + using var app = TestServerHelper.StartApp( _ => { }, out var port); var serviceCollection = new ServiceCollection(); diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/StarWarsGetFriendsNoStoreTest.cs b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/StarWarsGetFriendsNoStoreTest.cs index b1e09dcbf6b..41596a355f7 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/StarWarsGetFriendsNoStoreTest.cs +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/StarWarsGetFriendsNoStoreTest.cs @@ -16,7 +16,7 @@ public async Task Execute_StarWarsGetFriendsNoStore_Test() { // arrange var ct = new CancellationTokenSource(20_000).Token; - using var host = TestServerHelper.CreateServer( + await using var app = TestServerHelper.StartApp( _ => { }, out var port); var serviceCollection = new ServiceCollection(); diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/StarWarsGetFriendsTest.cs b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/StarWarsGetFriendsTest.cs index 4cc5dc99000..3aea5d13a61 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/StarWarsGetFriendsTest.cs +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/StarWarsGetFriendsTest.cs @@ -15,7 +15,7 @@ public async Task Execute_StarWarsGetFriends_Test() { // arrange var ct = new CancellationTokenSource(20_000).Token; - using var host = TestServerHelper.CreateServer(_ => { }, out var port); + await using var app = TestServerHelper.StartApp(_ => { }, out var port); var serviceCollection = new ServiceCollection(); serviceCollection @@ -45,7 +45,7 @@ public async Task Execute_StarWarsGetFriends_WithRequestUri_Test() { // arrange var ct = new CancellationTokenSource(20_000).Token; - using var host = TestServerHelper.CreateServer(_ => { }, out var port); + await using var app = TestServerHelper.StartApp(_ => { }, out var port); var serviceCollection = new ServiceCollection(); serviceCollection @@ -72,7 +72,7 @@ public async Task Execute_StarWarsGetFriends_WithHttpClient_Test() { // arrange var ct = new CancellationTokenSource(20_000).Token; - using var host = TestServerHelper.CreateServer(_ => { }, out var port); + await using var app = TestServerHelper.StartApp(_ => { }, out var port); var serviceCollection = new ServiceCollection(); serviceCollection @@ -102,7 +102,7 @@ public async Task Execute_StarWarsGetFriends_Test_404() { // arrange var ct = new CancellationTokenSource(20_000).Token; - using var host = TestServerHelper.CreateServer(_ => { }, out var port); + await using var app = TestServerHelper.StartApp(_ => { }, out var port); var serviceCollection = new ServiceCollection(); serviceCollection @@ -129,7 +129,7 @@ public async Task Execute_StarWarsGetFriends_Test_403_And_Success_Content_1() { // arrange var ct = new CancellationTokenSource(20_000).Token; - using var host = TestServerHelper.CreateServer( + await using var app = TestServerHelper.StartApp( _ => { }, out var port); var serviceCollection = new ServiceCollection(); @@ -172,7 +172,7 @@ public async Task Execute_StarWarsGetFriends_Test_403_And_Success_Content_2() { // arrange var ct = new CancellationTokenSource(20_000).Token; - using var host = TestServerHelper.CreateServer( + await using var app = TestServerHelper.StartApp( _ => { }, out var port); var serviceCollection = new ServiceCollection(); diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/StarWarsGetHeroTest.cs b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/StarWarsGetHeroTest.cs index a2f38bbd649..51c10be51fa 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/StarWarsGetHeroTest.cs +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/StarWarsGetHeroTest.cs @@ -18,7 +18,7 @@ public async Task Execute_StarWarsGetHero_Test() { // arrange var ct = new CancellationTokenSource(20_000).Token; - using var host = TestServerHelper.CreateServer( + await using var app = TestServerHelper.StartApp( _ => { }, out var port); var serviceCollection = new ServiceCollection(); @@ -45,7 +45,7 @@ public async Task Watch_StarWarsGetHero_Test() { // arrange var ct = new CancellationTokenSource(20_000).Token; - using var host = TestServerHelper.CreateServer( + await using var app = TestServerHelper.StartApp( _ => { }, out var port); var serviceCollection = new ServiceCollection(); @@ -82,7 +82,7 @@ public async Task Watch_CacheFirst_StarWarsGetHero_Test() { // arrange var ct = new CancellationTokenSource(20_000).Token; - using var host = TestServerHelper.CreateServer( + await using var app = TestServerHelper.StartApp( _ => { }, out var port); var serviceCollection = new ServiceCollection(); @@ -121,7 +121,7 @@ public async Task Watch_Interact_With_Store() { // arrange using var cts = new CancellationTokenSource(20_000); - using var host = TestServerHelper.CreateServer( + await using var app = TestServerHelper.StartApp( _ => { }, out var port); var serviceCollection = new ServiceCollection(); @@ -190,7 +190,7 @@ public async Task Watch_Interact_With_Persistence() { // arrange using var cts = new CancellationTokenSource(100_20_000); - using var host = TestServerHelper.CreateServer( + await using var app = TestServerHelper.StartApp( _ => { }, out var port); var serviceCollection = new ServiceCollection(); @@ -256,7 +256,7 @@ public async Task Watch_Interact_With_Persistence() { // arrange using var cts = new CancellationTokenSource(100_20_000); - using var host = TestServerHelper.CreateServer( + await using var app = TestServerHelper.StartApp( _ => { }, out var port); var serviceCollection = new ServiceCollection(); @@ -303,7 +303,7 @@ public async Task Update_Once() { // arrange var ct = new CancellationTokenSource(20_000).Token; - using var host = TestServerHelper.CreateServer( + await using var app = TestServerHelper.StartApp( _ => { }, out var port); var serviceCollection = new ServiceCollection(); @@ -338,7 +338,7 @@ public async Task Update_Once_With_Cache_And_Network() { // arrange var ct = new CancellationTokenSource(20_000).Token; - using var host = TestServerHelper.CreateServer( + await using var app = TestServerHelper.StartApp( _ => { }, out var port); var serviceCollection = new ServiceCollection(); diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/StarWarsGetHeroTraitsTest.cs b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/StarWarsGetHeroTraitsTest.cs index 0e404a06f65..ecdad9b0f65 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/StarWarsGetHeroTraitsTest.cs +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/StarWarsGetHeroTraitsTest.cs @@ -15,7 +15,7 @@ public async Task Execute_StarWarsGetHeroTraits_Test() { // arrange var ct = new CancellationTokenSource(20_000).Token; - using var host = TestServerHelper.CreateServer( + await using var app = TestServerHelper.StartApp( _ => { }, out var port); var serviceCollection = new ServiceCollection(); diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/StarWarsGetHeroWithFragmentIncludeAndSkipDirectiveTest.cs b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/StarWarsGetHeroWithFragmentIncludeAndSkipDirectiveTest.cs index f4b48455f6b..f94fae12f6d 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/StarWarsGetHeroWithFragmentIncludeAndSkipDirectiveTest.cs +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/StarWarsGetHeroWithFragmentIncludeAndSkipDirectiveTest.cs @@ -15,7 +15,7 @@ public async Task Execute_StarWarsGetHeroWithFragmentIncludeAndSkipDirective_Tes { // arrange var ct = new CancellationTokenSource(20_000).Token; - using var host = TestServerHelper.CreateServer( + await using var app = TestServerHelper.StartApp( _ => { }, out var port); var serviceCollection = new ServiceCollection(); diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/StarWarsIntrospectionTest.cs b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/StarWarsIntrospectionTest.cs index 8a70f4de852..e1f7dbd2dac 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/StarWarsIntrospectionTest.cs +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/StarWarsIntrospectionTest.cs @@ -15,7 +15,7 @@ public async Task Execute_StarWarsIntrospection_Test() { // arrange var ct = new CancellationTokenSource(20_000).Token; - using var host = TestServerHelper.CreateServer( + await using var app = TestServerHelper.StartApp( _ => { }, out var port); var serviceCollection = new ServiceCollection(); diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/StarWarsOnReviewSubCompletionTest.cs b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/StarWarsOnReviewSubCompletionTest.cs index 2bb167bf847..56c1579d10e 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/StarWarsOnReviewSubCompletionTest.cs +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/StarWarsOnReviewSubCompletionTest.cs @@ -18,8 +18,8 @@ public class StarWarsOnReviewSubCompletionTest(TestServerFactory serverFactory) public async Task Watch_StarWarsOnReviewSubCompletion_Test() { // arrange - using var host = TestServerHelper.CreateServer(_ => { }, out var port); - var topicEventSender = host.Services.GetRequiredService(); + await using var app = TestServerHelper.StartApp(_ => { }, out var port); + var topicEventSender = app.Services.GetRequiredService(); var serviceCollection = new ServiceCollection(); serviceCollection.AddStarWarsOnReviewSubCompletionClient( @@ -74,10 +74,10 @@ await topicEventSender.SendAsync( public async Task Watch_StarWarsOnReviewSubCompletionPassively_Test() { // arrange - using var host = TestServerHelper.CreateServer( + await using var app = TestServerHelper.StartApp( _ => { }, out var port); - var topicEventSender = host.Services.GetRequiredService(); + var topicEventSender = app.Services.GetRequiredService(); var serviceCollection = new ServiceCollection(); serviceCollection.AddStarWarsOnReviewSubCompletionClient( @@ -118,7 +118,7 @@ await topicEventSender.SendAsync( var monitor = services.GetRequiredService(); monitor.AbortSocket(); - //await host.StopAsync(); + //await app.StopAsync(); // waiting for completion message sent for (var times = 0; !completionTriggered && times < 10; times++) diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/StarWarsOnReviewSubGraphQLSSETest.cs b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/StarWarsOnReviewSubGraphQLSSETest.cs index ebe6319529d..aac7d9922f3 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/StarWarsOnReviewSubGraphQLSSETest.cs +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/StarWarsOnReviewSubGraphQLSSETest.cs @@ -19,7 +19,7 @@ public async Task Execute_StarWarsOnReviewSubGraphQLSSE_Test() // arrange using var cts = new CancellationTokenSource(20_000); var ct = cts.Token; - using var host = TestServerHelper.CreateServer( + await using var app = TestServerHelper.StartApp( _ => { }, out var port); var serviceCollection = new ServiceCollection(); @@ -34,7 +34,7 @@ public async Task Execute_StarWarsOnReviewSubGraphQLSSE_Test() var client = services.GetRequiredService(); // act - var topicEventSender = host.Services.GetRequiredService(); + var topicEventSender = app.Services.GetRequiredService(); const Episode topic = Episode.NewHope; var connectCompletionSource = new TaskCompletionSource(); diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/StarWarsOnReviewSubNoStoreTest.cs b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/StarWarsOnReviewSubNoStoreTest.cs index 44414be0b22..245c3c9d1a0 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/StarWarsOnReviewSubNoStoreTest.cs +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/StarWarsOnReviewSubNoStoreTest.cs @@ -18,7 +18,7 @@ public async Task Watch_StarWarsOnReviewSubNoStore_NotifyCompletion() { // arrange using var cts = new CancellationTokenSource(20_000); - using var host = TestServerHelper.CreateServer(_ => { }, out var port); + await using var app = TestServerHelper.StartApp(_ => { }, out var port); var ct = cts.Token; var serviceCollection = new ServiceCollection(); @@ -33,7 +33,7 @@ public async Task Watch_StarWarsOnReviewSubNoStore_NotifyCompletion() var client = services.GetRequiredService(); // act - var topicEventSender = host.Services.GetRequiredService(); + var topicEventSender = app.Services.GetRequiredService(); const Episode topic = Episode.NewHope; var connectCompletionSource = new TaskCompletionSource(); diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/StarWarsTypeNameOnInterfacesTest.cs b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/StarWarsTypeNameOnInterfacesTest.cs index 59c2b4f5bd5..09e2bd36cc8 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/StarWarsTypeNameOnInterfacesTest.cs +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/StarWarsTypeNameOnInterfacesTest.cs @@ -17,7 +17,7 @@ public async Task Execute_StarWarsTypeNameOnInterfaces_Test() // arrange using var cts = new CancellationTokenSource(20_000); - using var host = TestServerHelper.CreateServer( + await using var app = TestServerHelper.StartApp( _ => { }, out var port); var serviceCollection = new ServiceCollection(); diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/StarWarsTypeNameOnUnionsTest.cs b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/StarWarsTypeNameOnUnionsTest.cs index 2a0db3b6aa3..549bdab8965 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/StarWarsTypeNameOnUnionsTest.cs +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/StarWarsTypeNameOnUnionsTest.cs @@ -15,7 +15,7 @@ public async Task Execute_StarWarsTypeNameOnUnions_Test() { // arrange var cts = new CancellationTokenSource(20_000); - using var host = TestServerHelper.CreateServer( + await using var app = TestServerHelper.StartApp( _ => { }, out var port); var serviceCollection = new ServiceCollection(); diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/StarWarsUnionListTest.cs b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/StarWarsUnionListTest.cs index c0ed1a1945d..b74e2b4c7ac 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/StarWarsUnionListTest.cs +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/StarWarsUnionListTest.cs @@ -16,7 +16,7 @@ public async Task Execute_StarWarsUnionList_Test() // arrange using var cts = new CancellationTokenSource(20_000); - using var host = TestServerHelper.CreateServer( + await using var app = TestServerHelper.StartApp( _ => { }, out var port); var serviceCollection = new ServiceCollection(); diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/UnsignedTypesTest.cs b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/UnsignedTypesTest.cs index 6e22176a36a..fd1f48e042f 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/UnsignedTypesTest.cs +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/UnsignedTypesTest.cs @@ -16,7 +16,7 @@ public async Task Execute_UnsignedTypes_Test() { // arrange var ct = new CancellationTokenSource(20_000).Token; - using var host = TestServerHelper.CreateServer( + await using var app = TestServerHelper.StartApp( builder => builder.AddTypeExtension(typeof(QueryType)), out var port); var serviceCollection = new ServiceCollection(); diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/UploadScalarTest.cs b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/UploadScalarTest.cs index a662fc6cd7f..b3ae7972b19 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/UploadScalarTest.cs +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/UploadScalarTest.cs @@ -1,5 +1,5 @@ using HotChocolate.AspNetCore.Tests.Utilities; -using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Builder; using Microsoft.Extensions.DependencyInjection; using StrawberryShake.Transport.WebSockets; using static StrawberryShake.CodeGeneration.CSharp.Integration.UploadScalar.UploadSchemaHelpers; @@ -19,8 +19,8 @@ public async Task Execute_UploadScalar_Argument(string? contentType) { // arrange var ct = new CancellationTokenSource(20_000).Token; - using var host = TestServerHelper.CreateServer(Configure, out var port); - var client = CreateClient(host, port); + await using var app = TestServerHelper.StartApp(Configure, out var port); + var client = CreateClient(app, port); await using var data = CreateStream("a"); // act @@ -45,8 +45,8 @@ public async Task Execute_UploadScalarList_Argument(string? contentType) { // arrange var ct = new CancellationTokenSource(20_000).Token; - using var host = TestServerHelper.CreateServer(Configure, out var port); - var client = CreateClient(host, port); + await using var app = TestServerHelper.StartApp(Configure, out var port); + var client = CreateClient(app, port); await using var dataA = CreateStream("a"); await using var dataB = CreateStream("b"); @@ -72,8 +72,8 @@ public async Task Execute_UploadScalarNested_Argument(string? contentType) { // arrange var ct = new CancellationTokenSource(20_000).Token; - using var host = TestServerHelper.CreateServer(Configure, out var port); - var client = CreateClient(host, port); + await using var app = TestServerHelper.StartApp(Configure, out var port); + var client = CreateClient(app, port); await using var dataA = CreateStream("a"); await using var dataB = CreateStream("b"); @@ -99,8 +99,8 @@ public async Task Execute_Input_Argument(string? contentType) { // arrange var ct = new CancellationTokenSource(20_000).Token; - using var host = TestServerHelper.CreateServer(Configure, out var port); - var client = CreateClient(host, port); + await using var app = TestServerHelper.StartApp(Configure, out var port); + var client = CreateClient(app, port); await using var data = CreateStream("a"); // act @@ -131,8 +131,8 @@ public async Task Execute_InputList_Argument(string? contentType) { // arrange var ct = new CancellationTokenSource(20_000).Token; - using var host = TestServerHelper.CreateServer(Configure, out var port); - var client = CreateClient(host, port); + await using var app = TestServerHelper.StartApp(Configure, out var port); + var client = CreateClient(app, port); await using var dataA = CreateStream("a"); await using var dataB = CreateStream("b"); // act @@ -173,8 +173,8 @@ public async Task Execute_InputNested_Argument(string? contentType) { // arrange var ct = new CancellationTokenSource(20_000).Token; - using var host = TestServerHelper.CreateServer(Configure, out var port); - var client = CreateClient(host, port); + await using var app = TestServerHelper.StartApp(Configure, out var port); + var client = CreateClient(app, port); await using var dataA = CreateStream("a"); await using var dataB = CreateStream("b"); @@ -212,7 +212,7 @@ public async Task Execute_InputNested_Argument(string? contentType) Assert.Equal($"[A:a|{contentType}],[B:b|{contentType}]", result.Data!.Upload); } - public static UploadScalarClient CreateClient(IWebHost host, int port) + public static UploadScalarClient CreateClient(WebApplication app, int port) { var serviceCollection = new ServiceCollection(); serviceCollection.AddHttpClient( @@ -237,8 +237,8 @@ public async Task Execute_ListWorksWithNull(string? contentType) { // arrange var ct = new CancellationTokenSource(20_000).Token; - using var host = TestServerHelper.CreateServer(Configure, out var port); - var client = CreateClient(host, port); + await using var app = TestServerHelper.StartApp(Configure, out var port); + var client = CreateClient(app, port); await using var dataA = CreateStream("a"); await using var dataB = CreateStream("b"); diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__resources__/TestTemplate.txt b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__resources__/TestTemplate.txt index 8b302fa78cd..dd75d7d25e6 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__resources__/TestTemplate.txt +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__resources__/TestTemplate.txt @@ -3,7 +3,6 @@ using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using HotChocolate.AspNetCore.Tests.Utilities; -using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.DependencyInjection; using StrawberryShake.Transport.WebSockets; @@ -20,7 +19,7 @@ public class {TestName}Test : ServerTestBase { // arrange CancellationToken ct = new CancellationTokenSource(20_000).Token; - using IWebHost host = TestServerHelper.CreateServer( + using var app = TestServerHelper.StartApp( _ => { }, out var port); var serviceCollection = new ServiceCollection();