From 31298d281df26e29a6f1abc42d9fd211ffc95248 Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Wed, 26 Oct 2022 08:43:51 +0200 Subject: [PATCH] Support deleting / resetting a single scenario (#834) * Support deleting / resetting a single scenario * move files --- WireMock.Net Solution.sln.DotSettings | 8 ++ .../Constants/HttpRequestMethod.cs} | 4 +- .../Server/IWireMockServer.cs | 7 +- .../Assertions/WireMockAssertions.cs | 19 ++--- .../IWireMockAdminApi.cs | 16 +++- .../Owin/WireMockMiddlewareOptions.cs | 2 +- .../Pact/Models/V2/Interaction.cs | 1 + .../Pact/Models/V2/MatchingRule.cs | 40 +++++----- src/WireMock.Net/Pact/Models/V2/Metadata.cs | 12 +-- src/WireMock.Net/Pact/Models/V2/Pact.cs | 16 ++-- .../Pact/Models/V2/PactRequest.cs | 4 +- .../Pact/Models/V2/PactResponse.cs | 1 + src/WireMock.Net/Pact/Models/V2/PactRust.cs | 14 ++-- .../Pact/Models/V2/PactSpecification.cs | 10 +-- .../Pact/Models/V2/Pacticipant.cs | 10 +-- .../Pact/Models/V2/ProviderState.cs | 12 +-- src/WireMock.Net/Proxy/ProxyHelper.cs | 74 +------------------ .../RequestBuilders/Request.UsingMethods.cs | 22 +++--- src/WireMock.Net/ScenarioState.cs | 2 +- .../Server/WireMockServer.Admin.cs | 15 ++++ src/WireMock.Net/Server/WireMockServer.cs | 7 ++ src/WireMock.Net/Transformers/ITransformer.cs | 2 +- src/WireMock.Net/Transformers/Transformer.cs | 7 +- src/WireMock.Net/Util/BodyParser.cs | 22 +++--- .../WireMockAdminApiTests.cs | 26 +++++++ .../WireMockServer.Proxy.cs | 2 +- .../WireMockServer.Settings.cs | 6 +- 27 files changed, 184 insertions(+), 177 deletions(-) rename src/{WireMock.Net/Http/HttpRequestMethods.cs => WireMock.Net.Abstractions/Constants/HttpRequestMethod.cs} (87%) diff --git a/WireMock.Net Solution.sln.DotSettings b/WireMock.Net Solution.sln.DotSettings index 448c9dfd6..99ef6d875 100644 --- a/WireMock.Net Solution.sln.DotSettings +++ b/WireMock.Net Solution.sln.DotSettings @@ -1,10 +1,18 @@  AD + CONNECT CS + DELETE EC + GET + HEAD ID IP MD5 + OPTIONS + PATCH + POST + PUT RSA SSL TE diff --git a/src/WireMock.Net/Http/HttpRequestMethods.cs b/src/WireMock.Net.Abstractions/Constants/HttpRequestMethod.cs similarity index 87% rename from src/WireMock.Net/Http/HttpRequestMethods.cs rename to src/WireMock.Net.Abstractions/Constants/HttpRequestMethod.cs index e33399dbb..6f689a09f 100644 --- a/src/WireMock.Net/Http/HttpRequestMethods.cs +++ b/src/WireMock.Net.Abstractions/Constants/HttpRequestMethod.cs @@ -1,9 +1,9 @@ -namespace WireMock.Http; +namespace WireMock.Constants; /// /// https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods /// -internal static class HttpRequestMethods +public static class HttpRequestMethod { public const string CONNECT = "CONNECT"; public const string DELETE = "DELETE"; diff --git a/src/WireMock.Net.Abstractions/Server/IWireMockServer.cs b/src/WireMock.Net.Abstractions/Server/IWireMockServer.cs index 3393bdda5..2775049be 100644 --- a/src/WireMock.Net.Abstractions/Server/IWireMockServer.cs +++ b/src/WireMock.Net.Abstractions/Server/IWireMockServer.cs @@ -1,10 +1,8 @@ using System; using System.Collections.Generic; using System.Collections.Specialized; -using JetBrains.Annotations; using WireMock.Admin.Mappings; using WireMock.Logging; -using WireMock.Matchers.Request; namespace WireMock.Server { @@ -144,6 +142,11 @@ public interface IWireMockServer : IDisposable /// void ResetScenarios(); + /// + /// Resets a specific Scenario by the name. + /// + bool ResetScenario(string name); + /// /// Resets the LogEntries. /// diff --git a/src/WireMock.Net.FluentAssertions/Assertions/WireMockAssertions.cs b/src/WireMock.Net.FluentAssertions/Assertions/WireMockAssertions.cs index 7fb7911f6..cb1709877 100644 --- a/src/WireMock.Net.FluentAssertions/Assertions/WireMockAssertions.cs +++ b/src/WireMock.Net.FluentAssertions/Assertions/WireMockAssertions.cs @@ -4,6 +4,7 @@ using System.Linq; using FluentAssertions; using FluentAssertions.Execution; +using WireMock.Constants; using WireMock.Server; using WireMock.Types; @@ -169,39 +170,39 @@ public AndConstraint WithHeader(string expectedKey, string[] [CustomAssertion] public AndConstraint UsingConnect(string because = "", params object[] becauseArgs) - => UsingMethod("CONNECT", because, becauseArgs); + => UsingMethod(HttpRequestMethod.CONNECT, because, becauseArgs); [CustomAssertion] public AndConstraint UsingDelete(string because = "", params object[] becauseArgs) - => UsingMethod("DELETE", because, becauseArgs); + => UsingMethod(HttpRequestMethod.DELETE, because, becauseArgs); [CustomAssertion] public AndConstraint UsingGet(string because = "", params object[] becauseArgs) - => UsingMethod("GET", because, becauseArgs); + => UsingMethod(HttpRequestMethod.GET, because, becauseArgs); [CustomAssertion] public AndConstraint UsingHead(string because = "", params object[] becauseArgs) - => UsingMethod("HEAD", because, becauseArgs); + => UsingMethod(HttpRequestMethod.HEAD, because, becauseArgs); [CustomAssertion] public AndConstraint UsingOptions(string because = "", params object[] becauseArgs) - => UsingMethod("OPTIONS", because, becauseArgs); + => UsingMethod(HttpRequestMethod.OPTIONS, because, becauseArgs); [CustomAssertion] public AndConstraint UsingPost(string because = "", params object[] becauseArgs) - => UsingMethod("POST", because, becauseArgs); + => UsingMethod(HttpRequestMethod.POST, because, becauseArgs); [CustomAssertion] public AndConstraint UsingPatch(string because = "", params object[] becauseArgs) - => UsingMethod("PATCH", because, becauseArgs); + => UsingMethod(HttpRequestMethod.PATCH, because, becauseArgs); [CustomAssertion] public AndConstraint UsingPut(string because = "", params object[] becauseArgs) - => UsingMethod("PUT", because, becauseArgs); + => UsingMethod(HttpRequestMethod.PUT, because, becauseArgs); [CustomAssertion] public AndConstraint UsingTrace(string because = "", params object[] becauseArgs) - => UsingMethod("TRACE", because, becauseArgs); + => UsingMethod(HttpRequestMethod.TRACE, because, becauseArgs); [CustomAssertion] public AndConstraint UsingAnyMethod(string because = "", params object[] becauseArgs) diff --git a/src/WireMock.Net.RestClient/IWireMockAdminApi.cs b/src/WireMock.Net.RestClient/IWireMockAdminApi.cs index 44737cbb6..65a62bb4a 100644 --- a/src/WireMock.Net.RestClient/IWireMockAdminApi.cs +++ b/src/WireMock.Net.RestClient/IWireMockAdminApi.cs @@ -1,4 +1,4 @@ -using RestEase; +using RestEase; using System; using System.Collections.Generic; using System.Net.Http.Headers; @@ -179,6 +179,20 @@ public interface IWireMockAdminApi [Post("scenarios")] Task ResetScenariosAsync(); + /// + /// Delete (reset) a specific scenario + /// + [Delete("scenarios/{name}")] + [AllowAnyStatusCode] + Task DeleteScenarioAsync([Path] string name); + + /// + /// Delete (reset) all scenarios + /// + [Post("scenarios/{name}/reset")] + [AllowAnyStatusCode] + Task ResetScenarioAsync([Path] string name); + /// /// Create a new File /// diff --git a/src/WireMock.Net/Owin/WireMockMiddlewareOptions.cs b/src/WireMock.Net/Owin/WireMockMiddlewareOptions.cs index 568d1bd14..72cbe3cf1 100644 --- a/src/WireMock.Net/Owin/WireMockMiddlewareOptions.cs +++ b/src/WireMock.Net/Owin/WireMockMiddlewareOptions.cs @@ -26,7 +26,7 @@ internal class WireMockMiddlewareOptions : IWireMockMiddlewareOptions public ConcurrentDictionary Mappings { get; } = new ConcurrentDictionary(); - public ConcurrentDictionary Scenarios { get; } = new(); + public ConcurrentDictionary Scenarios { get; } = new(StringComparer.OrdinalIgnoreCase); public ConcurrentObservableCollection LogEntries { get; } = new(); diff --git a/src/WireMock.Net/Pact/Models/V2/Interaction.cs b/src/WireMock.Net/Pact/Models/V2/Interaction.cs index f09dc1cc4..b5d4de8ee 100644 --- a/src/WireMock.Net/Pact/Models/V2/Interaction.cs +++ b/src/WireMock.Net/Pact/Models/V2/Interaction.cs @@ -1,3 +1,4 @@ +#pragma warning disable CS1591 namespace WireMock.Pact.Models.V2; public class Interaction diff --git a/src/WireMock.Net/Pact/Models/V2/MatchingRule.cs b/src/WireMock.Net/Pact/Models/V2/MatchingRule.cs index f5b147a48..6d08751b1 100644 --- a/src/WireMock.Net/Pact/Models/V2/MatchingRule.cs +++ b/src/WireMock.Net/Pact/Models/V2/MatchingRule.cs @@ -1,25 +1,25 @@ -namespace WireMock.Pact.Models.V2 +#pragma warning disable CS1591 +namespace WireMock.Pact.Models.V2; + +public class MatchingRule { - public class MatchingRule - { - /// - /// type or regex - /// - public string Match { get; set; } = "type"; + /// + /// type or regex + /// + public string Match { get; set; } = "type"; - /// - /// Used for Match = "type" - /// - public string Min { get; set; } + /// + /// Used for Match = "type" + /// + public string Min { get; set; } - /// - /// Used for Match = "type" - /// - public string Max { get; set; } + /// + /// Used for Match = "type" + /// + public string Max { get; set; } - /// - /// Used for Match = "regex" - /// - public string Regex { get; set; } - } + /// + /// Used for Match = "regex" + /// + public string Regex { get; set; } } \ No newline at end of file diff --git a/src/WireMock.Net/Pact/Models/V2/Metadata.cs b/src/WireMock.Net/Pact/Models/V2/Metadata.cs index fbae2808d..f3c8edddf 100644 --- a/src/WireMock.Net/Pact/Models/V2/Metadata.cs +++ b/src/WireMock.Net/Pact/Models/V2/Metadata.cs @@ -1,9 +1,9 @@ -namespace WireMock.Pact.Models.V2 +#pragma warning disable CS1591 +namespace WireMock.Pact.Models.V2; + +public class Metadata { - public class Metadata - { - public string PactSpecificationVersion { get; set; } + public string PactSpecificationVersion { get; set; } - public PactSpecification PactSpecification { get; set; } = new PactSpecification(); - } + public PactSpecification PactSpecification { get; set; } = new PactSpecification(); } \ No newline at end of file diff --git a/src/WireMock.Net/Pact/Models/V2/Pact.cs b/src/WireMock.Net/Pact/Models/V2/Pact.cs index 9c75f80a8..de8e904ae 100644 --- a/src/WireMock.Net/Pact/Models/V2/Pact.cs +++ b/src/WireMock.Net/Pact/Models/V2/Pact.cs @@ -1,15 +1,15 @@ +#pragma warning disable CS1591 using System.Collections.Generic; -namespace WireMock.Pact.Models.V2 +namespace WireMock.Pact.Models.V2; + +public class Pact { - public class Pact - { - public Pacticipant Consumer { get; set; } + public Pacticipant Consumer { get; set; } - public List Interactions { get; set; } = new List(); + public List Interactions { get; set; } = new List(); - public Metadata Metadata { get; set; } + public Metadata Metadata { get; set; } - public Pacticipant Provider { get; set; } - } + public Pacticipant Provider { get; set; } } \ No newline at end of file diff --git a/src/WireMock.Net/Pact/Models/V2/PactRequest.cs b/src/WireMock.Net/Pact/Models/V2/PactRequest.cs index b4329b832..03ac4bc68 100644 --- a/src/WireMock.Net/Pact/Models/V2/PactRequest.cs +++ b/src/WireMock.Net/Pact/Models/V2/PactRequest.cs @@ -1,4 +1,6 @@ +#pragma warning disable CS1591 using System.Collections.Generic; +using WireMock.Constants; namespace WireMock.Pact.Models.V2; @@ -6,7 +8,7 @@ public class PactRequest { public IDictionary? Headers { get; set; } - public string Method { get; set; } = "GET"; + public string Method { get; set; } = HttpRequestMethod.GET; public string? Path { get; set; } = "/"; diff --git a/src/WireMock.Net/Pact/Models/V2/PactResponse.cs b/src/WireMock.Net/Pact/Models/V2/PactResponse.cs index d2bdc8122..e5e4fbb8d 100644 --- a/src/WireMock.Net/Pact/Models/V2/PactResponse.cs +++ b/src/WireMock.Net/Pact/Models/V2/PactResponse.cs @@ -1,3 +1,4 @@ +#pragma warning disable CS1591 using System.Collections.Generic; namespace WireMock.Pact.Models.V2; diff --git a/src/WireMock.Net/Pact/Models/V2/PactRust.cs b/src/WireMock.Net/Pact/Models/V2/PactRust.cs index 70347ca9c..d9cd1e3b9 100644 --- a/src/WireMock.Net/Pact/Models/V2/PactRust.cs +++ b/src/WireMock.Net/Pact/Models/V2/PactRust.cs @@ -1,11 +1,11 @@ -namespace WireMock.Pact.Models.V2 +#pragma warning disable CS1591 +namespace WireMock.Pact.Models.V2; + +public class PactRust { - public class PactRust - { - public string Ffi { get; set; } + public string Ffi { get; set; } - public string Mockserver { get; set; } + public string Mockserver { get; set; } - public string Models { get; set; } - } + public string Models { get; set; } } \ No newline at end of file diff --git a/src/WireMock.Net/Pact/Models/V2/PactSpecification.cs b/src/WireMock.Net/Pact/Models/V2/PactSpecification.cs index de7309be1..c91515f8b 100644 --- a/src/WireMock.Net/Pact/Models/V2/PactSpecification.cs +++ b/src/WireMock.Net/Pact/Models/V2/PactSpecification.cs @@ -1,7 +1,7 @@ -namespace WireMock.Pact.Models.V2 +#pragma warning disable CS1591 +namespace WireMock.Pact.Models.V2; + +public class PactSpecification { - public class PactSpecification - { - public string Version { get; set; } = "2.0"; - } + public string Version { get; set; } = "2.0"; } \ No newline at end of file diff --git a/src/WireMock.Net/Pact/Models/V2/Pacticipant.cs b/src/WireMock.Net/Pact/Models/V2/Pacticipant.cs index 3077835e8..b701137e1 100644 --- a/src/WireMock.Net/Pact/Models/V2/Pacticipant.cs +++ b/src/WireMock.Net/Pact/Models/V2/Pacticipant.cs @@ -1,7 +1,7 @@ -namespace WireMock.Pact.Models.V2 +#pragma warning disable CS1591 +namespace WireMock.Pact.Models.V2; + +public class Pacticipant { - public class Pacticipant - { - public string Name { get; set; } - } + public string Name { get; set; } } \ No newline at end of file diff --git a/src/WireMock.Net/Pact/Models/V2/ProviderState.cs b/src/WireMock.Net/Pact/Models/V2/ProviderState.cs index 0d163cc82..dd9a5a0d3 100644 --- a/src/WireMock.Net/Pact/Models/V2/ProviderState.cs +++ b/src/WireMock.Net/Pact/Models/V2/ProviderState.cs @@ -1,11 +1,11 @@ +#pragma warning disable CS1591 using System.Collections.Generic; -namespace WireMock.Pact.Models.V2 +namespace WireMock.Pact.Models.V2; + +public class ProviderState { - public class ProviderState - { - public string Name { get; set; } + public string Name { get; set; } - public IDictionary Params { get; set; } - } + public IDictionary Params { get; set; } } \ No newline at end of file diff --git a/src/WireMock.Net/Proxy/ProxyHelper.cs b/src/WireMock.Net/Proxy/ProxyHelper.cs index 26a32a8e9..ed7270e51 100644 --- a/src/WireMock.Net/Proxy/ProxyHelper.cs +++ b/src/WireMock.Net/Proxy/ProxyHelper.cs @@ -1,17 +1,10 @@ -using Stef.Validation; using System; -using System.Collections.Generic; -using System.Linq; using System.Net.Http; using System.Threading.Tasks; -using WireMock.Constants; +using Stef.Validation; using WireMock.Http; -using WireMock.Matchers; -using WireMock.RequestBuilders; -using WireMock.ResponseBuilders; using WireMock.Serialization; using WireMock.Settings; -using WireMock.Types; using WireMock.Util; namespace WireMock.Proxy; @@ -62,69 +55,4 @@ public ProxyHelper(WireMockServerSettings settings) return (responseMessage, newMapping); } - - private IMapping ToMapping(ProxyAndRecordSettings proxyAndRecordSettings, IRequestMessage requestMessage, ResponseMessage responseMessage) - { - var excludedHeaders = proxyAndRecordSettings.ExcludedHeaders ?? new string[] { }; - var excludedCookies = proxyAndRecordSettings.ExcludedCookies ?? new string[] { }; - - var request = Request.Create(); - request.WithPath(requestMessage.Path); - request.UsingMethod(requestMessage.Method); - - requestMessage.Query?.Loop((key, value) => request.WithParam(key, false, value.ToArray())); - requestMessage.Cookies?.Loop((key, value) => - { - if (!excludedCookies.Contains(key, StringComparer.OrdinalIgnoreCase)) - { - request.WithCookie(key, value); - } - }); - - var allExcludedHeaders = new List(excludedHeaders) { "Cookie" }; - requestMessage.Headers?.Loop((key, value) => - { - if (!allExcludedHeaders.Contains(key, StringComparer.OrdinalIgnoreCase)) - { - request.WithHeader(key, value.ToArray()); - } - }); - - bool throwExceptionWhenMatcherFails = _settings.ThrowExceptionWhenMatcherFails == true; - switch (requestMessage.BodyData?.DetectedBodyType) - { - case BodyType.Json: - request.WithBody(new JsonMatcher(MatchBehaviour.AcceptOnMatch, requestMessage.BodyData.BodyAsJson!, true, throwExceptionWhenMatcherFails)); - break; - - case BodyType.String: - request.WithBody(new ExactMatcher(MatchBehaviour.AcceptOnMatch, false, throwExceptionWhenMatcherFails, MatchOperator.Or, requestMessage.BodyData.BodyAsString)); - break; - - case BodyType.Bytes: - request.WithBody(new ExactObjectMatcher(MatchBehaviour.AcceptOnMatch, requestMessage.BodyData.BodyAsBytes, throwExceptionWhenMatcherFails)); - break; - } - - var response = Response.Create(responseMessage); - - return new Mapping - ( - guid: Guid.NewGuid(), - title: $"Proxy Mapping for {requestMessage.Method} {requestMessage.Path}", - description: string.Empty, - path: null, - settings: _settings, - request, - response, - priority: WireMockConstants.ProxyPriority, // This was 0 - scenario: null, - executionConditionState: null, - nextState: null, - stateTimes: null, - webhooks: null, - useWebhooksFireAndForget: null, - timeSettings: null - ); - } } \ No newline at end of file diff --git a/src/WireMock.Net/RequestBuilders/Request.UsingMethods.cs b/src/WireMock.Net/RequestBuilders/Request.UsingMethods.cs index a380c5b56..f5c4d1a44 100644 --- a/src/WireMock.Net/RequestBuilders/Request.UsingMethods.cs +++ b/src/WireMock.Net/RequestBuilders/Request.UsingMethods.cs @@ -1,10 +1,10 @@ // This source file is based on mock4net by Alexandre Victoor which is licensed under the Apache 2.0 License. // For more details see 'mock4net/LICENSE.txt' and 'mock4net/readme.md' in this project root. using System.Linq; -using WireMock.Http; +using Stef.Validation; +using WireMock.Constants; using WireMock.Matchers; using WireMock.Matchers.Request; -using Stef.Validation; namespace WireMock.RequestBuilders; @@ -13,63 +13,63 @@ public partial class Request /// public IRequestBuilder UsingConnect(MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch) { - _requestMatchers.Add(new RequestMessageMethodMatcher(matchBehaviour, MatchOperator.Or, HttpRequestMethods.CONNECT)); + _requestMatchers.Add(new RequestMessageMethodMatcher(matchBehaviour, MatchOperator.Or, HttpRequestMethod.CONNECT)); return this; } /// public IRequestBuilder UsingDelete(MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch) { - _requestMatchers.Add(new RequestMessageMethodMatcher(matchBehaviour, MatchOperator.Or, HttpRequestMethods.DELETE)); + _requestMatchers.Add(new RequestMessageMethodMatcher(matchBehaviour, MatchOperator.Or, HttpRequestMethod.DELETE)); return this; } /// public IRequestBuilder UsingGet(MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch) { - _requestMatchers.Add(new RequestMessageMethodMatcher(matchBehaviour, MatchOperator.Or, HttpRequestMethods.GET)); + _requestMatchers.Add(new RequestMessageMethodMatcher(matchBehaviour, MatchOperator.Or, HttpRequestMethod.GET)); return this; } /// public IRequestBuilder UsingHead(MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch) { - _requestMatchers.Add(new RequestMessageMethodMatcher(matchBehaviour, MatchOperator.Or, HttpRequestMethods.HEAD)); + _requestMatchers.Add(new RequestMessageMethodMatcher(matchBehaviour, MatchOperator.Or, HttpRequestMethod.HEAD)); return this; } /// public IRequestBuilder UsingOptions(MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch) { - _requestMatchers.Add(new RequestMessageMethodMatcher(matchBehaviour, MatchOperator.Or, HttpRequestMethods.OPTIONS)); + _requestMatchers.Add(new RequestMessageMethodMatcher(matchBehaviour, MatchOperator.Or, HttpRequestMethod.OPTIONS)); return this; } /// public IRequestBuilder UsingPost(MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch) { - _requestMatchers.Add(new RequestMessageMethodMatcher(matchBehaviour, MatchOperator.Or, HttpRequestMethods.POST)); + _requestMatchers.Add(new RequestMessageMethodMatcher(matchBehaviour, MatchOperator.Or, HttpRequestMethod.POST)); return this; } /// public IRequestBuilder UsingPatch(MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch) { - _requestMatchers.Add(new RequestMessageMethodMatcher(matchBehaviour, MatchOperator.Or, HttpRequestMethods.PATCH)); + _requestMatchers.Add(new RequestMessageMethodMatcher(matchBehaviour, MatchOperator.Or, HttpRequestMethod.PATCH)); return this; } /// public IRequestBuilder UsingPut(MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch) { - _requestMatchers.Add(new RequestMessageMethodMatcher(matchBehaviour, MatchOperator.Or, HttpRequestMethods.PUT)); + _requestMatchers.Add(new RequestMessageMethodMatcher(matchBehaviour, MatchOperator.Or, HttpRequestMethod.PUT)); return this; } /// public IRequestBuilder UsingTrace(MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch) { - _requestMatchers.Add(new RequestMessageMethodMatcher(matchBehaviour, MatchOperator.Or, HttpRequestMethods.TRACE)); + _requestMatchers.Add(new RequestMessageMethodMatcher(matchBehaviour, MatchOperator.Or, HttpRequestMethod.TRACE)); return this; } diff --git a/src/WireMock.Net/ScenarioState.cs b/src/WireMock.Net/ScenarioState.cs index 2c7f57a82..37e303e69 100644 --- a/src/WireMock.Net/ScenarioState.cs +++ b/src/WireMock.Net/ScenarioState.cs @@ -8,7 +8,7 @@ public class ScenarioState /// /// Gets or sets the Name (from the Scenario). /// - public string? Name { get; set; } + public string Name { get; set; } = null!; /// /// Gets or sets the NextState. diff --git a/src/WireMock.Net/Server/WireMockServer.Admin.cs b/src/WireMock.Net/Server/WireMockServer.Admin.cs index f8399fcad..9e0e04244 100644 --- a/src/WireMock.Net/Server/WireMockServer.Admin.cs +++ b/src/WireMock.Net/Server/WireMockServer.Admin.cs @@ -46,6 +46,8 @@ public partial class WireMockServer private static readonly RegexMatcher AdminRequestContentTypeJson = new ContentTypeMatcher(WireMockConstants.ContentTypeJson, true); private static readonly RegexMatcher AdminMappingsGuidPathMatcher = new(@"^\/__admin\/mappings\/([0-9A-Fa-f]{8}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{12})$"); private static readonly RegexMatcher AdminRequestsGuidPathMatcher = new(@"^\/__admin\/requests\/([0-9A-Fa-f]{8}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{12})$"); + private static readonly RegexMatcher AdminScenariosNameMatcher = new(@"^\/__admin\/scenarios\/.+$"); + private static readonly RegexMatcher AdminScenariosNameWithResetMatcher = new(@"^\/__admin\/scenarios\/.+\/reset$"); private EnhancedFileSystemWatcher? _enhancedFileSystemWatcher; @@ -93,9 +95,11 @@ private void InitAdmin() // __admin/scenarios Given(Request.Create().WithPath(AdminScenarios).UsingGet()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(ScenariosGet)); Given(Request.Create().WithPath(AdminScenarios).UsingDelete()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(ScenariosReset)); + Given(Request.Create().WithPath(AdminScenariosNameMatcher).UsingDelete()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(ScenarioReset)); // __admin/scenarios/reset Given(Request.Create().WithPath(AdminScenarios + "/reset").UsingPost()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(ScenariosReset)); + Given(Request.Create().WithPath(AdminScenariosNameWithResetMatcher).UsingPost()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(ScenarioReset)); // __admin/files/{filename} Given(Request.Create().WithPath(_adminFilesFilenamePathMatcher).UsingPost()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(FilePost)); @@ -610,6 +614,17 @@ private IResponseMessage ScenariosReset(IRequestMessage requestMessage) return ResponseMessageBuilder.Create("Scenarios reset"); } + + private IResponseMessage ScenarioReset(IRequestMessage requestMessage) + { + var name = string.Equals(HttpRequestMethod.DELETE, requestMessage.Method, StringComparison.OrdinalIgnoreCase) ? + requestMessage.Path.Substring(AdminScenarios.Length + 1) : + requestMessage.Path.Split('/').Reverse().Skip(1).First(); + + return ResetScenario(name) ? + ResponseMessageBuilder.Create("Scenario reset") : + ResponseMessageBuilder.Create($"No scenario found by name '{name}'.", 404); + } #endregion #region Pact diff --git a/src/WireMock.Net/Server/WireMockServer.cs b/src/WireMock.Net/Server/WireMockServer.cs index 77efceb3b..085cc0733 100644 --- a/src/WireMock.Net/Server/WireMockServer.cs +++ b/src/WireMock.Net/Server/WireMockServer.cs @@ -477,6 +477,13 @@ public void ResetScenarios() _options.Scenarios.Clear(); } + /// + [PublicAPI] + public bool ResetScenario(string name) + { + return _options.Scenarios.ContainsKey(name) && _options.Scenarios.TryRemove(name, out _); + } + /// [PublicAPI] public IWireMockServer WithMapping(params MappingModel[] mappings) diff --git a/src/WireMock.Net/Transformers/ITransformer.cs b/src/WireMock.Net/Transformers/ITransformer.cs index b1797135e..026f7bbcf 100644 --- a/src/WireMock.Net/Transformers/ITransformer.cs +++ b/src/WireMock.Net/Transformers/ITransformer.cs @@ -10,7 +10,7 @@ interface ITransformer IBodyData? TransformBody(IMapping mapping, IRequestMessage originalRequestMessage, IResponseMessage originalResponseMessage, IBodyData? bodyData, ReplaceNodeOptions options); - IDictionary>? TransformHeaders(IMapping mapping, IRequestMessage originalRequestMessage, IResponseMessage originalResponseMessage, IDictionary>? headers); + IDictionary> TransformHeaders(IMapping mapping, IRequestMessage originalRequestMessage, IResponseMessage originalResponseMessage, IDictionary>? headers); string TransformString(IMapping mapping, IRequestMessage originalRequestMessage, IResponseMessage originalResponseMessage, string? value); } \ No newline at end of file diff --git a/src/WireMock.Net/Transformers/Transformer.cs b/src/WireMock.Net/Transformers/Transformer.cs index d53d4e459..cddf0b3f6 100644 --- a/src/WireMock.Net/Transformers/Transformer.cs +++ b/src/WireMock.Net/Transformers/Transformer.cs @@ -36,7 +36,7 @@ public Transformer(ITransformerContextFactory factory) return newBodyData; } - public IDictionary>? TransformHeaders( + public IDictionary> TransformHeaders( IMapping mapping, IRequestMessage originalRequestMessage, IResponseMessage originalResponseMessage, @@ -239,6 +239,7 @@ private static void WalkNode(ITransformerContext handlebarsContext, ReplaceNodeO } } + // ReSharper disable once UnusedParameter.Local private static void ReplaceNodeValue(ReplaceNodeOptions options, JToken node, string transformedString) { StringUtils.TryParseQuotedString(transformedString, out var result, out _); @@ -270,13 +271,13 @@ private static IBodyData TransformBodyAsString(ITransformerContext handlebarsCon Encoding = original.Encoding, DetectedBodyType = original.DetectedBodyType, DetectedBodyTypeFromContentType = original.DetectedBodyTypeFromContentType, - BodyAsString = handlebarsContext.ParseAndRender(original.BodyAsString, model) + BodyAsString = handlebarsContext.ParseAndRender(original.BodyAsString!, model) }; } private static IBodyData TransformBodyAsFile(ITransformerContext handlebarsContext, object model, IBodyData original, bool useTransformerForBodyAsFile) { - string transformedBodyAsFilename = handlebarsContext.ParseAndRender(original.BodyAsFile, model); + string transformedBodyAsFilename = handlebarsContext.ParseAndRender(original.BodyAsFile!, model); if (!useTransformerForBodyAsFile) { diff --git a/src/WireMock.Net/Util/BodyParser.cs b/src/WireMock.Net/Util/BodyParser.cs index 3ba043916..5ff5a9ddd 100644 --- a/src/WireMock.Net/Util/BodyParser.cs +++ b/src/WireMock.Net/Util/BodyParser.cs @@ -5,7 +5,7 @@ using System.Text; using System.Threading.Tasks; using Stef.Validation; -using WireMock.Http; +using WireMock.Constants; using WireMock.Matchers; using WireMock.Types; @@ -29,15 +29,15 @@ internal static class BodyParser */ private static readonly IDictionary BodyAllowedForMethods = new Dictionary { - { HttpRequestMethods.HEAD, false }, - { HttpRequestMethods.GET, false }, - { HttpRequestMethods.PUT, true }, - { HttpRequestMethods.POST, true }, - { HttpRequestMethods.DELETE, true }, - { HttpRequestMethods.TRACE, false }, - { HttpRequestMethods.OPTIONS, true }, - { HttpRequestMethods.CONNECT, false }, - { HttpRequestMethods.PATCH, true } + { HttpRequestMethod.HEAD, false }, + { HttpRequestMethod.GET, false }, + { HttpRequestMethod.PUT, true }, + { HttpRequestMethod.POST, true }, + { HttpRequestMethod.DELETE, true }, + { HttpRequestMethod.TRACE, false }, + { HttpRequestMethod.OPTIONS, true }, + { HttpRequestMethod.CONNECT, false }, + { HttpRequestMethod.PATCH, true } }; private static readonly IStringMatcher[] MultipartContentTypesMatchers = { @@ -122,7 +122,7 @@ public static async Task ParseAsync(BodyParserSettings settings) // In case of MultiPart: check if the BodyAsBytes is a valid UTF8 or ASCII string, in that case read as String else keep as-is if (data.DetectedBodyTypeFromContentType == BodyType.MultiPart) { - if (BytesEncodingUtils.TryGetEncoding(data.BodyAsBytes, out Encoding encoding) && + if (BytesEncodingUtils.TryGetEncoding(data.BodyAsBytes, out var encoding) && SupportedBodyAsStringEncodingForMultipart.Select(x => x.Equals(encoding)).Any()) { data.BodyAsString = encoding.GetString(data.BodyAsBytes); diff --git a/test/WireMock.Net.Tests/WireMockAdminApiTests.cs b/test/WireMock.Net.Tests/WireMockAdminApiTests.cs index 9fea0a0c3..d3b4ff1c9 100644 --- a/test/WireMock.Net.Tests/WireMockAdminApiTests.cs +++ b/test/WireMock.Net.Tests/WireMockAdminApiTests.cs @@ -603,5 +603,31 @@ public void IWireMockAdminApi_FileExistsAsync_NotFound() server.Stop(); } + + [Fact] + public async Task IWireMockAdminApi_DeleteScenarioUsingDeleteAsync() + { + // Arrange + var name = "x"; + var server = WireMockServer.StartWithAdminInterface(); + var api = RestClient.For(server.Urls[0]); + + // Act + var status = await api.DeleteScenarioAsync(name).ConfigureAwait(false); + status.Status.Should().Be("No scenario found by name 'x'."); + } + + [Fact] + public async Task IWireMockAdminApi_DeleteScenarioUsingPostAsync() + { + // Arrange + var name = "x"; + var server = WireMockServer.StartWithAdminInterface(); + var api = RestClient.For(server.Urls[0]); + + // Act + var status = await api.ResetScenarioAsync(name).ConfigureAwait(false); + status.Status.Should().Be("No scenario found by name 'x'."); + } } #endif \ No newline at end of file diff --git a/test/WireMock.Net.Tests/WireMockServer.Proxy.cs b/test/WireMock.Net.Tests/WireMockServer.Proxy.cs index aa2a03a74..430a9b445 100644 --- a/test/WireMock.Net.Tests/WireMockServer.Proxy.cs +++ b/test/WireMock.Net.Tests/WireMockServer.Proxy.cs @@ -117,7 +117,7 @@ public async Task WireMockServer_Proxy_AdminTrue_With_SaveMapping_Is_True_And_Sa } // Assert - server.Mappings.Should().HaveCount(28); + server.Mappings.Should().HaveCount(30); } [Fact] diff --git a/test/WireMock.Net.Tests/WireMockServer.Settings.cs b/test/WireMock.Net.Tests/WireMockServer.Settings.cs index 07337ac43..fe1d13dcf 100644 --- a/test/WireMock.Net.Tests/WireMockServer.Settings.cs +++ b/test/WireMock.Net.Tests/WireMockServer.Settings.cs @@ -81,7 +81,7 @@ public void WireMockServer_WireMockServerSettings_PriorityFromAllAdminMappingsIs // Assert server.Mappings.Should().NotBeNull(); - server.Mappings.Should().HaveCount(26); + server.Mappings.Should().HaveCount(28); server.Mappings.All(m => m.Priority == WireMockConstants.AdminPriority).Should().BeTrue(); } @@ -100,9 +100,9 @@ public void WireMockServer_WireMockServerSettings_ProxyAndRecordSettings_ProxyPr // Assert server.Mappings.Should().NotBeNull(); - server.Mappings.Should().HaveCount(27); + server.Mappings.Should().HaveCount(29); - server.Mappings.Count(m => m.Priority == WireMockConstants.AdminPriority).Should().Be(26); + server.Mappings.Count(m => m.Priority == WireMockConstants.AdminPriority).Should().Be(28); server.Mappings.Count(m => m.Priority == WireMockConstants.ProxyPriority).Should().Be(1); }