Support deleting / resetting a single scenario (#834)

* Support deleting / resetting a single scenario

* move files
This commit is contained in:
Stef Heyenrath
2022-10-26 08:43:51 +02:00
committed by GitHub
parent b4c32dd66b
commit 31298d281d
27 changed files with 186 additions and 179 deletions

View File

@@ -1,10 +1,18 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation"> <wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=AD/@EntryIndexedValue">AD</s:String> <s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=AD/@EntryIndexedValue">AD</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=CONNECT/@EntryIndexedValue">CONNECT</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=CS/@EntryIndexedValue">CS</s:String> <s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=CS/@EntryIndexedValue">CS</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=DELETE/@EntryIndexedValue">DELETE</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=EC/@EntryIndexedValue">EC</s:String> <s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=EC/@EntryIndexedValue">EC</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=GET/@EntryIndexedValue">GET</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=HEAD/@EntryIndexedValue">HEAD</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=ID/@EntryIndexedValue">ID</s:String> <s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=ID/@EntryIndexedValue">ID</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=IP/@EntryIndexedValue">IP</s:String> <s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=IP/@EntryIndexedValue">IP</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=MD/@EntryIndexedValue">MD5</s:String> <s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=MD/@EntryIndexedValue">MD5</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=OPTIONS/@EntryIndexedValue">OPTIONS</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=PATCH/@EntryIndexedValue">PATCH</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=POST/@EntryIndexedValue">POST</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=PUT/@EntryIndexedValue">PUT</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=RSA/@EntryIndexedValue">RSA</s:String> <s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=RSA/@EntryIndexedValue">RSA</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=SSL/@EntryIndexedValue">SSL</s:String> <s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=SSL/@EntryIndexedValue">SSL</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=TE/@EntryIndexedValue">TE</s:String> <s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=TE/@EntryIndexedValue">TE</s:String>

View File

@@ -1,9 +1,9 @@
namespace WireMock.Http; namespace WireMock.Constants;
/// <summary> /// <summary>
/// https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods /// https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods
/// </summary> /// </summary>
internal static class HttpRequestMethods public static class HttpRequestMethod
{ {
public const string CONNECT = "CONNECT"; public const string CONNECT = "CONNECT";
public const string DELETE = "DELETE"; public const string DELETE = "DELETE";

View File

@@ -1,10 +1,8 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Specialized; using System.Collections.Specialized;
using JetBrains.Annotations;
using WireMock.Admin.Mappings; using WireMock.Admin.Mappings;
using WireMock.Logging; using WireMock.Logging;
using WireMock.Matchers.Request;
namespace WireMock.Server namespace WireMock.Server
{ {
@@ -144,6 +142,11 @@ namespace WireMock.Server
/// </summary> /// </summary>
void ResetScenarios(); void ResetScenarios();
/// <summary>
/// Resets a specific Scenario by the name.
/// </summary>
bool ResetScenario(string name);
/// <summary> /// <summary>
/// Resets the LogEntries. /// Resets the LogEntries.
/// </summary> /// </summary>

View File

@@ -4,6 +4,7 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using FluentAssertions; using FluentAssertions;
using FluentAssertions.Execution; using FluentAssertions.Execution;
using WireMock.Constants;
using WireMock.Server; using WireMock.Server;
using WireMock.Types; using WireMock.Types;
@@ -169,39 +170,39 @@ public class WireMockAssertions
[CustomAssertion] [CustomAssertion]
public AndConstraint<WireMockAssertions> UsingConnect(string because = "", params object[] becauseArgs) public AndConstraint<WireMockAssertions> UsingConnect(string because = "", params object[] becauseArgs)
=> UsingMethod("CONNECT", because, becauseArgs); => UsingMethod(HttpRequestMethod.CONNECT, because, becauseArgs);
[CustomAssertion] [CustomAssertion]
public AndConstraint<WireMockAssertions> UsingDelete(string because = "", params object[] becauseArgs) public AndConstraint<WireMockAssertions> UsingDelete(string because = "", params object[] becauseArgs)
=> UsingMethod("DELETE", because, becauseArgs); => UsingMethod(HttpRequestMethod.DELETE, because, becauseArgs);
[CustomAssertion] [CustomAssertion]
public AndConstraint<WireMockAssertions> UsingGet(string because = "", params object[] becauseArgs) public AndConstraint<WireMockAssertions> UsingGet(string because = "", params object[] becauseArgs)
=> UsingMethod("GET", because, becauseArgs); => UsingMethod(HttpRequestMethod.GET, because, becauseArgs);
[CustomAssertion] [CustomAssertion]
public AndConstraint<WireMockAssertions> UsingHead(string because = "", params object[] becauseArgs) public AndConstraint<WireMockAssertions> UsingHead(string because = "", params object[] becauseArgs)
=> UsingMethod("HEAD", because, becauseArgs); => UsingMethod(HttpRequestMethod.HEAD, because, becauseArgs);
[CustomAssertion] [CustomAssertion]
public AndConstraint<WireMockAssertions> UsingOptions(string because = "", params object[] becauseArgs) public AndConstraint<WireMockAssertions> UsingOptions(string because = "", params object[] becauseArgs)
=> UsingMethod("OPTIONS", because, becauseArgs); => UsingMethod(HttpRequestMethod.OPTIONS, because, becauseArgs);
[CustomAssertion] [CustomAssertion]
public AndConstraint<WireMockAssertions> UsingPost(string because = "", params object[] becauseArgs) public AndConstraint<WireMockAssertions> UsingPost(string because = "", params object[] becauseArgs)
=> UsingMethod("POST", because, becauseArgs); => UsingMethod(HttpRequestMethod.POST, because, becauseArgs);
[CustomAssertion] [CustomAssertion]
public AndConstraint<WireMockAssertions> UsingPatch(string because = "", params object[] becauseArgs) public AndConstraint<WireMockAssertions> UsingPatch(string because = "", params object[] becauseArgs)
=> UsingMethod("PATCH", because, becauseArgs); => UsingMethod(HttpRequestMethod.PATCH, because, becauseArgs);
[CustomAssertion] [CustomAssertion]
public AndConstraint<WireMockAssertions> UsingPut(string because = "", params object[] becauseArgs) public AndConstraint<WireMockAssertions> UsingPut(string because = "", params object[] becauseArgs)
=> UsingMethod("PUT", because, becauseArgs); => UsingMethod(HttpRequestMethod.PUT, because, becauseArgs);
[CustomAssertion] [CustomAssertion]
public AndConstraint<WireMockAssertions> UsingTrace(string because = "", params object[] becauseArgs) public AndConstraint<WireMockAssertions> UsingTrace(string because = "", params object[] becauseArgs)
=> UsingMethod("TRACE", because, becauseArgs); => UsingMethod(HttpRequestMethod.TRACE, because, becauseArgs);
[CustomAssertion] [CustomAssertion]
public AndConstraint<WireMockAssertions> UsingAnyMethod(string because = "", params object[] becauseArgs) public AndConstraint<WireMockAssertions> UsingAnyMethod(string because = "", params object[] becauseArgs)

View File

@@ -1,4 +1,4 @@
using RestEase; using RestEase;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Net.Http.Headers; using System.Net.Http.Headers;
@@ -179,6 +179,20 @@ namespace WireMock.Client
[Post("scenarios")] [Post("scenarios")]
Task<StatusModel> ResetScenariosAsync(); Task<StatusModel> ResetScenariosAsync();
/// <summary>
/// Delete (reset) a specific scenario
/// </summary>
[Delete("scenarios/{name}")]
[AllowAnyStatusCode]
Task<StatusModel> DeleteScenarioAsync([Path] string name);
/// <summary>
/// Delete (reset) all scenarios
/// </summary>
[Post("scenarios/{name}/reset")]
[AllowAnyStatusCode]
Task<StatusModel> ResetScenarioAsync([Path] string name);
/// <summary> /// <summary>
/// Create a new File /// Create a new File
/// </summary> /// </summary>

View File

@@ -26,7 +26,7 @@ internal class WireMockMiddlewareOptions : IWireMockMiddlewareOptions
public ConcurrentDictionary<Guid, IMapping> Mappings { get; } = new ConcurrentDictionary<Guid, IMapping>(); public ConcurrentDictionary<Guid, IMapping> Mappings { get; } = new ConcurrentDictionary<Guid, IMapping>();
public ConcurrentDictionary<string, ScenarioState> Scenarios { get; } = new(); public ConcurrentDictionary<string, ScenarioState> Scenarios { get; } = new(StringComparer.OrdinalIgnoreCase);
public ConcurrentObservableCollection<LogEntry> LogEntries { get; } = new(); public ConcurrentObservableCollection<LogEntry> LogEntries { get; } = new();

View File

@@ -1,3 +1,4 @@
#pragma warning disable CS1591
namespace WireMock.Pact.Models.V2; namespace WireMock.Pact.Models.V2;
public class Interaction public class Interaction

View File

@@ -1,25 +1,25 @@
namespace WireMock.Pact.Models.V2 #pragma warning disable CS1591
namespace WireMock.Pact.Models.V2;
public class MatchingRule
{ {
public class MatchingRule /// <summary>
{ /// type or regex
/// <summary> /// </summary>
/// type or regex public string Match { get; set; } = "type";
/// </summary>
public string Match { get; set; } = "type";
/// <summary> /// <summary>
/// Used for Match = "type" /// Used for Match = "type"
/// </summary> /// </summary>
public string Min { get; set; } public string Min { get; set; }
/// <summary> /// <summary>
/// Used for Match = "type" /// Used for Match = "type"
/// </summary> /// </summary>
public string Max { get; set; } public string Max { get; set; }
/// <summary> /// <summary>
/// Used for Match = "regex" /// Used for Match = "regex"
/// </summary> /// </summary>
public string Regex { get; set; } public string Regex { get; set; }
}
} }

View File

@@ -1,9 +1,9 @@
namespace WireMock.Pact.Models.V2 #pragma warning disable CS1591
{ namespace WireMock.Pact.Models.V2;
public class Metadata
{
public string PactSpecificationVersion { get; set; }
public PactSpecification PactSpecification { get; set; } = new PactSpecification(); public class Metadata
} {
public string PactSpecificationVersion { get; set; }
public PactSpecification PactSpecification { get; set; } = new PactSpecification();
} }

View File

@@ -1,15 +1,15 @@
#pragma warning disable CS1591
using System.Collections.Generic; 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<Interaction> Interactions { get; set; } = new List<Interaction>(); public List<Interaction> Interactions { get; set; } = new List<Interaction>();
public Metadata Metadata { get; set; } public Metadata Metadata { get; set; }
public Pacticipant Provider { get; set; } public Pacticipant Provider { get; set; }
}
} }

View File

@@ -1,4 +1,6 @@
#pragma warning disable CS1591
using System.Collections.Generic; using System.Collections.Generic;
using WireMock.Constants;
namespace WireMock.Pact.Models.V2; namespace WireMock.Pact.Models.V2;
@@ -6,7 +8,7 @@ public class PactRequest
{ {
public IDictionary<string, string>? Headers { get; set; } public IDictionary<string, string>? Headers { get; set; }
public string Method { get; set; } = "GET"; public string Method { get; set; } = HttpRequestMethod.GET;
public string? Path { get; set; } = "/"; public string? Path { get; set; } = "/";

View File

@@ -1,3 +1,4 @@
#pragma warning disable CS1591
using System.Collections.Generic; using System.Collections.Generic;
namespace WireMock.Pact.Models.V2; namespace WireMock.Pact.Models.V2;

View File

@@ -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; }
}
} }

View File

@@ -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";
}
} }

View File

@@ -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; }
}
} }

View File

@@ -1,11 +1,11 @@
#pragma warning disable CS1591
using System.Collections.Generic; using System.Collections.Generic;
namespace WireMock.Pact.Models.V2 namespace WireMock.Pact.Models.V2;
{
public class ProviderState
{
public string Name { get; set; }
public IDictionary<string, string> Params { get; set; } public class ProviderState
} {
public string Name { get; set; }
public IDictionary<string, string> Params { get; set; }
} }

View File

@@ -1,17 +1,10 @@
using Stef.Validation;
using System; using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http; using System.Net.Http;
using System.Threading.Tasks; using System.Threading.Tasks;
using WireMock.Constants; using Stef.Validation;
using WireMock.Http; using WireMock.Http;
using WireMock.Matchers;
using WireMock.RequestBuilders;
using WireMock.ResponseBuilders;
using WireMock.Serialization; using WireMock.Serialization;
using WireMock.Settings; using WireMock.Settings;
using WireMock.Types;
using WireMock.Util; using WireMock.Util;
namespace WireMock.Proxy; namespace WireMock.Proxy;
@@ -62,69 +55,4 @@ internal class ProxyHelper
return (responseMessage, newMapping); 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<string>(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
);
}
} }

View File

@@ -1,10 +1,10 @@
// This source file is based on mock4net by Alexandre Victoor which is licensed under the Apache 2.0 License. // 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. // For more details see 'mock4net/LICENSE.txt' and 'mock4net/readme.md' in this project root.
using System.Linq; using System.Linq;
using WireMock.Http; using Stef.Validation;
using WireMock.Constants;
using WireMock.Matchers; using WireMock.Matchers;
using WireMock.Matchers.Request; using WireMock.Matchers.Request;
using Stef.Validation;
namespace WireMock.RequestBuilders; namespace WireMock.RequestBuilders;
@@ -13,63 +13,63 @@ public partial class Request
/// <inheritdoc /> /// <inheritdoc />
public IRequestBuilder UsingConnect(MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch) 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; return this;
} }
/// <inheritdoc /> /// <inheritdoc />
public IRequestBuilder UsingDelete(MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch) 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; return this;
} }
/// <inheritdoc /> /// <inheritdoc />
public IRequestBuilder UsingGet(MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch) 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; return this;
} }
/// <inheritdoc /> /// <inheritdoc />
public IRequestBuilder UsingHead(MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch) 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; return this;
} }
/// <inheritdoc /> /// <inheritdoc />
public IRequestBuilder UsingOptions(MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch) 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; return this;
} }
/// <inheritdoc /> /// <inheritdoc />
public IRequestBuilder UsingPost(MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch) 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; return this;
} }
/// <inheritdoc /> /// <inheritdoc />
public IRequestBuilder UsingPatch(MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch) 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; return this;
} }
/// <inheritdoc /> /// <inheritdoc />
public IRequestBuilder UsingPut(MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch) 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; return this;
} }
/// <inheritdoc /> /// <inheritdoc />
public IRequestBuilder UsingTrace(MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch) 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; return this;
} }

View File

@@ -8,7 +8,7 @@ public class ScenarioState
/// <summary> /// <summary>
/// Gets or sets the Name (from the Scenario). /// Gets or sets the Name (from the Scenario).
/// </summary> /// </summary>
public string? Name { get; set; } public string Name { get; set; } = null!;
/// <summary> /// <summary>
/// Gets or sets the NextState. /// Gets or sets the NextState.

View File

@@ -46,6 +46,8 @@ public partial class WireMockServer
private static readonly RegexMatcher AdminRequestContentTypeJson = new ContentTypeMatcher(WireMockConstants.ContentTypeJson, true); 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 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 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; private EnhancedFileSystemWatcher? _enhancedFileSystemWatcher;
@@ -93,9 +95,11 @@ public partial class WireMockServer
// __admin/scenarios // __admin/scenarios
Given(Request.Create().WithPath(AdminScenarios).UsingGet()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(ScenariosGet)); 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(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 // __admin/scenarios/reset
Given(Request.Create().WithPath(AdminScenarios + "/reset").UsingPost()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(ScenariosReset)); 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} // __admin/files/{filename}
Given(Request.Create().WithPath(_adminFilesFilenamePathMatcher).UsingPost()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(FilePost)); Given(Request.Create().WithPath(_adminFilesFilenamePathMatcher).UsingPost()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(FilePost));
@@ -610,6 +614,17 @@ public partial class WireMockServer
return ResponseMessageBuilder.Create("Scenarios reset"); 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 #endregion
#region Pact #region Pact

View File

@@ -477,6 +477,13 @@ public partial class WireMockServer : IWireMockServer
_options.Scenarios.Clear(); _options.Scenarios.Clear();
} }
/// <inheritdoc />
[PublicAPI]
public bool ResetScenario(string name)
{
return _options.Scenarios.ContainsKey(name) && _options.Scenarios.TryRemove(name, out _);
}
/// <inheritdoc cref="IWireMockServer.WithMapping(MappingModel[])" /> /// <inheritdoc cref="IWireMockServer.WithMapping(MappingModel[])" />
[PublicAPI] [PublicAPI]
public IWireMockServer WithMapping(params MappingModel[] mappings) public IWireMockServer WithMapping(params MappingModel[] mappings)

View File

@@ -10,7 +10,7 @@ interface ITransformer
IBodyData? TransformBody(IMapping mapping, IRequestMessage originalRequestMessage, IResponseMessage originalResponseMessage, IBodyData? bodyData, ReplaceNodeOptions options); IBodyData? TransformBody(IMapping mapping, IRequestMessage originalRequestMessage, IResponseMessage originalResponseMessage, IBodyData? bodyData, ReplaceNodeOptions options);
IDictionary<string, WireMockList<string>>? TransformHeaders(IMapping mapping, IRequestMessage originalRequestMessage, IResponseMessage originalResponseMessage, IDictionary<string, WireMockList<string>>? headers); IDictionary<string, WireMockList<string>> TransformHeaders(IMapping mapping, IRequestMessage originalRequestMessage, IResponseMessage originalResponseMessage, IDictionary<string, WireMockList<string>>? headers);
string TransformString(IMapping mapping, IRequestMessage originalRequestMessage, IResponseMessage originalResponseMessage, string? value); string TransformString(IMapping mapping, IRequestMessage originalRequestMessage, IResponseMessage originalResponseMessage, string? value);
} }

View File

@@ -36,7 +36,7 @@ internal class Transformer : ITransformer
return newBodyData; return newBodyData;
} }
public IDictionary<string, WireMockList<string>>? TransformHeaders( public IDictionary<string, WireMockList<string>> TransformHeaders(
IMapping mapping, IMapping mapping,
IRequestMessage originalRequestMessage, IRequestMessage originalRequestMessage,
IResponseMessage originalResponseMessage, IResponseMessage originalResponseMessage,
@@ -239,6 +239,7 @@ internal class Transformer : ITransformer
} }
} }
// ReSharper disable once UnusedParameter.Local
private static void ReplaceNodeValue(ReplaceNodeOptions options, JToken node, string transformedString) private static void ReplaceNodeValue(ReplaceNodeOptions options, JToken node, string transformedString)
{ {
StringUtils.TryParseQuotedString(transformedString, out var result, out _); StringUtils.TryParseQuotedString(transformedString, out var result, out _);
@@ -270,13 +271,13 @@ internal class Transformer : ITransformer
Encoding = original.Encoding, Encoding = original.Encoding,
DetectedBodyType = original.DetectedBodyType, DetectedBodyType = original.DetectedBodyType,
DetectedBodyTypeFromContentType = original.DetectedBodyTypeFromContentType, 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) 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) if (!useTransformerForBodyAsFile)
{ {

View File

@@ -5,7 +5,7 @@ using System.Net.Http.Headers;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using Stef.Validation; using Stef.Validation;
using WireMock.Http; using WireMock.Constants;
using WireMock.Matchers; using WireMock.Matchers;
using WireMock.Types; using WireMock.Types;
@@ -29,15 +29,15 @@ internal static class BodyParser
*/ */
private static readonly IDictionary<string, bool> BodyAllowedForMethods = new Dictionary<string, bool> private static readonly IDictionary<string, bool> BodyAllowedForMethods = new Dictionary<string, bool>
{ {
{ HttpRequestMethods.HEAD, false }, { HttpRequestMethod.HEAD, false },
{ HttpRequestMethods.GET, false }, { HttpRequestMethod.GET, false },
{ HttpRequestMethods.PUT, true }, { HttpRequestMethod.PUT, true },
{ HttpRequestMethods.POST, true }, { HttpRequestMethod.POST, true },
{ HttpRequestMethods.DELETE, true }, { HttpRequestMethod.DELETE, true },
{ HttpRequestMethods.TRACE, false }, { HttpRequestMethod.TRACE, false },
{ HttpRequestMethods.OPTIONS, true }, { HttpRequestMethod.OPTIONS, true },
{ HttpRequestMethods.CONNECT, false }, { HttpRequestMethod.CONNECT, false },
{ HttpRequestMethods.PATCH, true } { HttpRequestMethod.PATCH, true }
}; };
private static readonly IStringMatcher[] MultipartContentTypesMatchers = { private static readonly IStringMatcher[] MultipartContentTypesMatchers = {
@@ -122,7 +122,7 @@ internal static class BodyParser
// 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 // 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 (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()) SupportedBodyAsStringEncodingForMultipart.Select(x => x.Equals(encoding)).Any())
{ {
data.BodyAsString = encoding.GetString(data.BodyAsBytes); data.BodyAsString = encoding.GetString(data.BodyAsBytes);

View File

@@ -603,5 +603,31 @@ public class WireMockAdminApiTests
server.Stop(); server.Stop();
} }
[Fact]
public async Task IWireMockAdminApi_DeleteScenarioUsingDeleteAsync()
{
// Arrange
var name = "x";
var server = WireMockServer.StartWithAdminInterface();
var api = RestClient.For<IWireMockAdminApi>(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<IWireMockAdminApi>(server.Urls[0]);
// Act
var status = await api.ResetScenarioAsync(name).ConfigureAwait(false);
status.Status.Should().Be("No scenario found by name 'x'.");
}
} }
#endif #endif

View File

@@ -117,7 +117,7 @@ public class WireMockServerProxyTests
} }
// Assert // Assert
server.Mappings.Should().HaveCount(28); server.Mappings.Should().HaveCount(30);
} }
[Fact] [Fact]

View File

@@ -81,7 +81,7 @@ namespace WireMock.Net.Tests
// Assert // Assert
server.Mappings.Should().NotBeNull(); server.Mappings.Should().NotBeNull();
server.Mappings.Should().HaveCount(26); server.Mappings.Should().HaveCount(28);
server.Mappings.All(m => m.Priority == WireMockConstants.AdminPriority).Should().BeTrue(); server.Mappings.All(m => m.Priority == WireMockConstants.AdminPriority).Should().BeTrue();
} }
@@ -100,9 +100,9 @@ namespace WireMock.Net.Tests
// Assert // Assert
server.Mappings.Should().NotBeNull(); 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); server.Mappings.Count(m => m.Priority == WireMockConstants.ProxyPriority).Should().Be(1);
} }