From 94fed7b0f3e776fd4d01d7a46607c427f61b7327 Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Wed, 11 Sep 2024 20:20:20 +0200 Subject: [PATCH 1/5] Add WireMock.Net.AspNetCore.Middleware --- WireMock.Net Solution.sln | 14 ++++ .../WireMock.Net.WebApplication/Program.cs | 26 +++++++ .../Properties/launchSettings.json | 41 ++++++++++ .../WireMock.Net.WebApplication.csproj | 13 ++++ .../appsettings.Development.json | 8 ++ .../appsettings.json | 9 +++ .../AppConstants.cs | 9 +++ .../IWireMockDelegationHandlerSettings.cs | 8 ++ .../WireMockDelegationHandler .cs | 74 +++++++++++++++++++ .../WireMockDelegationHandlerSettings.cs | 8 ++ .../ServiceCollectionExtensions.cs | 59 +++++++++++++++ .../WireMock.Net.AspNetCore.Middleware.csproj | 49 ++++++++++++ .../WireMockBackgroundService.cs | 39 ++++++++++ .../WireMockServerInstance.cs | 51 +++++++++++++ 14 files changed, 408 insertions(+) create mode 100644 examples/WireMock.Net.WebApplication/Program.cs create mode 100644 examples/WireMock.Net.WebApplication/Properties/launchSettings.json create mode 100644 examples/WireMock.Net.WebApplication/WireMock.Net.WebApplication.csproj create mode 100644 examples/WireMock.Net.WebApplication/appsettings.Development.json create mode 100644 examples/WireMock.Net.WebApplication/appsettings.json create mode 100644 src/WireMock.Net.AspNetCore.Middleware/AppConstants.cs create mode 100644 src/WireMock.Net.AspNetCore.Middleware/HttpDelegatingHandler/IWireMockDelegationHandlerSettings.cs create mode 100644 src/WireMock.Net.AspNetCore.Middleware/HttpDelegatingHandler/WireMockDelegationHandler .cs create mode 100644 src/WireMock.Net.AspNetCore.Middleware/HttpDelegatingHandler/WireMockDelegationHandlerSettings.cs create mode 100644 src/WireMock.Net.AspNetCore.Middleware/ServiceCollectionExtensions.cs create mode 100644 src/WireMock.Net.AspNetCore.Middleware/WireMock.Net.AspNetCore.Middleware.csproj create mode 100644 src/WireMock.Net.AspNetCore.Middleware/WireMockBackgroundService.cs create mode 100644 src/WireMock.Net.AspNetCore.Middleware/WireMockServerInstance.cs diff --git a/WireMock.Net Solution.sln b/WireMock.Net Solution.sln index c262e192a..fa0d003de 100644 --- a/WireMock.Net Solution.sln +++ b/WireMock.Net Solution.sln @@ -131,6 +131,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WireMock.Net.Aspire.TestApp EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AspireApp1.AppHostOriginal", "examples-Aspire\AspireApp1.AppHostOriginal\AspireApp1.AppHostOriginal.csproj", "{C9210DA3-F390-4598-8512-349A473FE9C9}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WireMock.Net.WebApplication", "examples\WireMock.Net.WebApplication\WireMock.Net.WebApplication.csproj", "{E72ADFAB-4B42-439E-B1EE-C06E504B35D2}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WireMock.Net.AspNetCore.Middleware", "src\WireMock.Net.AspNetCore.Middleware\WireMock.Net.AspNetCore.Middleware.csproj", "{B6269AAC-170A-4346-8B9A-579DED3D9A13}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -317,6 +321,14 @@ Global {C9210DA3-F390-4598-8512-349A473FE9C9}.Debug|Any CPU.Build.0 = Debug|Any CPU {C9210DA3-F390-4598-8512-349A473FE9C9}.Release|Any CPU.ActiveCfg = Release|Any CPU {C9210DA3-F390-4598-8512-349A473FE9C9}.Release|Any CPU.Build.0 = Release|Any CPU + {E72ADFAB-4B42-439E-B1EE-C06E504B35D2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E72ADFAB-4B42-439E-B1EE-C06E504B35D2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E72ADFAB-4B42-439E-B1EE-C06E504B35D2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E72ADFAB-4B42-439E-B1EE-C06E504B35D2}.Release|Any CPU.Build.0 = Release|Any CPU + {B6269AAC-170A-4346-8B9A-579DED3D9A13}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B6269AAC-170A-4346-8B9A-579DED3D9A13}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B6269AAC-170A-4346-8B9A-579DED3D9A13}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B6269AAC-170A-4346-8B9A-579DED3D9A13}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -369,6 +381,8 @@ Global {CE602F57-FEF8-4559-A9E0-6200BE1BF398} = {0BB8B634-407A-4610-A91F-11586990767A} {F1B5999D-D22E-48A6-AB86-18A7876BD32E} = {0BB8B634-407A-4610-A91F-11586990767A} {C9210DA3-F390-4598-8512-349A473FE9C9} = {AD474543-0715-49F2-A284-936B060BF736} + {E72ADFAB-4B42-439E-B1EE-C06E504B35D2} = {985E0ADB-D4B4-473A-AA40-567E279B7946} + {B6269AAC-170A-4346-8B9A-579DED3D9A13} = {8F890C6F-9ACC-438D-928A-AD61CDA862F2} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {DC539027-9852-430C-B19F-FD035D018458} diff --git a/examples/WireMock.Net.WebApplication/Program.cs b/examples/WireMock.Net.WebApplication/Program.cs new file mode 100644 index 000000000..a20ae58e9 --- /dev/null +++ b/examples/WireMock.Net.WebApplication/Program.cs @@ -0,0 +1,26 @@ +using WireMock.Net.AspNetCore.Middleware; +using WireMock.RequestBuilders; +using WireMock.ResponseBuilders; + +var builder = WebApplication.CreateBuilder(args); + +builder.Services.AddControllers(); + +if (!builder.Environment.IsProduction()) +{ + builder.Services.AddWireMockService(server => + { + server.Given(Request.Create() + .WithPath("/test") + .UsingAnyMethod() + ).RespondWith(Response.Create() + .WithBody("WebApplication") + ); + }, true); +} + +var app = builder.Build(); + +app.MapControllers(); + +await app.RunAsync(); \ No newline at end of file diff --git a/examples/WireMock.Net.WebApplication/Properties/launchSettings.json b/examples/WireMock.Net.WebApplication/Properties/launchSettings.json new file mode 100644 index 000000000..f8e0180db --- /dev/null +++ b/examples/WireMock.Net.WebApplication/Properties/launchSettings.json @@ -0,0 +1,41 @@ +{ + "$schema": "http://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:57375", + "sslPort": 44333 + } + }, + "profiles": { + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "launchUrl": "test", + "applicationUrl": "http://localhost:5112", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "launchUrl": "test", + "applicationUrl": "https://localhost:7021;http://localhost:5112", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "launchUrl": "test", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/examples/WireMock.Net.WebApplication/WireMock.Net.WebApplication.csproj b/examples/WireMock.Net.WebApplication/WireMock.Net.WebApplication.csproj new file mode 100644 index 000000000..a2ac20648 --- /dev/null +++ b/examples/WireMock.Net.WebApplication/WireMock.Net.WebApplication.csproj @@ -0,0 +1,13 @@ + + + + net8.0 + enable + enable + + + + + + + diff --git a/examples/WireMock.Net.WebApplication/appsettings.Development.json b/examples/WireMock.Net.WebApplication/appsettings.Development.json new file mode 100644 index 000000000..0c208ae91 --- /dev/null +++ b/examples/WireMock.Net.WebApplication/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/examples/WireMock.Net.WebApplication/appsettings.json b/examples/WireMock.Net.WebApplication/appsettings.json new file mode 100644 index 000000000..10f68b8c8 --- /dev/null +++ b/examples/WireMock.Net.WebApplication/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" +} diff --git a/src/WireMock.Net.AspNetCore.Middleware/AppConstants.cs b/src/WireMock.Net.AspNetCore.Middleware/AppConstants.cs new file mode 100644 index 000000000..a9e6fa0ef --- /dev/null +++ b/src/WireMock.Net.AspNetCore.Middleware/AppConstants.cs @@ -0,0 +1,9 @@ +// Copyright © WireMock.Net + +namespace WireMock.Net.AspNetCore.Middleware; + +internal static class AppConstants +{ + internal const string HEADER_WIREMOCK_STATUS = "X-WireMock-Status"; + internal const string HEADER_RESPONSE_DELAY = "X-Response-Delay"; +} \ No newline at end of file diff --git a/src/WireMock.Net.AspNetCore.Middleware/HttpDelegatingHandler/IWireMockDelegationHandlerSettings.cs b/src/WireMock.Net.AspNetCore.Middleware/HttpDelegatingHandler/IWireMockDelegationHandlerSettings.cs new file mode 100644 index 000000000..0c44a36c6 --- /dev/null +++ b/src/WireMock.Net.AspNetCore.Middleware/HttpDelegatingHandler/IWireMockDelegationHandlerSettings.cs @@ -0,0 +1,8 @@ +// Copyright © WireMock.Net + +namespace WireMock.Net.AspNetCore.Middleware.HttpDelegatingHandler; + +internal interface IWireMockDelegationHandlerSettings +{ + bool AlwaysRedirect { get; set; } +} \ No newline at end of file diff --git a/src/WireMock.Net.AspNetCore.Middleware/HttpDelegatingHandler/WireMockDelegationHandler .cs b/src/WireMock.Net.AspNetCore.Middleware/HttpDelegatingHandler/WireMockDelegationHandler .cs new file mode 100644 index 000000000..43daf3898 --- /dev/null +++ b/src/WireMock.Net.AspNetCore.Middleware/HttpDelegatingHandler/WireMockDelegationHandler .cs @@ -0,0 +1,74 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Logging; +using Stef.Validation; +using WireMock.Server; + +namespace WireMock.Net.AspNetCore.Middleware.HttpDelegatingHandler; + +/// +/// DelegatingHandler that takes requests made via the +/// and routes them to the . +/// +internal class WireMockDelegationHandler : DelegatingHandler +{ + private readonly ILogger _logger; + private readonly WireMockServerInstance _server; + private readonly IHttpContextAccessor _httpContextAccessor; + private readonly IWireMockDelegationHandlerSettings _settings; + + /// + /// Creates a new instance of + /// + public WireMockDelegationHandler( + ILogger logger, + WireMockServerInstance server, + IHttpContextAccessor httpContextAccessor, + IWireMockDelegationHandlerSettings settings + ) + { + _server = Guard.NotNull(server); + _httpContextAccessor = Guard.NotNull(httpContextAccessor); + _logger = Guard.NotNull(logger); + _settings = Guard.NotNull(settings); + } + + /// + /// + protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) + { + Guard.NotNull(request); + Guard.NotNull(_httpContextAccessor.HttpContext); + + if (_settings.AlwaysRedirect || IsWireMockStatusHeaderSetToTrue()) + { + _logger.LogDebug("Redirecting request to WireMock server"); + if (_server.Instance?.Url != null) + { + request.RequestUri = new Uri(_server.Instance.Url + request.RequestUri!.PathAndQuery); + } + } + + if (TryGetDelayHeaderValue(out var delayInMs)) + { + await Task.Delay(delayInMs, cancellationToken); + } + + return await base.SendAsync(request, cancellationToken); + } + + private bool IsWireMockStatusHeaderSetToTrue() + { + return + _httpContextAccessor.HttpContext!.Request.Headers.TryGetValue(AppConstants.HEADER_WIREMOCK_STATUS, out var values) && + bool.TryParse(values.ToString(), out var shouldRedirectToWireMock) && + shouldRedirectToWireMock; + } + + private bool TryGetDelayHeaderValue(out int delayInMs) + { + delayInMs = 0; + return + _httpContextAccessor.HttpContext!.Request.Headers.TryGetValue(AppConstants.HEADER_RESPONSE_DELAY, out var values) && + int.TryParse(values.ToString(), out delayInMs); + } +} \ No newline at end of file diff --git a/src/WireMock.Net.AspNetCore.Middleware/HttpDelegatingHandler/WireMockDelegationHandlerSettings.cs b/src/WireMock.Net.AspNetCore.Middleware/HttpDelegatingHandler/WireMockDelegationHandlerSettings.cs new file mode 100644 index 000000000..95882c24d --- /dev/null +++ b/src/WireMock.Net.AspNetCore.Middleware/HttpDelegatingHandler/WireMockDelegationHandlerSettings.cs @@ -0,0 +1,8 @@ +// Copyright © WireMock.Net + +namespace WireMock.Net.AspNetCore.Middleware.HttpDelegatingHandler; + +internal class WireMockDelegationHandlerSettings : IWireMockDelegationHandlerSettings +{ + public bool AlwaysRedirect { get; set; } +} \ No newline at end of file diff --git a/src/WireMock.Net.AspNetCore.Middleware/ServiceCollectionExtensions.cs b/src/WireMock.Net.AspNetCore.Middleware/ServiceCollectionExtensions.cs new file mode 100644 index 000000000..a67aa5d79 --- /dev/null +++ b/src/WireMock.Net.AspNetCore.Middleware/ServiceCollectionExtensions.cs @@ -0,0 +1,59 @@ +// Copyright © WireMock.Net + +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Http; +using Stef.Validation; +using WireMock.Net.AspNetCore.Middleware.HttpDelegatingHandler; +using WireMock.Server; +using WireMock.Settings; + +namespace WireMock.Net.AspNetCore.Middleware; + +/// +/// Extension methods for . +/// +public static class ServiceCollectionExtensions +{ + /// + /// Adds all the components necessary to run WireMock.Net as a background service. + /// + public static IServiceCollection AddWireMockService( + this IServiceCollection services, + Action configure, + bool alwaysRedirectToWireMock = false, + WireMockServerSettings? settings = null + ) + { + Guard.NotNull(services); + Guard.NotNull(configure); + + services.AddTransient(); + + if (settings is null) + { + services.AddSingleton(new WireMockServerInstance(configure)); + } + else + { + services.AddSingleton(new WireMockServerInstance(configure, settings)); + } + + services.AddSingleton(new WireMockDelegationHandlerSettings + { + AlwaysRedirect = alwaysRedirectToWireMock + }); + + services.AddHostedService(); + services.AddHttpClient(); + services.AddHttpContextAccessor(); + services.ConfigureAll(options => + { + options.HttpMessageHandlerBuilderActions.Add(builder => + { + builder.AdditionalHandlers.Add(builder.Services.GetRequiredService()); + }); + }); + + return services; + } +} \ No newline at end of file diff --git a/src/WireMock.Net.AspNetCore.Middleware/WireMock.Net.AspNetCore.Middleware.csproj b/src/WireMock.Net.AspNetCore.Middleware/WireMock.Net.AspNetCore.Middleware.csproj new file mode 100644 index 000000000..0025768c9 --- /dev/null +++ b/src/WireMock.Net.AspNetCore.Middleware/WireMock.Net.AspNetCore.Middleware.csproj @@ -0,0 +1,49 @@ + + + + enable + Middleware which can be used to host WireMock.Net as a AspNetCore Middleware in a WebApplication + WireMock.Net.AspNetCore.Middleware + Matt Yost;Stef Heyenrath + net8.0 + true + WireMock.Net.AspNetCore.Middleware + WireMock.Net.AspNetCore.Middleware + dotnet;middleware;wiremock;service;webapplication + {B6269AAC-170A-4346-8B9A-579DED3D9A13} + true + $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb + true + true + true + ../WireMock.Net/WireMock.Net.ruleset + true + ../WireMock.Net/WireMock.Net.snk + true + MIT + WireMock.Net-LogoAspire.png + ../../resources/WireMock.Net-LogoAspire.ico + + + + + + + + + + + + + + + + + + + + + true + + + diff --git a/src/WireMock.Net.AspNetCore.Middleware/WireMockBackgroundService.cs b/src/WireMock.Net.AspNetCore.Middleware/WireMockBackgroundService.cs new file mode 100644 index 000000000..fc6716d08 --- /dev/null +++ b/src/WireMock.Net.AspNetCore.Middleware/WireMockBackgroundService.cs @@ -0,0 +1,39 @@ +// Copyright © WireMock.Net + +using Microsoft.Extensions.Hosting; +using Stef.Validation; +using WireMock.Server; + +namespace WireMock.Net.AspNetCore.Middleware; + +/// +/// used to start/stop the +/// +public class WireMockBackgroundService : BackgroundService +{ + private readonly WireMockServerInstance _server; + + /// + /// Creates a new using an instance + /// of + /// + /// + public WireMockBackgroundService(WireMockServerInstance server) + { + _server = Guard.NotNull(server); + } + + /// + protected override Task ExecuteAsync(CancellationToken stoppingToken) + { + _server.Start(); + return Task.CompletedTask; + } + + /// + public override Task StopAsync(CancellationToken cancellationToken) + { + _server.Stop(); + return base.StopAsync(cancellationToken); + } +} \ No newline at end of file diff --git a/src/WireMock.Net.AspNetCore.Middleware/WireMockServerInstance.cs b/src/WireMock.Net.AspNetCore.Middleware/WireMockServerInstance.cs new file mode 100644 index 000000000..49392b4fd --- /dev/null +++ b/src/WireMock.Net.AspNetCore.Middleware/WireMockServerInstance.cs @@ -0,0 +1,51 @@ +// Copyright © WireMock.Net + +using WireMock.Server; +using WireMock.Settings; + +namespace WireMock.Net.AspNetCore.Middleware; + +/// +/// WireMockServer Instance object +/// +public class WireMockServerInstance +{ + private readonly Action _configureAction; + private readonly WireMockServerSettings? _settings; + + /// + /// Creates a new instance and provides ability to add configuration + /// for the start method of + /// + public WireMockServerInstance(Action configure, WireMockServerSettings? settings = null) + { + _configureAction = configure; + _settings = settings; + } + + /// + /// Instance accessor for the + /// + public WireMockServer? Instance { get; private set; } + + /// + /// Configures and starts instance for use. + /// + public void Start() + { + Instance = _settings != null ? WireMockServer.Start(_settings) : WireMockServer.Start(); + + _configureAction.Invoke(Instance); + } + + /// + /// Stops the + /// + public void Stop() + { + if (Instance != null && (Instance.IsStarted || Instance.IsStartedWithAdminInterface)) + { + Instance.Stop(); + } + } +} \ No newline at end of file From 41275f6ead749fcc8c282f92531ed9099eaca8de Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Wed, 11 Sep 2024 22:57:57 +0200 Subject: [PATCH 2/5] . --- .../WireMock.Net.WebApplication/Program.cs | 22 +++++++++++++++---- .../Properties/launchSettings.json | 6 ++--- 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/examples/WireMock.Net.WebApplication/Program.cs b/examples/WireMock.Net.WebApplication/Program.cs index a20ae58e9..d00c8d2d3 100644 --- a/examples/WireMock.Net.WebApplication/Program.cs +++ b/examples/WireMock.Net.WebApplication/Program.cs @@ -4,8 +4,6 @@ var builder = WebApplication.CreateBuilder(args); -builder.Services.AddControllers(); - if (!builder.Environment.IsProduction()) { builder.Services.AddWireMockService(server => @@ -21,6 +19,22 @@ var app = builder.Build(); -app.MapControllers(); +app.MapGet("/weatherforecast", () => +{ + var forecast = Enumerable.Range(1, 5).Select(index => + new WeatherForecast + ( + DateOnly.FromDateTime(DateTime.Now.AddDays(index)), + Random.Shared.Next(-20, 55), + "..." + )) + .ToArray(); + return forecast; +}); + +app.Run(); -await app.RunAsync(); \ No newline at end of file +record WeatherForecast(DateOnly Date, int TemperatureC, string? Summary) +{ + public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); +} \ No newline at end of file diff --git a/examples/WireMock.Net.WebApplication/Properties/launchSettings.json b/examples/WireMock.Net.WebApplication/Properties/launchSettings.json index f8e0180db..2dd72c1bf 100644 --- a/examples/WireMock.Net.WebApplication/Properties/launchSettings.json +++ b/examples/WireMock.Net.WebApplication/Properties/launchSettings.json @@ -13,7 +13,7 @@ "commandName": "Project", "dotnetRunMessages": true, "launchBrowser": true, - "launchUrl": "test", + "launchUrl": "weatherforecast", "applicationUrl": "http://localhost:5112", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" @@ -23,7 +23,7 @@ "commandName": "Project", "dotnetRunMessages": true, "launchBrowser": true, - "launchUrl": "test", + "launchUrl": "weatherforecast", "applicationUrl": "https://localhost:7021;http://localhost:5112", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" @@ -32,7 +32,7 @@ "IIS Express": { "commandName": "IISExpress", "launchBrowser": true, - "launchUrl": "test", + "launchUrl": "weatherforecast", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } From 1b2eb1074b1fc93168b64f1e186bbc92099e602a Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Fri, 27 Sep 2024 20:05:03 +0200 Subject: [PATCH 3/5] WireMock.Net.Middleware.Tests --- .github/workflows/ci.yml | 2 + WireMock.Net Solution.sln | 16 +++++- azure-pipelines-ci.yml | 1 + .../AppConstants.cs | 2 +- .../WireMockDelegationHandler.cs | 6 +-- .../ServiceCollectionExtensions.cs | 2 +- .../CustomWebApplicationFactory.cs | 25 ++++++++++ .../IntegrationTests.cs | 48 ++++++++++++++++++ .../WireMock.Net.Middleware.Tests.csproj | 43 ++++++++++++++++ .../Program.cs | 49 +++++++++++++++++++ .../WireMock.Net.TestWebApplication.csproj | 13 +++++ .../appsettings.json | 9 ++++ 12 files changed, 210 insertions(+), 6 deletions(-) create mode 100644 test/WireMock.Net.Middleware.Tests/CustomWebApplicationFactory.cs create mode 100644 test/WireMock.Net.Middleware.Tests/IntegrationTests.cs create mode 100644 test/WireMock.Net.Middleware.Tests/WireMock.Net.Middleware.Tests.csproj create mode 100644 test/WireMock.Net.TestWebApplication/Program.cs create mode 100644 test/WireMock.Net.TestWebApplication/WireMock.Net.TestWebApplication.csproj create mode 100644 test/WireMock.Net.TestWebApplication/appsettings.json diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3ebd41d34..8f6695aaa 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -23,6 +23,7 @@ jobs: run: | dotnet test './test/WireMock.Net.Tests/WireMock.Net.Tests.csproj' -c Release --framework net8.0 dotnet test './test/WireMock.Net.TUnitTests/WireMock.Net.TUnitTests.csproj' -c Release --framework net8.0 + dotnet test './test/WireMock.Net.Middleware.Tests/WireMock.Net.Middleware.Tests.csproj' -c Release --framework net8.0 linux-build-and-run: name: Run Tests on Linux @@ -38,6 +39,7 @@ jobs: run: | dotnet test './test/WireMock.Net.Tests/WireMock.Net.Tests.csproj' -c Release --framework net8.0 dotnet test './test/WireMock.Net.TUnitTests/WireMock.Net.TUnitTests.csproj' -c Release --framework net8.0 + dotnet test './test/WireMock.Net.Middleware.Tests/WireMock.Net.Middleware.Tests.csproj' -c Release --framework net8.0 - name: Install .NET Aspire workload run: dotnet workload install aspire diff --git a/WireMock.Net Solution.sln b/WireMock.Net Solution.sln index 1f243cb87..ea1480fff 100644 --- a/WireMock.Net Solution.sln +++ b/WireMock.Net Solution.sln @@ -131,7 +131,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WireMock.Net.Aspire.TestApp EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AspireApp1.AppHostOriginal", "examples-Aspire\AspireApp1.AppHostOriginal\AspireApp1.AppHostOriginal.csproj", "{C9210DA3-F390-4598-8512-349A473FE9C9}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WireMock.Net.TUnit", "src\WireMock.Net.TUnit\WireMock.Net.TUnit.csproj", "{91024A93-848F-4A02-AF53-5EBE5834E23C}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WireMock.Net.TUnit", "src\WireMock.Net.TUnit\WireMock.Net.TUnit.csproj", "{91024A93-848F-4A02-AF53-5EBE5834E23C}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WireMock.Net.TUnitTests", "test\WireMock.Net.TUnitTests\WireMock.Net.TUnitTests.csproj", "{4CD237F7-B616-46B8-872F-E49B4BBB3EAE}" EndProject @@ -139,6 +139,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WireMock.Net.WebApplication EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WireMock.Net.AspNetCore.Middleware", "src\WireMock.Net.AspNetCore.Middleware\WireMock.Net.AspNetCore.Middleware.csproj", "{B6269AAC-170A-4346-8B9A-579DED3D9A13}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WireMock.Net.TestWebApplication", "test\WireMock.Net.TestWebApplication\WireMock.Net.TestWebApplication.csproj", "{6B30AA9F-DA04-4EB5-B03C-45A8EF272ECE}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WireMock.Net.Middleware.Tests", "test\WireMock.Net.Middleware.Tests\WireMock.Net.Middleware.Tests.csproj", "{A5FEF4F7-7DA2-4962-89A8-16BA942886E5}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -341,6 +345,14 @@ Global {B6269AAC-170A-4346-8B9A-579DED3D9A13}.Debug|Any CPU.Build.0 = Debug|Any CPU {B6269AAC-170A-4346-8B9A-579DED3D9A13}.Release|Any CPU.ActiveCfg = Release|Any CPU {B6269AAC-170A-4346-8B9A-579DED3D9A13}.Release|Any CPU.Build.0 = Release|Any CPU + {6B30AA9F-DA04-4EB5-B03C-45A8EF272ECE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6B30AA9F-DA04-4EB5-B03C-45A8EF272ECE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6B30AA9F-DA04-4EB5-B03C-45A8EF272ECE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6B30AA9F-DA04-4EB5-B03C-45A8EF272ECE}.Release|Any CPU.Build.0 = Release|Any CPU + {A5FEF4F7-7DA2-4962-89A8-16BA942886E5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A5FEF4F7-7DA2-4962-89A8-16BA942886E5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A5FEF4F7-7DA2-4962-89A8-16BA942886E5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A5FEF4F7-7DA2-4962-89A8-16BA942886E5}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -397,6 +409,8 @@ Global {4CD237F7-B616-46B8-872F-E49B4BBB3EAE} = {0BB8B634-407A-4610-A91F-11586990767A} {E72ADFAB-4B42-439E-B1EE-C06E504B35D2} = {985E0ADB-D4B4-473A-AA40-567E279B7946} {B6269AAC-170A-4346-8B9A-579DED3D9A13} = {8F890C6F-9ACC-438D-928A-AD61CDA862F2} + {6B30AA9F-DA04-4EB5-B03C-45A8EF272ECE} = {0BB8B634-407A-4610-A91F-11586990767A} + {A5FEF4F7-7DA2-4962-89A8-16BA942886E5} = {0BB8B634-407A-4610-A91F-11586990767A} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {DC539027-9852-430C-B19F-FD035D018458} diff --git a/azure-pipelines-ci.yml b/azure-pipelines-ci.yml index 3b2523fc9..498f6bbab 100644 --- a/azure-pipelines-ci.yml +++ b/azure-pipelines-ci.yml @@ -54,6 +54,7 @@ jobs: script: | dotnet-coverage collect "dotnet test ./test/WireMock.Net.Tests/WireMock.Net.Tests.csproj --configuration Debug --no-build --framework net8.0" -f xml -o "wiremock-coverage-xunit.xml" dotnet-coverage collect "dotnet test ./test/WireMock.Net.TUnitTests/WireMock.Net.TUnitTests.csproj --configuration Debug --no-build --framework net8.0" -f xml -o "wiremock-coverage-tunit.xml" + dotnet-coverage collect "dotnet test ./test/WireMock.Net.Middleware.Tests/WireMock.Net.Middleware.Tests.csproj --configuration Debug --no-build --framework net8.0" -f xml -o "wiremock-coverage-middleware.xml" displayName: 'Execute WireMock.Net.Tests with Coverage' - task: CmdLine@2 diff --git a/src/WireMock.Net.AspNetCore.Middleware/AppConstants.cs b/src/WireMock.Net.AspNetCore.Middleware/AppConstants.cs index 25c34add1..954dc8b40 100644 --- a/src/WireMock.Net.AspNetCore.Middleware/AppConstants.cs +++ b/src/WireMock.Net.AspNetCore.Middleware/AppConstants.cs @@ -4,6 +4,6 @@ namespace WireMock.Net.AspNetCore.Middleware; internal static class AppConstants { - internal const string HEADER_STATUS = "X-WireMock-Status"; + internal const string HEADER_REDIRECT = "X-WireMock-Redirect"; internal const string HEADER_RESPONSE_DELAY = "X-WireMock-Response-Delay"; } \ No newline at end of file diff --git a/src/WireMock.Net.AspNetCore.Middleware/HttpDelegatingHandler/WireMockDelegationHandler.cs b/src/WireMock.Net.AspNetCore.Middleware/HttpDelegatingHandler/WireMockDelegationHandler.cs index 3ca081c96..7eefd1362 100644 --- a/src/WireMock.Net.AspNetCore.Middleware/HttpDelegatingHandler/WireMockDelegationHandler.cs +++ b/src/WireMock.Net.AspNetCore.Middleware/HttpDelegatingHandler/WireMockDelegationHandler.cs @@ -39,7 +39,7 @@ protected override async Task SendAsync(HttpRequestMessage Guard.NotNull(request); Guard.NotNull(_httpContextAccessor.HttpContext); - if (_settings.AlwaysRedirect || IsWireMockStatusHeaderSetToTrue()) + if (_settings.AlwaysRedirect || IsWireMockRedirectHeaderSetToTrue()) { _logger.LogDebug("Redirecting request to WireMock server"); if (_server.Instance?.Url != null) @@ -56,10 +56,10 @@ protected override async Task SendAsync(HttpRequestMessage return await base.SendAsync(request, cancellationToken); } - private bool IsWireMockStatusHeaderSetToTrue() + private bool IsWireMockRedirectHeaderSetToTrue() { return - _httpContextAccessor.HttpContext!.Request.Headers.TryGetValue(AppConstants.HEADER_STATUS, out var values) && + _httpContextAccessor.HttpContext!.Request.Headers.TryGetValue(AppConstants.HEADER_REDIRECT, out var values) && bool.TryParse(values.ToString(), out var shouldRedirectToWireMock) && shouldRedirectToWireMock; } diff --git a/src/WireMock.Net.AspNetCore.Middleware/ServiceCollectionExtensions.cs b/src/WireMock.Net.AspNetCore.Middleware/ServiceCollectionExtensions.cs index fb5b9cc23..8a933d037 100644 --- a/src/WireMock.Net.AspNetCore.Middleware/ServiceCollectionExtensions.cs +++ b/src/WireMock.Net.AspNetCore.Middleware/ServiceCollectionExtensions.cs @@ -20,7 +20,7 @@ public static class ServiceCollectionExtensions public static IServiceCollection AddWireMockService( this IServiceCollection services, Action configure, - bool alwaysRedirectToWireMock = false, + bool alwaysRedirectToWireMock = true, WireMockServerSettings? settings = null ) { diff --git a/test/WireMock.Net.Middleware.Tests/CustomWebApplicationFactory.cs b/test/WireMock.Net.Middleware.Tests/CustomWebApplicationFactory.cs new file mode 100644 index 000000000..00ebcd7a0 --- /dev/null +++ b/test/WireMock.Net.Middleware.Tests/CustomWebApplicationFactory.cs @@ -0,0 +1,25 @@ +// Copyright © WireMock.Net + +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Mvc.Testing; + +namespace WireMock.Net.Middleware.Tests; + +internal class CustomWebApplicationFactory : WebApplicationFactory + where TEntryPoint : class +{ + private readonly List<(string Key, string Value)> _settings = new(); + + public CustomWebApplicationFactory(bool alwaysRedirectToWireMock = true) + { + _settings.Add(("AlwaysRedirectToWireMock", alwaysRedirectToWireMock.ToString().ToLowerInvariant())); + } + + protected override void ConfigureWebHost(IWebHostBuilder builder) + { + foreach (var arg in _settings) + { + builder.UseSetting(arg.Key, arg.Value); + } + } +} \ No newline at end of file diff --git a/test/WireMock.Net.Middleware.Tests/IntegrationTests.cs b/test/WireMock.Net.Middleware.Tests/IntegrationTests.cs new file mode 100644 index 000000000..c2b9f79ad --- /dev/null +++ b/test/WireMock.Net.Middleware.Tests/IntegrationTests.cs @@ -0,0 +1,48 @@ +// Copyright © WireMock.Net + +using FluentAssertions; +using WireMock.Net.TestWebApplication; + +namespace WireMock.Net.Middleware.Tests; + +public class IntegrationTests +{ + [Theory] + [InlineData("/real1", "Hello 1 from WireMock.Net !")] + [InlineData("/real2", "Hello 2 from WireMock.Net !")] + public async Task CallingRealApi_WithAlwaysRedirectToWireMockIsTrue(string requestUri, string expectedResponse) + { + // Arrange + await using var factory = new CustomWebApplicationFactory(); + using var client = factory.CreateClient(); + + // Act + var response = await client.GetAsync(requestUri); + + // Assert + response.EnsureSuccessStatusCode(); + var stringResponse = await response.Content.ReadAsStringAsync(); + stringResponse.Should().Be(expectedResponse); + } + + [Theory] + [InlineData("/real1", "Hello 1 from WireMock.Net !")] + [InlineData("/real2", "Hello 2 from WireMock.Net !")] + public async Task CallingRealApi_WithAlwaysRedirectToWireMockIsFalse(string requestUri, string expectedResponse) + { + // Arrange + await using var factory = new CustomWebApplicationFactory(false); + using var client = factory.CreateClient(); + + var request = new HttpRequestMessage(HttpMethod.Get, requestUri); + request.Headers.Add("X-WireMock-Redirect", "true"); + + // Act + var response = await client.SendAsync(request); + + // Assert + response.EnsureSuccessStatusCode(); + var stringResponse = await response.Content.ReadAsStringAsync(); + stringResponse.Should().Be(expectedResponse); + } +} \ No newline at end of file diff --git a/test/WireMock.Net.Middleware.Tests/WireMock.Net.Middleware.Tests.csproj b/test/WireMock.Net.Middleware.Tests/WireMock.Net.Middleware.Tests.csproj new file mode 100644 index 000000000..fec8bdf68 --- /dev/null +++ b/test/WireMock.Net.Middleware.Tests/WireMock.Net.Middleware.Tests.csproj @@ -0,0 +1,43 @@ + + + + net8.0 + enable + enable + false + true + true + ../../src/WireMock.Net/WireMock.Net.snk + true + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + + + \ No newline at end of file diff --git a/test/WireMock.Net.TestWebApplication/Program.cs b/test/WireMock.Net.TestWebApplication/Program.cs new file mode 100644 index 000000000..63320a130 --- /dev/null +++ b/test/WireMock.Net.TestWebApplication/Program.cs @@ -0,0 +1,49 @@ +using WireMock.Net.AspNetCore.Middleware; +using WireMock.RequestBuilders; +using WireMock.ResponseBuilders; + +namespace WireMock.Net.TestWebApplication; + +// Make the implicit Program class public so test projects can access it. +public class Program +{ + public static async Task Main(string[] args) + { + var alwaysRedirectToWireMock = args.Contains("--AlwaysRedirectToWireMock=true"); + + var builder = WebApplication.CreateBuilder(args); + + builder.Services.AddWireMockService(server => + { + server.Given(Request.Create() + .WithPath("/test1") + .UsingAnyMethod() + ).RespondWith(Response.Create() + .WithBody("Hello 1 from WireMock.Net !") + ); + + server.Given(Request.Create() + .WithPath("/test2") + .UsingAnyMethod() + ).RespondWith(Response.Create() + .WithBody("Hello 2 from WireMock.Net !") + ); + }, alwaysRedirectToWireMock); + + var app = builder.Build(); + + app.MapGet("/real1", async (HttpClient client) => + { + var result = await client.GetStringAsync("https://real-api:12345/test1"); + return result; + }); + + app.MapGet("/real2", async (IHttpClientFactory factory) => + { + using var client = factory.CreateClient(); + return await client.GetStringAsync("https://real-api:12345/test2"); + }); + + await app.RunAsync(); + } +} \ No newline at end of file diff --git a/test/WireMock.Net.TestWebApplication/WireMock.Net.TestWebApplication.csproj b/test/WireMock.Net.TestWebApplication/WireMock.Net.TestWebApplication.csproj new file mode 100644 index 000000000..acfdf66c6 --- /dev/null +++ b/test/WireMock.Net.TestWebApplication/WireMock.Net.TestWebApplication.csproj @@ -0,0 +1,13 @@ + + + + net8.0 + enable + enable + + + + + + + diff --git a/test/WireMock.Net.TestWebApplication/appsettings.json b/test/WireMock.Net.TestWebApplication/appsettings.json new file mode 100644 index 000000000..7cb1bd585 --- /dev/null +++ b/test/WireMock.Net.TestWebApplication/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Debug", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" +} \ No newline at end of file From ecb329d11e12dc2474943b2dd1756b8a00366034 Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Fri, 27 Sep 2024 20:08:45 +0200 Subject: [PATCH 4/5] . --- .../HttpDelegatingHandler/WireMockDelegationHandler.cs | 4 +--- .../WireMockServerInstance.cs | 3 ++- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/WireMock.Net.AspNetCore.Middleware/HttpDelegatingHandler/WireMockDelegationHandler.cs b/src/WireMock.Net.AspNetCore.Middleware/HttpDelegatingHandler/WireMockDelegationHandler.cs index 7eefd1362..546cab2a5 100644 --- a/src/WireMock.Net.AspNetCore.Middleware/HttpDelegatingHandler/WireMockDelegationHandler.cs +++ b/src/WireMock.Net.AspNetCore.Middleware/HttpDelegatingHandler/WireMockDelegationHandler.cs @@ -33,7 +33,6 @@ WireMockDelegationHandlerSettings settings } /// - /// protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { Guard.NotNull(request); @@ -60,8 +59,7 @@ private bool IsWireMockRedirectHeaderSetToTrue() { return _httpContextAccessor.HttpContext!.Request.Headers.TryGetValue(AppConstants.HEADER_REDIRECT, out var values) && - bool.TryParse(values.ToString(), out var shouldRedirectToWireMock) && - shouldRedirectToWireMock; + bool.TryParse(values.ToString(), out var shouldRedirectToWireMock) && shouldRedirectToWireMock; } private bool TryGetDelayHeaderValue(out int delayInMs) diff --git a/src/WireMock.Net.AspNetCore.Middleware/WireMockServerInstance.cs b/src/WireMock.Net.AspNetCore.Middleware/WireMockServerInstance.cs index fcb36fd1e..c3cc91f6f 100644 --- a/src/WireMock.Net.AspNetCore.Middleware/WireMockServerInstance.cs +++ b/src/WireMock.Net.AspNetCore.Middleware/WireMockServerInstance.cs @@ -1,5 +1,6 @@ // Copyright © WireMock.Net +using Stef.Validation; using WireMock.Server; using WireMock.Settings; @@ -19,7 +20,7 @@ internal class WireMockServerInstance /// public WireMockServerInstance(Action configure, WireMockServerSettings? settings = null) { - _configureAction = configure; + _configureAction = Guard.NotNull(configure); _settings = settings; } From a3bae4b27cc088166efcecb168171de7ce311eef Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Fri, 27 Sep 2024 20:25:05 +0200 Subject: [PATCH 5/5] X-WireMock-Response-Delay --- .../IntegrationTests.cs | 1 + .../Properties/launchSettings.json | 12 ++++++++++++ 2 files changed, 13 insertions(+) create mode 100644 test/WireMock.Net.TestWebApplication/Properties/launchSettings.json diff --git a/test/WireMock.Net.Middleware.Tests/IntegrationTests.cs b/test/WireMock.Net.Middleware.Tests/IntegrationTests.cs index c2b9f79ad..00d420e02 100644 --- a/test/WireMock.Net.Middleware.Tests/IntegrationTests.cs +++ b/test/WireMock.Net.Middleware.Tests/IntegrationTests.cs @@ -36,6 +36,7 @@ public async Task CallingRealApi_WithAlwaysRedirectToWireMockIsFalse(string requ var request = new HttpRequestMessage(HttpMethod.Get, requestUri); request.Headers.Add("X-WireMock-Redirect", "true"); + request.Headers.Add("X-WireMock-Response-Delay", "10"); // Act var response = await client.SendAsync(request); diff --git a/test/WireMock.Net.TestWebApplication/Properties/launchSettings.json b/test/WireMock.Net.TestWebApplication/Properties/launchSettings.json new file mode 100644 index 000000000..a79dad858 --- /dev/null +++ b/test/WireMock.Net.TestWebApplication/Properties/launchSettings.json @@ -0,0 +1,12 @@ +{ + "profiles": { + "WireMock.Net.TestWebApplication": { + "commandName": "Project", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "applicationUrl": "https://localhost:57712;http://localhost:57713" + } + } +} \ No newline at end of file