From 455b78048763311a67723207d5e9ba21c075fc4a Mon Sep 17 00:00:00 2001 From: martincostello Date: Sun, 14 Apr 2024 17:55:06 +0100 Subject: [PATCH] Add .NET 8 Minimal API test app Add WeatherForecast .NET 8 Minimal API test application. Resolves #2791. --- Directory.Packages.props | 1 + Swashbuckle.AspNetCore.sln | 15 ++++++ .../SwaggerIntegrationTests.cs | 37 ++++++++++++--- ...hbuckle.AspNetCore.IntegrationTests.csproj | 5 ++ .../TestApplication.cs | 19 ++++++++ test/WebSites/WebApi/Program.cs | 46 +++++++++++++++++++ .../WebApi/Properties/launchSettings.json | 41 +++++++++++++++++ test/WebSites/WebApi/WebApi.csproj | 19 ++++++++ test/WebSites/WebApi/WebApi.http | 4 ++ .../WebApi/appsettings.Development.json | 8 ++++ test/WebSites/WebApi/appsettings.json | 9 ++++ 11 files changed, 198 insertions(+), 6 deletions(-) create mode 100644 test/Swashbuckle.AspNetCore.IntegrationTests/TestApplication.cs create mode 100644 test/WebSites/WebApi/Program.cs create mode 100644 test/WebSites/WebApi/Properties/launchSettings.json create mode 100644 test/WebSites/WebApi/WebApi.csproj create mode 100644 test/WebSites/WebApi/WebApi.http create mode 100644 test/WebSites/WebApi/appsettings.Development.json create mode 100644 test/WebSites/WebApi/appsettings.json diff --git a/Directory.Packages.props b/Directory.Packages.props index b8276feace..76ad9e3eaa 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -13,6 +13,7 @@ + diff --git a/Swashbuckle.AspNetCore.sln b/Swashbuckle.AspNetCore.sln index d310bb0f8b..a72a6e346a 100644 --- a/Swashbuckle.AspNetCore.sln +++ b/Swashbuckle.AspNetCore.sln @@ -107,6 +107,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{ .github\workflows\update-dotnet-sdk.yml = .github\workflows\update-dotnet-sdk.yml EndProjectSection EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WebApi", "test\WebSites\WebApi\WebApi.csproj", "{DE1D77F8-3916-4DEE-A57D-6DDC357F64C6}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -501,6 +503,18 @@ Global {3D0126CB-5439-483C-B2D5-4B4BE111D15C}.Release|x64.Build.0 = Release|Any CPU {3D0126CB-5439-483C-B2D5-4B4BE111D15C}.Release|x86.ActiveCfg = Release|Any CPU {3D0126CB-5439-483C-B2D5-4B4BE111D15C}.Release|x86.Build.0 = Release|Any CPU + {DE1D77F8-3916-4DEE-A57D-6DDC357F64C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DE1D77F8-3916-4DEE-A57D-6DDC357F64C6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DE1D77F8-3916-4DEE-A57D-6DDC357F64C6}.Debug|x64.ActiveCfg = Debug|Any CPU + {DE1D77F8-3916-4DEE-A57D-6DDC357F64C6}.Debug|x64.Build.0 = Debug|Any CPU + {DE1D77F8-3916-4DEE-A57D-6DDC357F64C6}.Debug|x86.ActiveCfg = Debug|Any CPU + {DE1D77F8-3916-4DEE-A57D-6DDC357F64C6}.Debug|x86.Build.0 = Debug|Any CPU + {DE1D77F8-3916-4DEE-A57D-6DDC357F64C6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DE1D77F8-3916-4DEE-A57D-6DDC357F64C6}.Release|Any CPU.Build.0 = Release|Any CPU + {DE1D77F8-3916-4DEE-A57D-6DDC357F64C6}.Release|x64.ActiveCfg = Release|Any CPU + {DE1D77F8-3916-4DEE-A57D-6DDC357F64C6}.Release|x64.Build.0 = Release|Any CPU + {DE1D77F8-3916-4DEE-A57D-6DDC357F64C6}.Release|x86.ActiveCfg = Release|Any CPU + {DE1D77F8-3916-4DEE-A57D-6DDC357F64C6}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -541,6 +555,7 @@ Global {3D0126CB-5439-483C-B2D5-4B4BE111D15C} = {DB3F57FC-1472-4F03-B551-43394DA3C5EB} {3BA087DA-788C-43D6-9D8B-1EF017014A4A} = {FA1B4021-0A97-4F68-B966-148191F6AAA8} {A0EC16BE-C520-4FCF-BB54-2D79CD255F00} = {3BA087DA-788C-43D6-9D8B-1EF017014A4A} + {DE1D77F8-3916-4DEE-A57D-6DDC357F64C6} = {DB3F57FC-1472-4F03-B551-43394DA3C5EB} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {36FC6A67-247D-4149-8EDD-79FFD1A75F51} diff --git a/test/Swashbuckle.AspNetCore.IntegrationTests/SwaggerIntegrationTests.cs b/test/Swashbuckle.AspNetCore.IntegrationTests/SwaggerIntegrationTests.cs index ec55d29fa0..04ef9f71f6 100644 --- a/test/Swashbuckle.AspNetCore.IntegrationTests/SwaggerIntegrationTests.cs +++ b/test/Swashbuckle.AspNetCore.IntegrationTests/SwaggerIntegrationTests.cs @@ -1,6 +1,7 @@ using System; using System.Globalization; using System.Linq; +using System.Net.Http; using System.Text; using System.Text.Json; using System.Threading.Tasks; @@ -33,12 +34,7 @@ public async Task SwaggerEndpoint_ReturnsValidSwaggerJson( var testSite = new TestSite(startupType); var client = testSite.BuildClient(); - var swaggerResponse = await client.GetAsync(swaggerRequestUri); - - swaggerResponse.EnsureSuccessStatusCode(); - var contentStream = await swaggerResponse.Content.ReadAsStreamAsync(); - new OpenApiStreamReader().Read(contentStream, out OpenApiDiagnostic diagnostic); - Assert.Empty(diagnostic.Errors); + await AssertValidSwaggerJson(client, swaggerRequestUri); } [Fact] @@ -112,5 +108,34 @@ public async Task SwaggerMiddleware_CanBeConfiguredMultipleTimes( var json = await JsonSerializer.DeserializeAsync(contentStream); Assert.Equal(expectedVersionValue, json.GetProperty(expectedVersionProperty).GetString()); } + +#if NET8_0_OR_GREATER + [Theory] + [InlineData("/swagger/v1/swagger.json")] + public async Task SwaggerEndpoint_ReturnsValidSwaggerJson_For_WebApi( + string swaggerRequestUri) + { + await SwaggerEndpointReturnsValidSwaggerJson(swaggerRequestUri); + } + + private async Task SwaggerEndpointReturnsValidSwaggerJson(string swaggerRequestUri) + where TEntryPoint : class + { + var application = new TestApplication(); + var client = application.CreateDefaultClient(); + + await AssertValidSwaggerJson(client, swaggerRequestUri); + } +#endif + + private async Task AssertValidSwaggerJson(HttpClient client, string swaggerRequestUri) + { + using var swaggerResponse = await client.GetAsync(swaggerRequestUri); + + swaggerResponse.EnsureSuccessStatusCode(); + using var contentStream = await swaggerResponse.Content.ReadAsStreamAsync(); + new OpenApiStreamReader().Read(contentStream, out OpenApiDiagnostic diagnostic); + Assert.Empty(diagnostic.Errors); + } } } diff --git a/test/Swashbuckle.AspNetCore.IntegrationTests/Swashbuckle.AspNetCore.IntegrationTests.csproj b/test/Swashbuckle.AspNetCore.IntegrationTests/Swashbuckle.AspNetCore.IntegrationTests.csproj index a878d3540b..0218f6a066 100644 --- a/test/Swashbuckle.AspNetCore.IntegrationTests/Swashbuckle.AspNetCore.IntegrationTests.csproj +++ b/test/Swashbuckle.AspNetCore.IntegrationTests/Swashbuckle.AspNetCore.IntegrationTests.csproj @@ -21,7 +21,12 @@ + + + + + diff --git a/test/Swashbuckle.AspNetCore.IntegrationTests/TestApplication.cs b/test/Swashbuckle.AspNetCore.IntegrationTests/TestApplication.cs new file mode 100644 index 0000000000..64e1f3ca84 --- /dev/null +++ b/test/Swashbuckle.AspNetCore.IntegrationTests/TestApplication.cs @@ -0,0 +1,19 @@ +using System.IO; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Mvc.Testing; +using Microsoft.AspNetCore.TestHost; + +namespace Swashbuckle.AspNetCore.IntegrationTests; + +public class TestApplication : WebApplicationFactory + where TEntryPoint : class +{ + protected override void ConfigureWebHost(IWebHostBuilder builder) + { + var startupAssembly = typeof(TEntryPoint).Assembly; + var applicationName = startupAssembly.GetName().Name; + + builder.UseEnvironment("Development") + .UseSolutionRelativeContentRoot(Path.Combine("test", "WebSites", applicationName)); + } +} diff --git a/test/WebSites/WebApi/Program.cs b/test/WebSites/WebApi/Program.cs new file mode 100644 index 0000000000..860b079886 --- /dev/null +++ b/test/WebSites/WebApi/Program.cs @@ -0,0 +1,46 @@ +var builder = WebApplication.CreateBuilder(args); + +builder.Services.AddEndpointsApiExplorer(); +builder.Services.AddSwaggerGen(c => +{ + c.SwaggerDoc("v1", new() { Title = "WebApi", Version = "v1" }); +}); + +var app = builder.Build(); + +app.UseSwagger(); +app.UseSwaggerUI(); + +app.UseHttpsRedirection(); + +var summaries = new[] +{ + "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" +}; + +app.MapGet("/weatherforecast", () => +{ + var forecast = Enumerable.Range(1, 5).Select(index => + new WeatherForecast + ( + DateOnly.FromDateTime(DateTime.Now.AddDays(index)), + Random.Shared.Next(-20, 55), + summaries[Random.Shared.Next(summaries.Length)] + )) + .ToArray(); + return forecast; +}) +.WithName("GetWeatherForecast") +.WithOpenApi(); + +app.Run(); + +record WeatherForecast(DateOnly Date, int TemperatureC, string? Summary) +{ + public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); +} + +public partial class Program +{ + // Expose the Program class for use with WebApplicationFactory +} diff --git a/test/WebSites/WebApi/Properties/launchSettings.json b/test/WebSites/WebApi/Properties/launchSettings.json new file mode 100644 index 0000000000..82bdd832be --- /dev/null +++ b/test/WebSites/WebApi/Properties/launchSettings.json @@ -0,0 +1,41 @@ +{ + "$schema": "http://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:21394", + "sslPort": 44373 + } + }, + "profiles": { + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "launchUrl": "swagger", + "applicationUrl": "http://localhost:5205", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "launchUrl": "swagger", + "applicationUrl": "https://localhost:7175;http://localhost:5205", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "launchUrl": "swagger", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/test/WebSites/WebApi/WebApi.csproj b/test/WebSites/WebApi/WebApi.csproj new file mode 100644 index 0000000000..727da5ac8b --- /dev/null +++ b/test/WebSites/WebApi/WebApi.csproj @@ -0,0 +1,19 @@ + + + + enable + enable + WebApi + net8.0 + + + + + + + + + + + + diff --git a/test/WebSites/WebApi/WebApi.http b/test/WebSites/WebApi/WebApi.http new file mode 100644 index 0000000000..abc2f6184b --- /dev/null +++ b/test/WebSites/WebApi/WebApi.http @@ -0,0 +1,4 @@ +@WebApi_HostAddress = http://localhost:5205 + +GET {{WebApi_HostAddress}}/weatherforecast/ +Accept: application/json diff --git a/test/WebSites/WebApi/appsettings.Development.json b/test/WebSites/WebApi/appsettings.Development.json new file mode 100644 index 0000000000..0c208ae918 --- /dev/null +++ b/test/WebSites/WebApi/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/test/WebSites/WebApi/appsettings.json b/test/WebSites/WebApi/appsettings.json new file mode 100644 index 0000000000..10f68b8c8b --- /dev/null +++ b/test/WebSites/WebApi/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" +}