mirror of
https://github.com/wiremock/WireMock.Net.git
synced 2026-05-11 17:39:45 +02:00
Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1806ae39f8 | ||
|
|
67acdcf1d3 | ||
|
|
4bb378bdce | ||
|
|
f9df0d0ee9 | ||
|
|
f8d3b51fbc | ||
|
|
be9864461d | ||
|
|
4bb7e8af45 | ||
|
|
8bf42904ab | ||
|
|
0a48b40021 | ||
|
|
1962437dcd |
15
CHANGELOG.md
15
CHANGELOG.md
@@ -1,3 +1,18 @@
|
||||
# 2.6.0 (11 May 2026)
|
||||
- [#1455](https://github.com/wiremock/WireMock.Net/pull/1455) - Fix request storing when RequestLogExpirationDuration is set [bug] [bug] contributed by [pbenko-xitaso](https://github.com/pbenko-xitaso)
|
||||
- [#1454](https://github.com/wiremock/WireMock.Net/issues/1454) - No requests stored in Standalone when RequestLogExpirationDuration is set [bug]
|
||||
|
||||
# 2.5.0 (04 May 2026)
|
||||
- [#1451](https://github.com/wiremock/WireMock.Net/pull/1451) - Feature/early mismatch [feature] contributed by [Stepami](https://github.com/Stepami)
|
||||
- [#1452](https://github.com/wiremock/WireMock.Net/pull/1452) - Bump log4net from 2.0.15 to 3.3.0 in example console app [dependencies, .NET] contributed by [dependabot[bot]](https://github.com/apps/dependabot)
|
||||
- [#1453](https://github.com/wiremock/WireMock.Net/pull/1453) - Fix CVE-2026-40021: upgrade log4net to 3.3.0 in examples/WireMock.Net.Service/packages.config [dependencies] contributed by [Copilot](https://github.com/apps/copilot-swe-agent)
|
||||
- [#1442](https://github.com/wiremock/WireMock.Net/issues/1442) - Bug: [grpc] WithBodyAsProtoBuf exception on match [bug]
|
||||
|
||||
# 2.4.0 (24 April 2026)
|
||||
- [#1437](https://github.com/wiremock/WireMock.Net/pull/1437) - Added feature to enable and disable mappings [feature] contributed by [jayaraman-venkatesan](https://github.com/jayaraman-venkatesan)
|
||||
- [#1450](https://github.com/wiremock/WireMock.Net/pull/1450) - Bump OpenTelemetry.Exporter.OpenTelemetryProtocol from 1.14.0 to 1.15.x [dependencies, .NET] contributed by [dependabot[bot]](https://github.com/apps/dependabot)
|
||||
- [#1421](https://github.com/wiremock/WireMock.Net/issues/1421) - Deactivate mapping without deleting it [feature]
|
||||
|
||||
# 2.3.0 (20 April 2026)
|
||||
- [#1436](https://github.com/wiremock/WireMock.Net/pull/1436) - Moving Scenario state change before global response delay is set [feature] contributed by [jayaraman-venkatesan](https://github.com/jayaraman-venkatesan)
|
||||
- [#1440](https://github.com/wiremock/WireMock.Net/pull/1440) - Bump log4net from 2.0.15 to 3.3.0 [dependencies] contributed by [dependabot[bot]](https://github.com/apps/dependabot)
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<VersionPrefix>2.3.0</VersionPrefix>
|
||||
<VersionPrefix>2.6.0</VersionPrefix>
|
||||
<PackageIcon>WireMock.Net-Logo.png</PackageIcon>
|
||||
<PackageProjectUrl>https://github.com/wiremock/WireMock.Net</PackageProjectUrl>
|
||||
<PackageLicenseExpression>Apache-2.0</PackageLicenseExpression>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
rem https://github.com/StefH/GitHubReleaseNotes
|
||||
|
||||
SET version=2.3.0
|
||||
SET version=2.6.0
|
||||
|
||||
GitHubReleaseNotes --output CHANGELOG.md --skip-empty-releases --exclude-labels wontfix test question invalid doc duplicate example environment --version %version% --token %GH_TOKEN%
|
||||
|
||||
|
||||
@@ -1,12 +1,5 @@
|
||||
# 2.3.0 (20 April 2026)
|
||||
- #1436 Moving Scenario state change before global response delay is set [feature]
|
||||
- #1440 Bump log4net from 2.0.15 to 3.3.0 [dependencies]
|
||||
- #1443 Fix ExactMatcher and JsonMatcher not working for ISO dates as string [bug]
|
||||
- #1444 Update instructions.md [refactor]
|
||||
- #1448 Use DefaultJsonSerializer for BodyAsJson-Response [bug]
|
||||
- #1205 System.Private.Uri 4.3.0 Blackduck security High vulnerability [bug]
|
||||
- #1260 Scenario state change before a response delay timeout ends [bug]
|
||||
- #1441 ExactMatcher and JsonMatcher not working for ISO dates in v2.2.0 [bug]
|
||||
- #1446 WithBodyAsJson does not return correctly formatted json when using SystemTextJsonConverter [bug]
|
||||
# 2.6.0 (11 May 2026)
|
||||
- #1455 Fix request storing when RequestLogExpirationDuration is set [bug] [bug]
|
||||
- #1454 No requests stored in Standalone when RequestLogExpirationDuration is set [bug]
|
||||
|
||||
The full release notes can be found here: https://github.com/wiremock/WireMock.Net/blob/master/CHANGELOG.md
|
||||
@@ -12,11 +12,11 @@
|
||||
|
||||
<PackageReference Include="Microsoft.Extensions.Http.Resilience" Version="8.4.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.ServiceDiscovery" Version="8.0.0" />
|
||||
<PackageReference Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.8.1" />
|
||||
<PackageReference Include="OpenTelemetry.Extensions.Hosting" Version="1.8.1" />
|
||||
<PackageReference Include="OpenTelemetry.Instrumentation.AspNetCore" Version="1.8.1" />
|
||||
<PackageReference Include="OpenTelemetry.Instrumentation.Http" Version="1.8.1" />
|
||||
<PackageReference Include="OpenTelemetry.Instrumentation.Runtime" Version="1.8.0" />
|
||||
<PackageReference Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.15.3" />
|
||||
<PackageReference Include="OpenTelemetry.Extensions.Hosting" Version="1.15.3" />
|
||||
<PackageReference Include="OpenTelemetry.Instrumentation.AspNetCore" Version="1.15.2" />
|
||||
<PackageReference Include="OpenTelemetry.Instrumentation.Http" Version="1.15.1" />
|
||||
<PackageReference Include="OpenTelemetry.Instrumentation.Runtime" Version="1.15.1" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\src\WireMock.Net\WireMock.Net.csproj" />
|
||||
<PackageReference Include="log4net" Version="2.0.15" />
|
||||
<PackageReference Include="log4net" Version="3.3.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration" Version="8.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
@@ -8,9 +8,9 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="OpenTelemetry.Exporter.Console" Version="1.14.0" />
|
||||
<PackageReference Include="OpenTelemetry.Instrumentation.Http" Version="1.14.0" />
|
||||
<PackageReference Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.14.0" />
|
||||
<PackageReference Include="OpenTelemetry.Exporter.Console" Version="1.15.3" />
|
||||
<PackageReference Include="OpenTelemetry.Instrumentation.Http" Version="1.15.1" />
|
||||
<PackageReference Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.15.3" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="log4net" version="2.0.15" targetFramework="net48" />
|
||||
<package id="log4net" version="3.3.0" targetFramework="net48" />
|
||||
<package id="Microsoft.AspNet.WebApi.Client" version="5.2.3" targetFramework="net452" />
|
||||
<package id="Microsoft.AspNet.WebApi.Core" version="5.2.3" targetFramework="net452" />
|
||||
<package id="Microsoft.AspNet.WebApi.Owin" version="5.2.3" targetFramework="net452" />
|
||||
|
||||
@@ -55,12 +55,17 @@ public class MappingModel
|
||||
/// In case the value is null state will not be changed.
|
||||
/// </summary>
|
||||
public string? SetStateTo { get; set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// The number of times this match should be matched before the state will be changed to the specified one.
|
||||
/// </summary>
|
||||
public int? TimesInSameState { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Value to determine if the mapping is disabled. Defaults to <c>null</c> (not disabled).
|
||||
/// </summary>
|
||||
public bool? IsDisabled { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The request model.
|
||||
/// </summary>
|
||||
@@ -100,7 +105,7 @@ public class MappingModel
|
||||
/// </summary>
|
||||
public object? Data { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// <summary>
|
||||
/// The probability when this request should be matched. Value is between 0 and 1. [Optional]
|
||||
/// </summary>
|
||||
public double? Probability { get; set; }
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using WireMock.Matchers.Request;
|
||||
|
||||
namespace WireMock.Admin.Mappings;
|
||||
|
||||
/// <summary>
|
||||
@@ -61,9 +63,15 @@ public class RequestModel
|
||||
/// Gets or sets the Params.
|
||||
/// </summary>
|
||||
public IList<ParamModel>? Params { get; set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the body.
|
||||
/// </summary>
|
||||
public BodyModel? Body { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Type of the request matcher to return an immediate mismatch during mapping processing.
|
||||
/// Optional.
|
||||
/// </summary>
|
||||
public RequestMatcherType? EarlyMatcherType { get; set; }
|
||||
}
|
||||
@@ -7,6 +7,11 @@ namespace WireMock.Matchers.Request;
|
||||
/// </summary>
|
||||
public interface IRequestMatcher
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the request matcher's type.
|
||||
/// </summary>
|
||||
public RequestMatcherType Type { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified RequestMessage is match.
|
||||
/// </summary>
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
namespace WireMock.Matchers.Request;
|
||||
|
||||
/// <summary>
|
||||
/// List of predefined request matcher types.
|
||||
/// </summary>
|
||||
public enum RequestMatcherType
|
||||
{
|
||||
/// <summary>
|
||||
/// RequestMessageBodyMatcher
|
||||
/// </summary>
|
||||
Body = 0,
|
||||
|
||||
/// <summary>
|
||||
/// RequestMessageBodyMatcher{T}
|
||||
/// </summary>
|
||||
BodyOfT = 1,
|
||||
|
||||
/// <summary>
|
||||
/// RequestMessageClientIPMatcher
|
||||
/// </summary>
|
||||
ClientIP = 2,
|
||||
|
||||
/// <summary>
|
||||
/// RequestMessageCookieMatcher
|
||||
/// </summary>
|
||||
Cookie = 3,
|
||||
|
||||
/// <summary>
|
||||
/// RequestMessageGraphQLMatcher
|
||||
/// </summary>
|
||||
GraphQL = 4,
|
||||
|
||||
/// <summary>
|
||||
/// RequestMessageHeaderMatcher
|
||||
/// </summary>
|
||||
Header = 5,
|
||||
|
||||
/// <summary>
|
||||
/// RequestMessageHttpVersionMatcher
|
||||
/// </summary>
|
||||
HttpVersion = 6,
|
||||
|
||||
/// <summary>
|
||||
/// RequestMessageMethodMatcher
|
||||
/// </summary>
|
||||
Method = 7,
|
||||
|
||||
/// <summary>
|
||||
/// RequestMessageMultiPartMatcher
|
||||
/// </summary>
|
||||
MultiPart = 8,
|
||||
|
||||
/// <summary>
|
||||
/// RequestMessageParamMatcher
|
||||
/// </summary>
|
||||
Param = 9,
|
||||
|
||||
/// <summary>
|
||||
/// RequestMessagePathMatcher
|
||||
/// </summary>
|
||||
Path = 10,
|
||||
|
||||
/// <summary>
|
||||
/// RequestMessageProtoBufMatcher
|
||||
/// </summary>
|
||||
ProtoBuf = 11,
|
||||
|
||||
/// <summary>
|
||||
/// RequestMessageScenarioAndStateMatcher
|
||||
/// </summary>
|
||||
ScenarioAndState = 12,
|
||||
|
||||
/// <summary>
|
||||
/// RequestMessageUrlMatcher
|
||||
/// </summary>
|
||||
Url = 13,
|
||||
|
||||
/// <summary>
|
||||
/// RequestMessageCompositeMatcher
|
||||
/// </summary>
|
||||
Composite = 14
|
||||
}
|
||||
16
src/WireMock.Net.Minimal/IResponseMessageBuilder.cs
Normal file
16
src/WireMock.Net.Minimal/IResponseMessageBuilder.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System.Net;
|
||||
|
||||
namespace WireMock;
|
||||
|
||||
internal interface IResponseMessageBuilder
|
||||
{
|
||||
ResponseMessage Create(HttpStatusCode statusCode, string? status, Guid? guid = null);
|
||||
|
||||
ResponseMessage Create(int statusCode, string? status, Guid? guid = null);
|
||||
|
||||
ResponseMessage Create(int statusCode, string? status, string? error, Guid? guid = null);
|
||||
|
||||
ResponseMessage Create(HttpStatusCode statusCode);
|
||||
}
|
||||
@@ -62,6 +62,9 @@ public class Mapping : IMapping
|
||||
/// <inheritdoc />
|
||||
public bool IsProxy => Provider is ProxyAsyncResponseProvider;
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IsDisabled { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool LogMapping => Provider is not (DynamicResponseProvider or DynamicAsyncResponseProvider);
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@ public class MappingBuilder : IMappingBuilder
|
||||
private readonly MappingToFileSaver _mappingToFileSaver;
|
||||
private readonly IGuidUtils _guidUtils;
|
||||
private readonly IDateTimeUtils _dateTimeUtils;
|
||||
private readonly IResponseMessageBuilder _responseMessageBuilder;
|
||||
|
||||
/// <summary>
|
||||
/// Create a MappingBuilder
|
||||
@@ -43,6 +44,7 @@ public class MappingBuilder : IMappingBuilder
|
||||
|
||||
_guidUtils = new GuidUtils();
|
||||
_dateTimeUtils = new DateTimeUtils();
|
||||
_responseMessageBuilder = new ResponseMessageBuilder(_dateTimeUtils);
|
||||
}
|
||||
|
||||
internal MappingBuilder(
|
||||
@@ -51,7 +53,8 @@ public class MappingBuilder : IMappingBuilder
|
||||
MappingConverter mappingConverter,
|
||||
MappingToFileSaver mappingToFileSaver,
|
||||
IGuidUtils guidUtils,
|
||||
IDateTimeUtils dateTimeUtils
|
||||
IDateTimeUtils dateTimeUtils,
|
||||
IResponseMessageBuilder responseMessageBuilder
|
||||
)
|
||||
{
|
||||
_settings = Guard.NotNull(settings);
|
||||
@@ -60,12 +63,13 @@ public class MappingBuilder : IMappingBuilder
|
||||
_mappingToFileSaver = Guard.NotNull(mappingToFileSaver);
|
||||
_guidUtils = Guard.NotNull(guidUtils);
|
||||
_dateTimeUtils = Guard.NotNull(dateTimeUtils);
|
||||
_responseMessageBuilder = Guard.NotNull(responseMessageBuilder);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IRespondWithAProvider Given(IRequestMatcher requestMatcher, bool saveToFile = false)
|
||||
{
|
||||
return new RespondWithAProvider(RegisterMapping, Guard.NotNull(requestMatcher), _settings, _guidUtils, _dateTimeUtils, saveToFile);
|
||||
return new RespondWithAProvider(RegisterMapping, Guard.NotNull(requestMatcher), _settings, _guidUtils, _dateTimeUtils, _responseMessageBuilder, saveToFile);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -142,6 +142,9 @@ public class RequestMessageBodyMatcher : IRequestMatcher
|
||||
MatchOperator = matchOperator;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public RequestMatcherType Type => RequestMatcherType.Body;
|
||||
|
||||
/// <inheritdoc />
|
||||
public double GetMatchingScore(IRequestMessage requestMessage, IRequestMatchResult requestMatchResult)
|
||||
{
|
||||
|
||||
@@ -31,6 +31,9 @@ public class RequestMessageBodyMatcher<T> : IRequestMatcher
|
||||
Func = Guard.NotNull(func);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public RequestMatcherType Type => RequestMatcherType.BodyOfT;
|
||||
|
||||
/// <inheritdoc />
|
||||
public double GetMatchingScore(IRequestMessage requestMessage, IRequestMatchResult requestMatchResult)
|
||||
{
|
||||
|
||||
@@ -74,6 +74,9 @@ public class RequestMessageClientIPMatcher : IRequestMatcher
|
||||
Funcs = Guard.NotNull(funcs);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public RequestMatcherType Type => RequestMatcherType.ClientIP;
|
||||
|
||||
/// <inheritdoc />
|
||||
public double GetMatchingScore(IRequestMessage requestMessage, IRequestMatchResult requestMatchResult)
|
||||
{
|
||||
|
||||
@@ -20,6 +20,11 @@ public abstract class RequestMessageCompositeMatcher : IRequestMatcher
|
||||
/// </value>
|
||||
private IEnumerable<IRequestMatcher> RequestMatchers { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Selected type to choose the matcher from <see cref="RequestMatchers"/> which will immediately return a mismatch.
|
||||
/// </summary>
|
||||
internal RequestMatcherType? EarlyMatcherType { get; private protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RequestMessageCompositeMatcher"/> class.
|
||||
/// </summary>
|
||||
@@ -31,6 +36,9 @@ public abstract class RequestMessageCompositeMatcher : IRequestMatcher
|
||||
_type = type;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public RequestMatcherType Type => RequestMatcherType.Composite;
|
||||
|
||||
/// <inheritdoc />
|
||||
public double GetMatchingScore(IRequestMessage requestMessage, IRequestMatchResult requestMatchResult)
|
||||
{
|
||||
@@ -39,6 +47,13 @@ public abstract class RequestMessageCompositeMatcher : IRequestMatcher
|
||||
return MatchScores.Mismatch;
|
||||
}
|
||||
|
||||
var earlyMatcher = new RequestMessageEarlyMatcher(EarlyMatcherType, RequestMatchers);
|
||||
var earlyMatchResult = earlyMatcher.GetMatchingScore(requestMessage, requestMatchResult);
|
||||
if (!MatchScores.IsPerfect(earlyMatchResult))
|
||||
{
|
||||
return MatchScores.Mismatch;
|
||||
}
|
||||
|
||||
if (_type == CompositeMatcherType.And)
|
||||
{
|
||||
return RequestMatchers.Average(requestMatcher => requestMatcher.GetMatchingScore(requestMessage, requestMatchResult));
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using Stef.Validation;
|
||||
using System.Linq;
|
||||
|
||||
namespace WireMock.Matchers.Request;
|
||||
|
||||
@@ -93,6 +92,9 @@ public class RequestMessageCookieMatcher : IRequestMatcher
|
||||
Name = string.Empty; // Not used when Func, but set to a non-null valid value.
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public RequestMatcherType Type => RequestMatcherType.Cookie;
|
||||
|
||||
/// <inheritdoc />
|
||||
public double GetMatchingScore(IRequestMessage requestMessage, IRequestMatchResult requestMatchResult)
|
||||
{
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
namespace WireMock.Matchers.Request;
|
||||
|
||||
/// <summary>
|
||||
/// Return the mismatch if the matching score of matchers is not perfect.
|
||||
/// </summary>
|
||||
internal sealed class RequestMessageEarlyMatcher(
|
||||
RequestMatcherType? earlyMatcherType,
|
||||
IEnumerable<IRequestMatcher> requestMatchers) : IRequestMatcher
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public RequestMatcherType Type => RequestMatcherType.Composite;
|
||||
|
||||
/// <inheritdoc />
|
||||
public double GetMatchingScore(IRequestMessage requestMessage, IRequestMatchResult requestMatchResult)
|
||||
{
|
||||
if (earlyMatcherType is null)
|
||||
{
|
||||
return MatchScores.Perfect;
|
||||
}
|
||||
|
||||
var earlyMatchers = requestMatchers
|
||||
.Where(m => m.Type == earlyMatcherType)
|
||||
.ToList();
|
||||
|
||||
if (earlyMatchers.Count is 0)
|
||||
{
|
||||
return MatchScores.Perfect;
|
||||
}
|
||||
|
||||
var compositeMatcher = new RequestBuilders.Request(earlyMatchers);
|
||||
return compositeMatcher.GetMatchingScore(requestMessage, requestMatchResult);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System.Linq;
|
||||
using Stef.Validation;
|
||||
using WireMock.Types;
|
||||
|
||||
@@ -12,7 +11,7 @@ namespace WireMock.Matchers.Request;
|
||||
/// <inheritdoc cref="IRequestMatcher"/>
|
||||
public class RequestMessageHeaderMatcher : IRequestMatcher
|
||||
{
|
||||
private const string _name = nameof(RequestMessageCookieMatcher);
|
||||
private const string _name = nameof(RequestMessageHeaderMatcher);
|
||||
|
||||
/// <summary>
|
||||
/// MatchBehaviour
|
||||
@@ -106,6 +105,9 @@ public class RequestMessageHeaderMatcher : IRequestMatcher
|
||||
Name = string.Empty; // Not used when Func, but set to a non-null valid value.
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public RequestMatcherType Type => RequestMatcherType.Header;
|
||||
|
||||
/// <inheritdoc />
|
||||
public double GetMatchingScore(IRequestMessage requestMessage, IRequestMatchResult requestMatchResult)
|
||||
{
|
||||
|
||||
@@ -65,6 +65,9 @@ public class RequestMessageHttpVersionMatcher : IRequestMatcher
|
||||
MatcherOnStringFunc = Guard.NotNull(func);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public RequestMatcherType Type => RequestMatcherType.HttpVersion;
|
||||
|
||||
/// <inheritdoc />
|
||||
public double GetMatchingScore(IRequestMessage requestMessage, IRequestMatchResult requestMatchResult)
|
||||
{
|
||||
|
||||
@@ -46,6 +46,9 @@ internal class RequestMessageMethodMatcher : IRequestMatcher
|
||||
MatchOperator = matchOperator;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public RequestMatcherType Type => RequestMatcherType.Method;
|
||||
|
||||
/// <inheritdoc />
|
||||
public double GetMatchingScore(IRequestMessage requestMessage, IRequestMatchResult requestMatchResult)
|
||||
{
|
||||
|
||||
@@ -55,6 +55,9 @@ public class RequestMessageMultiPartMatcher : IRequestMatcher
|
||||
MatchOperator = matchOperator;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public RequestMatcherType Type => RequestMatcherType.MultiPart;
|
||||
|
||||
/// <inheritdoc />
|
||||
public double GetMatchingScore(IRequestMessage requestMessage, IRequestMatchResult requestMatchResult)
|
||||
{
|
||||
|
||||
@@ -82,6 +82,9 @@ public class RequestMessageParamMatcher : IRequestMatcher
|
||||
Funcs = Guard.NotNull(funcs);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public RequestMatcherType Type => RequestMatcherType.Param;
|
||||
|
||||
/// <inheritdoc />
|
||||
public double GetMatchingScore(IRequestMessage requestMessage, IRequestMatchResult requestMatchResult)
|
||||
{
|
||||
|
||||
@@ -72,6 +72,9 @@ public class RequestMessagePathMatcher : IRequestMatcher
|
||||
Funcs = Guard.NotNull(funcs);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public RequestMatcherType Type => RequestMatcherType.Path;
|
||||
|
||||
/// <inheritdoc />
|
||||
public double GetMatchingScore(IRequestMessage requestMessage, IRequestMatchResult requestMatchResult)
|
||||
{
|
||||
|
||||
@@ -29,6 +29,9 @@ internal class RequestMessageScenarioAndStateMatcher : IRequestMatcher
|
||||
_executionConditionState = executionConditionState;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public RequestMatcherType Type => RequestMatcherType.ScenarioAndState;
|
||||
|
||||
/// <inheritdoc />
|
||||
public double GetMatchingScore(IRequestMessage requestMessage, IRequestMatchResult requestMatchResult)
|
||||
{
|
||||
|
||||
@@ -72,6 +72,9 @@ public class RequestMessageUrlMatcher : IRequestMatcher
|
||||
Funcs = Guard.NotNull(funcs);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public RequestMatcherType Type => RequestMatcherType.Url;
|
||||
|
||||
/// <inheritdoc />
|
||||
public double GetMatchingScore(IRequestMessage requestMessage, IRequestMatchResult requestMatchResult)
|
||||
{
|
||||
|
||||
@@ -72,6 +72,7 @@ internal partial class AspNetCoreSelfHost
|
||||
services.AddSingleton<IOwinResponseMapper, OwinResponseMapper>();
|
||||
services.AddSingleton<IGuidUtils, GuidUtils>();
|
||||
services.AddSingleton<IDateTimeUtils, DateTimeUtils>();
|
||||
services.AddSingleton<IResponseMessageBuilder, ResponseMessageBuilder>();
|
||||
services.AddSingleton<LogEntryMapper>();
|
||||
services.AddSingleton<IWireMockMiddlewareLogger, WireMockMiddlewareLogger>();
|
||||
|
||||
|
||||
@@ -11,12 +11,14 @@ internal class GlobalExceptionMiddleware
|
||||
{
|
||||
private readonly IWireMockMiddlewareOptions _options;
|
||||
private readonly IOwinResponseMapper _responseMapper;
|
||||
private readonly IResponseMessageBuilder _responseMessageBuilder;
|
||||
|
||||
public GlobalExceptionMiddleware(RequestDelegate next, IWireMockMiddlewareOptions options, IOwinResponseMapper responseMapper)
|
||||
public GlobalExceptionMiddleware(RequestDelegate next, IWireMockMiddlewareOptions options, IOwinResponseMapper responseMapper, IResponseMessageBuilder responseMessageBuilder)
|
||||
{
|
||||
Next = next;
|
||||
_options = Guard.NotNull(options);
|
||||
_responseMapper = Guard.NotNull(responseMapper);
|
||||
_responseMessageBuilder = Guard.NotNull(responseMessageBuilder);
|
||||
}
|
||||
|
||||
public RequestDelegate Next { get; }
|
||||
@@ -35,7 +37,7 @@ internal class GlobalExceptionMiddleware
|
||||
catch (Exception ex)
|
||||
{
|
||||
_options.Logger.Error("HttpStatusCode set to 500 {0}", ex);
|
||||
await _responseMapper.MapAsync(ResponseMessageBuilder.Create(500, JsonConvert.SerializeObject(ex)), ctx.Response).ConfigureAwait(false);
|
||||
await _responseMapper.MapAsync(_responseMessageBuilder.Create(500, JsonConvert.SerializeObject(ex)), ctx.Response).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,6 +19,7 @@ internal class MappingMatcher(IWireMockMiddlewareOptions options, IRandomizerDou
|
||||
var possibleMappings = new List<MappingMatcherResult>();
|
||||
|
||||
var mappings = _options.Mappings.Values
|
||||
.Where(m => !m.IsDisabled)
|
||||
.Where(m => m.TimeSettings.IsValid())
|
||||
.Where(m => m.Probability is null || _randomizerDoubleBetween0And1.Generate() <= m.Probability)
|
||||
.ToArray();
|
||||
|
||||
@@ -26,7 +26,8 @@ internal class WireMockMiddleware(
|
||||
IMappingMatcher mappingMatcher,
|
||||
IWireMockMiddlewareLogger logger,
|
||||
IGuidUtils guidUtils,
|
||||
IDateTimeUtils dateTimeUtils
|
||||
IDateTimeUtils dateTimeUtils,
|
||||
IResponseMessageBuilder responseMessageBuilder
|
||||
)
|
||||
{
|
||||
private readonly object _lock = new();
|
||||
@@ -97,7 +98,7 @@ internal class WireMockMiddleware(
|
||||
{
|
||||
logRequest = true;
|
||||
options.Logger.Warn("HttpStatusCode set to 404 : No matching mapping found");
|
||||
response = ResponseMessageBuilder.Create(HttpStatusCode.NotFound, WireMockConstants.NoMatchingFound);
|
||||
response = responseMessageBuilder.Create(HttpStatusCode.NotFound, WireMockConstants.NoMatchingFound);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -109,7 +110,7 @@ internal class WireMockMiddleware(
|
||||
if (!authorizationHeaderPresent)
|
||||
{
|
||||
options.Logger.Error("HttpStatusCode set to 401, authorization header is missing.");
|
||||
response = ResponseMessageBuilder.Create(HttpStatusCode.Unauthorized, null);
|
||||
response = responseMessageBuilder.Create(HttpStatusCode.Unauthorized, null);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -117,7 +118,7 @@ internal class WireMockMiddleware(
|
||||
if (!MatchScores.IsPerfect(authorizationHeaderMatchResult.Score))
|
||||
{
|
||||
options.Logger.Error("HttpStatusCode set to 401, authentication failed.", authorizationHeaderMatchResult.Exception ?? throw new WireMockException("Authentication failed"));
|
||||
response = ResponseMessageBuilder.Create(HttpStatusCode.Unauthorized, null);
|
||||
response = responseMessageBuilder.Create(HttpStatusCode.Unauthorized, null);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -165,7 +166,7 @@ internal class WireMockMiddleware(
|
||||
options.Logger.Error($"Providing a Response for Mapping '{result.Match?.Mapping.Guid}' failed. HttpStatusCode set to 500. Exception: {ex}");
|
||||
WireMockActivitySource.RecordException(activity, ex);
|
||||
|
||||
response = ResponseMessageBuilder.Create(500, ex.Message);
|
||||
response = responseMessageBuilder.Create(500, ex.Message);
|
||||
}
|
||||
finally
|
||||
{
|
||||
@@ -179,7 +180,7 @@ internal class WireMockMiddleware(
|
||||
{
|
||||
options.Logger.Error("HttpStatusCode set to 404 : No matching mapping found", ex);
|
||||
|
||||
var notFoundResponse = ResponseMessageBuilder.Create(HttpStatusCode.NotFound, WireMockConstants.NoMatchingFound);
|
||||
var notFoundResponse = responseMessageBuilder.Create(HttpStatusCode.NotFound, WireMockConstants.NoMatchingFound);
|
||||
await responseMapper.MapAsync(notFoundResponse, ctx.Response).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ public partial class Request : RequestMessageCompositeMatcher, IRequestBuilder
|
||||
/// Initializes a new instance of the <see cref="Request"/> class.
|
||||
/// </summary>
|
||||
/// <param name="requestMatchers">The request matchers.</param>
|
||||
private Request(IList<IRequestMatcher> requestMatchers) : base(requestMatchers)
|
||||
internal Request(IList<IRequestMatcher> requestMatchers) : base(requestMatchers)
|
||||
{
|
||||
_requestMatchers = Guard.NotNull(requestMatchers);
|
||||
}
|
||||
@@ -81,6 +81,13 @@ public partial class Request : RequestMessageCompositeMatcher, IRequestBuilder
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IRequestBuilder WithEarlyMismatch(RequestMatcherType? earlyMatcherType)
|
||||
{
|
||||
EarlyMatcherType = earlyMatcherType;
|
||||
return this;
|
||||
}
|
||||
|
||||
internal bool TryGetProtoBufMatcher([NotNullWhen(true)] out IProtoBufMatcher? protoBufMatcher)
|
||||
{
|
||||
protoBufMatcher = GetRequestMessageMatcher<RequestMessageProtoBufMatcher>()?.Matcher;
|
||||
|
||||
@@ -9,29 +9,30 @@ using WireMock.Util;
|
||||
|
||||
namespace WireMock;
|
||||
|
||||
internal static class ResponseMessageBuilder
|
||||
internal class ResponseMessageBuilder(IDateTimeUtils dateTimeUtils) : IResponseMessageBuilder
|
||||
{
|
||||
private static readonly IDictionary<string, WireMockList<string>> ContentTypeJsonHeaders = new Dictionary<string, WireMockList<string>>
|
||||
{
|
||||
{ HttpKnownHeaderNames.ContentType, new WireMockList<string> { WireMockConstants.ContentTypeJson } }
|
||||
};
|
||||
|
||||
internal static ResponseMessage Create(HttpStatusCode statusCode, string? status, Guid? guid = null)
|
||||
public ResponseMessage Create(HttpStatusCode statusCode, string? status, Guid? guid = null)
|
||||
{
|
||||
return Create((int)statusCode, status, guid);
|
||||
return Create((int)statusCode, status, null, guid);
|
||||
}
|
||||
|
||||
internal static ResponseMessage Create(int statusCode, string? status, Guid? guid = null)
|
||||
public ResponseMessage Create(int statusCode, string? status, Guid? guid = null)
|
||||
{
|
||||
return Create(statusCode, status, null, guid);
|
||||
}
|
||||
|
||||
internal static ResponseMessage Create(int statusCode, string? status, string? error, Guid? guid = null)
|
||||
public ResponseMessage Create(int statusCode, string? status, string? error, Guid? guid = null)
|
||||
{
|
||||
var response = new ResponseMessage
|
||||
{
|
||||
StatusCode = statusCode,
|
||||
Headers = ContentTypeJsonHeaders
|
||||
Headers = ContentTypeJsonHeaders,
|
||||
DateTime = dateTimeUtils.UtcNow
|
||||
};
|
||||
|
||||
if (status != null || error != null)
|
||||
@@ -51,7 +52,7 @@ internal static class ResponseMessageBuilder
|
||||
return response;
|
||||
}
|
||||
|
||||
internal static ResponseMessage Create(HttpStatusCode statusCode)
|
||||
public ResponseMessage Create(HttpStatusCode statusCode)
|
||||
{
|
||||
return new ResponseMessage
|
||||
{
|
||||
|
||||
@@ -16,7 +16,7 @@ using WireMock.WebSockets;
|
||||
|
||||
namespace WireMock.ResponseProviders;
|
||||
|
||||
internal class WebSocketResponseProvider(WebSocketBuilder builder, IGuidUtils guidUtils, IDateTimeUtils dateTimeUtils) : IResponseProvider
|
||||
internal class WebSocketResponseProvider(WebSocketBuilder builder, IGuidUtils guidUtils, IDateTimeUtils dateTimeUtils, IResponseMessageBuilder responseMessageBuilder) : IResponseProvider
|
||||
{
|
||||
public async Task<(IResponseMessage Message, IMapping? Mapping)> ProvideResponseAsync(
|
||||
IMapping mapping,
|
||||
@@ -27,7 +27,7 @@ internal class WebSocketResponseProvider(WebSocketBuilder builder, IGuidUtils gu
|
||||
// Check if this is a WebSocket upgrade request
|
||||
if (!context.WebSockets.IsWebSocketRequest)
|
||||
{
|
||||
return (ResponseMessageBuilder.Create(HttpStatusCode.BadRequest, "Bad Request: Not a WebSocket upgrade request"), null);
|
||||
return (responseMessageBuilder.Create(HttpStatusCode.BadRequest, "Bad Request: Not a WebSocket upgrade request"), null);
|
||||
}
|
||||
|
||||
if (!context.Items.TryGetValue<IWireMockMiddlewareOptions>(nameof(IWireMockMiddlewareOptions), out var options))
|
||||
@@ -110,7 +110,7 @@ internal class WebSocketResponseProvider(WebSocketBuilder builder, IGuidUtils gu
|
||||
// If we haven't upgraded yet, we can return HTTP error
|
||||
if (!context.Response.HasStarted)
|
||||
{
|
||||
return (ResponseMessageBuilder.Create(HttpStatusCode.InternalServerError, $"WebSocket error: {ex.Message}"), null);
|
||||
return (responseMessageBuilder.Create(HttpStatusCode.InternalServerError, $"WebSocket error: {ex.Message}"), null);
|
||||
}
|
||||
|
||||
// Already upgraded - return marker
|
||||
|
||||
@@ -66,6 +66,12 @@ internal class MappingConverter(MatcherMapper mapper)
|
||||
|
||||
// Request
|
||||
sb.AppendLine(" .Given(Request.Create()");
|
||||
|
||||
if (request.EarlyMatcherType != null)
|
||||
{
|
||||
sb.AppendLine($" .WithEarlyMismatch({request.EarlyMatcherType.Value.GetFullyQualifiedEnumValue()})");
|
||||
}
|
||||
|
||||
sb.AppendLine($" .UsingMethod({To1Or2Or3Arguments(methodMatcher?.MatchBehaviour, methodMatcher?.MatchOperator, methodMatcher?.Methods, HttpRequestMethod.GET)})");
|
||||
|
||||
if (pathMatcher?.Matchers != null)
|
||||
@@ -275,6 +281,7 @@ internal class MappingConverter(MatcherMapper mapper)
|
||||
TimesInSameState = !string.IsNullOrWhiteSpace(mapping.NextState) ? mapping.TimesInSameState : null,
|
||||
Data = mapping.Data,
|
||||
Probability = mapping.Probability,
|
||||
IsDisabled = mapping.IsDisabled ? true : null,
|
||||
Request = new RequestModel
|
||||
{
|
||||
Headers = headerMatchers.Any() ? headerMatchers.Select(hm => new HeaderModel
|
||||
@@ -299,7 +306,9 @@ internal class MappingConverter(MatcherMapper mapper)
|
||||
IgnoreCase = pm.IgnoreCase ? true : null,
|
||||
RejectOnMatch = pm.MatchBehaviour == MatchBehaviour.RejectOnMatch ? true : null,
|
||||
Matchers = _mapper.Map(pm.Matchers)
|
||||
}).ToList() : null
|
||||
}).ToList() : null,
|
||||
|
||||
EarlyMatcherType = request.EarlyMatcherType
|
||||
},
|
||||
Response = new ResponseModel()
|
||||
};
|
||||
|
||||
@@ -234,6 +234,13 @@ public interface IRespondWithAProvider
|
||||
/// <returns>The <see cref="IRespondWithAProvider"/>.</returns>
|
||||
IRespondWithAProvider WithProbability(double probability);
|
||||
|
||||
/// <summary>
|
||||
/// Define whether this mapping is disabled. Defaults to <c>false</c>.
|
||||
/// </summary>
|
||||
/// <param name="isDisabled">Whether this mapping is disabled.</param>
|
||||
/// <returns>The <see cref="IRespondWithAProvider"/>.</returns>
|
||||
IRespondWithAProvider WithIsDisabled(bool isDisabled);
|
||||
|
||||
/// <summary>
|
||||
/// Define a Grpc ProtoDefinition which is used for the request and the response.
|
||||
/// This can be a ProtoDefinition as a string, or an id when the ProtoDefinitions are defined at the WireMockServer.
|
||||
|
||||
@@ -24,6 +24,7 @@ internal class RespondWithAProvider : IRespondWithAProvider
|
||||
private readonly WireMockServerSettings _settings;
|
||||
private readonly IDateTimeUtils _dateTimeUtils;
|
||||
private readonly IGuidUtils _guidUtils;
|
||||
private readonly IResponseMessageBuilder _responseMessageBuilder;
|
||||
|
||||
private readonly bool _saveToFile;
|
||||
|
||||
@@ -37,6 +38,7 @@ internal class RespondWithAProvider : IRespondWithAProvider
|
||||
private int _timesInSameState = 1;
|
||||
private bool? _useWebhookFireAndForget;
|
||||
private double? _probability;
|
||||
private bool _isDisabled = false;
|
||||
private GraphQLSchemaDetails? _graphQLSchemaDetails; // Future Use.
|
||||
|
||||
public Guid Guid { get; private set; }
|
||||
@@ -55,6 +57,7 @@ internal class RespondWithAProvider : IRespondWithAProvider
|
||||
WireMockServerSettings settings,
|
||||
IGuidUtils guidUtils,
|
||||
IDateTimeUtils dateTimeUtils,
|
||||
IResponseMessageBuilder responseMessageBuilder,
|
||||
bool saveToFile = false
|
||||
)
|
||||
{
|
||||
@@ -63,6 +66,7 @@ internal class RespondWithAProvider : IRespondWithAProvider
|
||||
_settings = Guard.NotNull(settings);
|
||||
_dateTimeUtils = Guard.NotNull(dateTimeUtils);
|
||||
_guidUtils = Guard.NotNull(guidUtils);
|
||||
_responseMessageBuilder = Guard.NotNull(responseMessageBuilder);
|
||||
|
||||
_saveToFile = saveToFile;
|
||||
|
||||
@@ -78,7 +82,8 @@ internal class RespondWithAProvider : IRespondWithAProvider
|
||||
provider = new WebSocketResponseProvider(
|
||||
response.WebSocketBuilder,
|
||||
_guidUtils,
|
||||
_dateTimeUtils
|
||||
_dateTimeUtils,
|
||||
_responseMessageBuilder
|
||||
);
|
||||
}
|
||||
|
||||
@@ -108,6 +113,11 @@ internal class RespondWithAProvider : IRespondWithAProvider
|
||||
mapping.WithProbability(_probability.Value);
|
||||
}
|
||||
|
||||
if (_isDisabled)
|
||||
{
|
||||
mapping.IsDisabled = true;
|
||||
}
|
||||
|
||||
if (ProtoDefinition != null)
|
||||
{
|
||||
mapping.WithProtoDefinition(ProtoDefinition.Value);
|
||||
@@ -354,6 +364,13 @@ internal class RespondWithAProvider : IRespondWithAProvider
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IRespondWithAProvider WithIsDisabled(bool isDisabled)
|
||||
{
|
||||
_isDisabled = isDisabled;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IRespondWithAProvider WithProtoDefinition(params string[] protoDefinitionOrId)
|
||||
{
|
||||
|
||||
@@ -57,6 +57,8 @@ public partial class WireMockServer
|
||||
public string OpenApi => $"{_prefix}/openapi";
|
||||
|
||||
public RegexMatcher MappingsGuidPathMatcher => new($"^{_prefixEscaped}\\/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}})$");
|
||||
public RegexMatcher MappingsGuidEnablePathMatcher => new($"^{_prefixEscaped}\\/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}})\\/enable$");
|
||||
public RegexMatcher MappingsGuidDisablePathMatcher => new($"^{_prefixEscaped}\\/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}})\\/disable$");
|
||||
public RegexMatcher MappingsCodeGuidPathMatcher => new($"^{_prefixEscaped}\\/mappings\\/code\\/([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}})$");
|
||||
public RegexMatcher RequestsGuidPathMatcher => new($"^{_prefixEscaped}\\/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}})$");
|
||||
public RegexMatcher ScenariosNameMatcher => new($"^{_prefixEscaped}\\/scenarios\\/.+$");
|
||||
@@ -100,6 +102,12 @@ public partial class WireMockServer
|
||||
Given(Request.Create().WithPath(_adminPaths.MappingsGuidPathMatcher).UsingPut().WithHeader(HttpKnownHeaderNames.ContentType, AdminRequestContentTypeJson)).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(MappingPut));
|
||||
Given(Request.Create().WithPath(_adminPaths.MappingsGuidPathMatcher).UsingDelete()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(MappingDelete));
|
||||
|
||||
// __admin/mappings/{guid}/enable
|
||||
Given(Request.Create().WithPath(_adminPaths.MappingsGuidEnablePathMatcher).UsingPut()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(MappingEnable));
|
||||
|
||||
// __admin/mappings/{guid}/disable
|
||||
Given(Request.Create().WithPath(_adminPaths.MappingsGuidDisablePathMatcher).UsingPut()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(MappingDisable));
|
||||
|
||||
// __admin/mappings/code/{guid}
|
||||
Given(Request.Create().WithPath(_adminPaths.MappingsCodeGuidPathMatcher).UsingGet()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(MappingCodeGet));
|
||||
|
||||
@@ -342,7 +350,7 @@ public partial class WireMockServer
|
||||
o.AcceptAnyClientCertificate = _settings.AcceptAnyClientCertificate;
|
||||
});
|
||||
|
||||
return ResponseMessageBuilder.Create(200, "Settings updated");
|
||||
return _responseMessageBuilder.Create(200, "Settings updated");
|
||||
}
|
||||
#endregion Settings
|
||||
|
||||
@@ -353,7 +361,7 @@ public partial class WireMockServer
|
||||
if (mapping == null)
|
||||
{
|
||||
_settings.Logger.Warn("HttpStatusCode set to 404 : Mapping not found");
|
||||
return ResponseMessageBuilder.Create(HttpStatusCode.NotFound, "Mapping not found");
|
||||
return _responseMessageBuilder.Create(HttpStatusCode.NotFound, "Mapping not found");
|
||||
}
|
||||
|
||||
var model = _mappingConverter.ToMappingModel(mapping);
|
||||
@@ -369,14 +377,14 @@ public partial class WireMockServer
|
||||
if (code is null)
|
||||
{
|
||||
_settings.Logger.Warn("HttpStatusCode set to 404 : Mapping not found");
|
||||
return ResponseMessageBuilder.Create(HttpStatusCode.NotFound, "Mapping not found");
|
||||
return _responseMessageBuilder.Create(HttpStatusCode.NotFound, "Mapping not found");
|
||||
}
|
||||
|
||||
return ToResponseMessage(code);
|
||||
}
|
||||
|
||||
_settings.Logger.Warn("HttpStatusCode set to 400");
|
||||
return ResponseMessageBuilder.Create(HttpStatusCode.BadRequest, "GUID is missing");
|
||||
return _responseMessageBuilder.Create(HttpStatusCode.BadRequest, "GUID is missing");
|
||||
}
|
||||
|
||||
private static TEnum GetEnumFromQuery<TEnum>(IRequestMessage requestMessage, TEnum defaultValue)
|
||||
@@ -403,22 +411,22 @@ public partial class WireMockServer
|
||||
var mappingModel = DeserializeObject<MappingModel>(requestMessage);
|
||||
var guidFromPut = ConvertMappingAndRegisterAsRespondProvider(mappingModel, guid);
|
||||
|
||||
return ResponseMessageBuilder.Create(HttpStatusCode.OK, "Mapping added or updated", guidFromPut);
|
||||
return _responseMessageBuilder.Create(HttpStatusCode.OK, "Mapping added or updated", guidFromPut);
|
||||
}
|
||||
|
||||
_settings.Logger.Warn("HttpStatusCode set to 404 : Mapping not found");
|
||||
return ResponseMessageBuilder.Create(HttpStatusCode.NotFound, "Mapping not found");
|
||||
return _responseMessageBuilder.Create(HttpStatusCode.NotFound, "Mapping not found");
|
||||
}
|
||||
|
||||
private IResponseMessage MappingDelete(HttpContext _, IRequestMessage requestMessage)
|
||||
{
|
||||
if (TryParseGuidFromRequestMessage(requestMessage, out var guid) && DeleteMapping(guid))
|
||||
{
|
||||
return ResponseMessageBuilder.Create(HttpStatusCode.OK, "Mapping removed", guid);
|
||||
return _responseMessageBuilder.Create(HttpStatusCode.OK, "Mapping removed", guid);
|
||||
}
|
||||
|
||||
_settings.Logger.Warn("HttpStatusCode set to 404 : Mapping not found");
|
||||
return ResponseMessageBuilder.Create(HttpStatusCode.NotFound, "Mapping not found");
|
||||
return _responseMessageBuilder.Create(HttpStatusCode.NotFound, "Mapping not found");
|
||||
}
|
||||
|
||||
private static bool TryParseGuidFromRequestMessage(IRequestMessage requestMessage, out Guid guid)
|
||||
@@ -426,6 +434,47 @@ public partial class WireMockServer
|
||||
var lastPart = requestMessage.Path.Split('/').LastOrDefault();
|
||||
return Guid.TryParse(lastPart, out guid);
|
||||
}
|
||||
|
||||
private static bool TryParseGuidFromSecondToLastSegment(IRequestMessage requestMessage, out Guid guid)
|
||||
{
|
||||
var parts = requestMessage.Path.Split('/');
|
||||
if (parts.Length >= 2 && Guid.TryParse(parts[parts.Length - 2], out guid))
|
||||
return true;
|
||||
guid = Guid.Empty;
|
||||
return false;
|
||||
}
|
||||
|
||||
private IResponseMessage MappingEnable(HttpContext _, IRequestMessage requestMessage)
|
||||
{
|
||||
if (TryParseGuidFromSecondToLastSegment(requestMessage, out var guid))
|
||||
{
|
||||
var mapping = Mappings.FirstOrDefault(m => !m.IsAdminInterface && m.Guid == guid);
|
||||
if (mapping != null)
|
||||
{
|
||||
mapping.IsDisabled = false;
|
||||
return _responseMessageBuilder.Create(HttpStatusCode.OK, "Mapping enabled", guid);
|
||||
}
|
||||
}
|
||||
|
||||
_settings.Logger.Warn("HttpStatusCode set to 404 : Mapping not found");
|
||||
return _responseMessageBuilder.Create(HttpStatusCode.NotFound, "Mapping not found");
|
||||
}
|
||||
|
||||
private IResponseMessage MappingDisable(HttpContext _, IRequestMessage requestMessage)
|
||||
{
|
||||
if (TryParseGuidFromSecondToLastSegment(requestMessage, out var guid))
|
||||
{
|
||||
var mapping = Mappings.FirstOrDefault(m => !m.IsAdminInterface && m.Guid == guid);
|
||||
if (mapping != null)
|
||||
{
|
||||
mapping.IsDisabled = true;
|
||||
return _responseMessageBuilder.Create(HttpStatusCode.OK, "Mapping disabled", guid);
|
||||
}
|
||||
}
|
||||
|
||||
_settings.Logger.Warn("HttpStatusCode set to 404 : Mapping not found");
|
||||
return _responseMessageBuilder.Create(HttpStatusCode.NotFound, "Mapping not found");
|
||||
}
|
||||
#endregion Mapping/{guid}
|
||||
|
||||
#region Mappings
|
||||
@@ -447,7 +496,7 @@ public partial class WireMockServer
|
||||
{
|
||||
SaveStaticMappings();
|
||||
|
||||
return ResponseMessageBuilder.Create(200, "Mappings saved to disk");
|
||||
return _responseMessageBuilder.Create(200, "Mappings saved to disk");
|
||||
}
|
||||
|
||||
private MappingModel[] ToMappingModels()
|
||||
@@ -477,22 +526,22 @@ public partial class WireMockServer
|
||||
if (mappingModels.Length == 1)
|
||||
{
|
||||
var guid = ConvertMappingAndRegisterAsRespondProvider(mappingModels[0]);
|
||||
return ResponseMessageBuilder.Create(201, "Mapping added", guid);
|
||||
return _responseMessageBuilder.Create(201, "Mapping added", guid);
|
||||
}
|
||||
|
||||
ConvertMappingsAndRegisterAsRespondProvider(mappingModels);
|
||||
|
||||
return ResponseMessageBuilder.Create(201, "Mappings added");
|
||||
return _responseMessageBuilder.Create(201, "Mappings added");
|
||||
}
|
||||
catch (ArgumentException a)
|
||||
{
|
||||
_settings.Logger.Error("HttpStatusCode set to 400 {0}", a);
|
||||
return ResponseMessageBuilder.Create(400, a.Message);
|
||||
return _responseMessageBuilder.Create(400, a.Message);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_settings.Logger.Error("HttpStatusCode set to 500 {0}", e);
|
||||
return ResponseMessageBuilder.Create(500, e.ToString());
|
||||
return _responseMessageBuilder.Create(500, e.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -503,18 +552,18 @@ public partial class WireMockServer
|
||||
var deletedGuids = MappingsDeleteMappingFromBody(requestMessage);
|
||||
if (deletedGuids != null)
|
||||
{
|
||||
return ResponseMessageBuilder.Create(200, $"Mappings deleted. Affected GUIDs: [{string.Join(", ", deletedGuids.ToArray())}]");
|
||||
return _responseMessageBuilder.Create(200, $"Mappings deleted. Affected GUIDs: [{string.Join(", ", deletedGuids.ToArray())}]");
|
||||
}
|
||||
|
||||
// return bad request
|
||||
return ResponseMessageBuilder.Create(400, "Poorly formed mapping JSON.");
|
||||
return _responseMessageBuilder.Create(400, "Poorly formed mapping JSON.");
|
||||
}
|
||||
|
||||
ResetMappings();
|
||||
|
||||
ResetScenarios();
|
||||
|
||||
return ResponseMessageBuilder.Create(200, "Mappings deleted");
|
||||
return _responseMessageBuilder.Create(200, "Mappings deleted");
|
||||
}
|
||||
|
||||
private IEnumerable<Guid>? MappingsDeleteMappingFromBody(IRequestMessage requestMessage)
|
||||
@@ -566,14 +615,14 @@ public partial class WireMockServer
|
||||
message += " and static mappings reloaded";
|
||||
}
|
||||
|
||||
return ResponseMessageBuilder.Create(200, message);
|
||||
return _responseMessageBuilder.Create(200, message);
|
||||
}
|
||||
|
||||
private IResponseMessage ReloadStaticMappings(HttpContext _, IRequestMessage __)
|
||||
{
|
||||
ReadStaticMappings();
|
||||
|
||||
return ResponseMessageBuilder.Create(200, "Static Mappings reloaded");
|
||||
return _responseMessageBuilder.Create(200, "Static Mappings reloaded");
|
||||
}
|
||||
#endregion Mappings
|
||||
|
||||
@@ -591,18 +640,18 @@ public partial class WireMockServer
|
||||
}
|
||||
|
||||
_settings.Logger.Warn("HttpStatusCode set to 404 : Request not found");
|
||||
return ResponseMessageBuilder.Create(HttpStatusCode.NotFound, "Request not found");
|
||||
return _responseMessageBuilder.Create(HttpStatusCode.NotFound, "Request not found");
|
||||
}
|
||||
|
||||
private IResponseMessage RequestDelete(HttpContext _, IRequestMessage requestMessage)
|
||||
{
|
||||
if (TryParseGuidFromRequestMessage(requestMessage, out var guid) && DeleteLogEntry(guid))
|
||||
{
|
||||
return ResponseMessageBuilder.Create(200, "Request removed");
|
||||
return _responseMessageBuilder.Create(200, "Request removed");
|
||||
}
|
||||
|
||||
_settings.Logger.Warn("HttpStatusCode set to 404 : Request not found");
|
||||
return ResponseMessageBuilder.Create(HttpStatusCode.NotFound, "Request not found");
|
||||
return _responseMessageBuilder.Create(HttpStatusCode.NotFound, "Request not found");
|
||||
}
|
||||
#endregion Request/{guid}
|
||||
|
||||
@@ -621,7 +670,7 @@ public partial class WireMockServer
|
||||
{
|
||||
ResetLogEntries();
|
||||
|
||||
return ResponseMessageBuilder.Create(200, "Requests deleted");
|
||||
return _responseMessageBuilder.Create(200, "Requests deleted");
|
||||
}
|
||||
#endregion Requests
|
||||
|
||||
@@ -661,7 +710,7 @@ public partial class WireMockServer
|
||||
return ToJson(result);
|
||||
}
|
||||
|
||||
return ResponseMessageBuilder.Create(HttpStatusCode.BadRequest);
|
||||
return _responseMessageBuilder.Create(HttpStatusCode.BadRequest);
|
||||
}
|
||||
#endregion Requests/find
|
||||
|
||||
@@ -684,7 +733,7 @@ public partial class WireMockServer
|
||||
{
|
||||
ResetScenarios();
|
||||
|
||||
return ResponseMessageBuilder.Create(200, "Scenarios reset");
|
||||
return _responseMessageBuilder.Create(200, "Scenarios reset");
|
||||
}
|
||||
|
||||
private IResponseMessage ScenarioReset(HttpContext _, IRequestMessage requestMessage)
|
||||
@@ -694,8 +743,8 @@ public partial class WireMockServer
|
||||
Enumerable.Reverse(requestMessage.Path.Split('/')).Skip(1).First();
|
||||
|
||||
return ResetScenario(name) ?
|
||||
ResponseMessageBuilder.Create(200, "Scenario reset") :
|
||||
ResponseMessageBuilder.Create(HttpStatusCode.NotFound, $"No scenario found by name '{name}'.");
|
||||
_responseMessageBuilder.Create(200, "Scenario reset") :
|
||||
_responseMessageBuilder.Create(HttpStatusCode.NotFound, $"No scenario found by name '{name}'.");
|
||||
}
|
||||
|
||||
private IResponseMessage ScenariosSetState(HttpContext _, IRequestMessage requestMessage)
|
||||
@@ -703,14 +752,14 @@ public partial class WireMockServer
|
||||
var name = Enumerable.Reverse(requestMessage.Path.Split('/')).Skip(1).First();
|
||||
if (!_options.ScenarioStateStore.ContainsKey(name))
|
||||
{
|
||||
ResponseMessageBuilder.Create(HttpStatusCode.NotFound, $"No scenario found by name '{name}'.");
|
||||
_responseMessageBuilder.Create(HttpStatusCode.NotFound, $"No scenario found by name '{name}'.");
|
||||
}
|
||||
|
||||
var update = DeserializeObject<ScenarioStateUpdateModel>(requestMessage);
|
||||
|
||||
return SetScenarioState(name, update.State) ?
|
||||
ResponseMessageBuilder.Create(200, $"Scenario state set to '{update.State}'") :
|
||||
ResponseMessageBuilder.Create(HttpStatusCode.NotFound, $"No scenario found by name '{name}'.");
|
||||
_responseMessageBuilder.Create(200, $"Scenario state set to '{update.State}'") :
|
||||
_responseMessageBuilder.Create(HttpStatusCode.NotFound, $"No scenario found by name '{name}'.");
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
||||
@@ -18,14 +18,14 @@ public partial class WireMockServer
|
||||
{
|
||||
if (requestMessage.Body is null)
|
||||
{
|
||||
return ResponseMessageBuilder.Create(HttpStatusCode.BadRequest, "Body is null");
|
||||
return _responseMessageBuilder.Create(HttpStatusCode.BadRequest, "Body is null");
|
||||
}
|
||||
|
||||
var id = requestMessage.Path.Split('/').Last();
|
||||
|
||||
AddProtoDefinition(id, requestMessage.Body);
|
||||
|
||||
return ResponseMessageBuilder.Create(HttpStatusCode.OK, "ProtoDefinition added");
|
||||
return _responseMessageBuilder.Create(HttpStatusCode.OK, "ProtoDefinition added");
|
||||
}
|
||||
#endregion
|
||||
|
||||
@@ -34,7 +34,7 @@ public partial class WireMockServer
|
||||
{
|
||||
if (requestMessage.BodyAsBytes is null)
|
||||
{
|
||||
return ResponseMessageBuilder.Create(HttpStatusCode.BadRequest, "Body is null");
|
||||
return _responseMessageBuilder.Create(HttpStatusCode.BadRequest, "Body is null");
|
||||
}
|
||||
|
||||
var filename = GetFileNameFromRequestMessage(requestMessage);
|
||||
@@ -47,14 +47,14 @@ public partial class WireMockServer
|
||||
|
||||
_settings.FileSystemHandler.WriteFile(filename, requestMessage.BodyAsBytes);
|
||||
|
||||
return ResponseMessageBuilder.Create(HttpStatusCode.OK, "File created");
|
||||
return _responseMessageBuilder.Create(HttpStatusCode.OK, "File created");
|
||||
}
|
||||
|
||||
private IResponseMessage FilePut(HttpContext _, IRequestMessage requestMessage)
|
||||
{
|
||||
if (requestMessage.BodyAsBytes is null)
|
||||
{
|
||||
return ResponseMessageBuilder.Create(HttpStatusCode.BadRequest, "Body is null");
|
||||
return _responseMessageBuilder.Create(HttpStatusCode.BadRequest, "Body is null");
|
||||
}
|
||||
|
||||
var filename = GetFileNameFromRequestMessage(requestMessage);
|
||||
@@ -62,12 +62,12 @@ public partial class WireMockServer
|
||||
if (!_settings.FileSystemHandler.FileExists(filename))
|
||||
{
|
||||
_settings.Logger.Info("The file '{0}' does not exist, updating file will be skipped.", filename);
|
||||
return ResponseMessageBuilder.Create(HttpStatusCode.NotFound, "File is not found");
|
||||
return _responseMessageBuilder.Create(HttpStatusCode.NotFound, "File is not found");
|
||||
}
|
||||
|
||||
_settings.FileSystemHandler.WriteFile(filename, requestMessage.BodyAsBytes);
|
||||
|
||||
return ResponseMessageBuilder.Create(HttpStatusCode.OK, "File updated");
|
||||
return _responseMessageBuilder.Create(HttpStatusCode.OK, "File updated");
|
||||
}
|
||||
|
||||
private IResponseMessage FileGet(HttpContext _, IRequestMessage requestMessage)
|
||||
@@ -77,7 +77,7 @@ public partial class WireMockServer
|
||||
if (!_settings.FileSystemHandler.FileExists(filename))
|
||||
{
|
||||
_settings.Logger.Info("The file '{0}' does not exist.", filename);
|
||||
return ResponseMessageBuilder.Create(HttpStatusCode.NotFound, "File is not found");
|
||||
return _responseMessageBuilder.Create(HttpStatusCode.NotFound, "File is not found");
|
||||
}
|
||||
|
||||
var bytes = _settings.FileSystemHandler.ReadFile(filename);
|
||||
@@ -112,10 +112,10 @@ public partial class WireMockServer
|
||||
if (!_settings.FileSystemHandler.FileExists(filename))
|
||||
{
|
||||
_settings.Logger.Info("The file '{0}' does not exist.", filename);
|
||||
return ResponseMessageBuilder.Create(HttpStatusCode.NotFound);
|
||||
return _responseMessageBuilder.Create(HttpStatusCode.NotFound);
|
||||
}
|
||||
|
||||
return ResponseMessageBuilder.Create(HttpStatusCode.NoContent);
|
||||
return _responseMessageBuilder.Create(HttpStatusCode.NoContent);
|
||||
}
|
||||
|
||||
private IResponseMessage FileDelete(HttpContext _, IRequestMessage requestMessage)
|
||||
@@ -125,11 +125,11 @@ public partial class WireMockServer
|
||||
if (!_settings.FileSystemHandler.FileExists(filename))
|
||||
{
|
||||
_settings.Logger.Info("The file '{0}' does not exist.", filename);
|
||||
return ResponseMessageBuilder.Create(HttpStatusCode.NotFound, "File is not deleted");
|
||||
return _responseMessageBuilder.Create(HttpStatusCode.NotFound, "File is not deleted");
|
||||
}
|
||||
|
||||
_settings.FileSystemHandler.DeleteFile(filename);
|
||||
return ResponseMessageBuilder.Create(HttpStatusCode.OK, "File deleted.");
|
||||
return _responseMessageBuilder.Create(HttpStatusCode.OK, "File deleted.");
|
||||
}
|
||||
|
||||
private string GetFileNameFromRequestMessage(IRequestMessage requestMessage)
|
||||
|
||||
@@ -120,6 +120,11 @@ public partial class WireMockServer
|
||||
respondProvider.WithProbability(mappingModel.Probability.Value);
|
||||
}
|
||||
|
||||
if (mappingModel.IsDisabled == true)
|
||||
{
|
||||
respondProvider.WithIsDisabled(true);
|
||||
}
|
||||
|
||||
// ProtoDefinition is defined at Mapping level
|
||||
if (mappingModel.ProtoDefinition != null)
|
||||
{
|
||||
@@ -263,6 +268,8 @@ public partial class WireMockServer
|
||||
}
|
||||
}
|
||||
|
||||
requestBuilder = requestBuilder.WithEarlyMismatch(requestModel.EarlyMatcherType);
|
||||
|
||||
return requestBuilder;
|
||||
}
|
||||
|
||||
|
||||
@@ -52,7 +52,7 @@ public partial class WireMockServer
|
||||
if (mappingModels.Length == 1)
|
||||
{
|
||||
var guid = ConvertWireMockOrgMappingAndRegisterAsRespondProvider(mappingModels[0]);
|
||||
return ResponseMessageBuilder.Create(201, "Mapping added", guid);
|
||||
return _responseMessageBuilder.Create(201, "Mapping added", guid);
|
||||
}
|
||||
|
||||
foreach (var mappingModel in mappingModels)
|
||||
@@ -60,17 +60,17 @@ public partial class WireMockServer
|
||||
ConvertWireMockOrgMappingAndRegisterAsRespondProvider(mappingModel);
|
||||
}
|
||||
|
||||
return ResponseMessageBuilder.Create(201, "Mappings added");
|
||||
return _responseMessageBuilder.Create(201, "Mappings added");
|
||||
}
|
||||
catch (ArgumentException a)
|
||||
{
|
||||
_settings.Logger.Error("HttpStatusCode set to 400 {0}", a);
|
||||
return ResponseMessageBuilder.Create(400, a.Message);
|
||||
return _responseMessageBuilder.Create(400, a.Message);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_settings.Logger.Error("HttpStatusCode set to 500 {0}", e);
|
||||
return ResponseMessageBuilder.Create(500, e.ToString());
|
||||
return _responseMessageBuilder.Create(500, e.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ public partial class WireMockServer
|
||||
catch (Exception e)
|
||||
{
|
||||
_settings.Logger.Error("HttpStatusCode set to {0} {1}", HttpStatusCode.BadRequest, e);
|
||||
return ResponseMessageBuilder.Create(HttpStatusCode.BadRequest, e.Message);
|
||||
return _responseMessageBuilder.Create(HttpStatusCode.BadRequest, e.Message);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,12 +35,12 @@ public partial class WireMockServer
|
||||
|
||||
ConvertMappingsAndRegisterAsRespondProvider(mappingModels);
|
||||
|
||||
return ResponseMessageBuilder.Create(HttpStatusCode.Created, "OpenApi document converted to Mappings");
|
||||
return _responseMessageBuilder.Create(HttpStatusCode.Created, "OpenApi document converted to Mappings");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_settings.Logger.Error("HttpStatusCode set to {0} {1}", HttpStatusCode.BadRequest, e);
|
||||
return ResponseMessageBuilder.Create(HttpStatusCode.BadRequest, e.Message);
|
||||
return _responseMessageBuilder.Create(HttpStatusCode.BadRequest, e.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -42,6 +42,7 @@ public partial class WireMockServer : IWireMockServer
|
||||
private readonly MappingBuilder _mappingBuilder;
|
||||
private readonly IGuidUtils _guidUtils = new GuidUtils();
|
||||
private readonly IDateTimeUtils _dateTimeUtils = new DateTimeUtils();
|
||||
private readonly IResponseMessageBuilder _responseMessageBuilder;
|
||||
private readonly MappingSerializer _mappingSerializer;
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -354,6 +355,8 @@ public partial class WireMockServer : IWireMockServer
|
||||
{
|
||||
_settings = Guard.NotNull(settings);
|
||||
|
||||
_responseMessageBuilder = new ResponseMessageBuilder(_dateTimeUtils);
|
||||
|
||||
_mappingSerializer = new MappingSerializer(settings.DefaultJsonSerializer ?? new NewtonsoftJsonConverter());
|
||||
|
||||
// Set default values if not provided
|
||||
@@ -407,7 +410,8 @@ public partial class WireMockServer : IWireMockServer
|
||||
_mappingConverter,
|
||||
_mappingToFileSaver,
|
||||
_guidUtils,
|
||||
_dateTimeUtils
|
||||
_dateTimeUtils,
|
||||
_responseMessageBuilder
|
||||
);
|
||||
|
||||
_options.AdditionalServiceRegistration = _settings.AdditionalServiceRegistration;
|
||||
@@ -471,7 +475,7 @@ public partial class WireMockServer : IWireMockServer
|
||||
Given(Request.Create().WithPath("/*").UsingAnyMethod())
|
||||
.WithGuid(Guid.Parse("90008000-0000-4444-a17e-669cd84f1f05"))
|
||||
.AtPriority(1000)
|
||||
.RespondWith(new DynamicResponseProvider((_, _) => ResponseMessageBuilder.Create(HttpStatusCode.NotFound, WireMockConstants.NoMatchingFound)));
|
||||
.RespondWith(new DynamicResponseProvider((_, _) => _responseMessageBuilder.Create(HttpStatusCode.NotFound, WireMockConstants.NoMatchingFound)));
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IWireMockServer.Reset" />
|
||||
|
||||
@@ -25,9 +25,9 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="OpenTelemetry.Extensions.Hosting" Version="1.14.0" />
|
||||
<PackageReference Include="OpenTelemetry.Instrumentation.AspNetCore" Version="1.14.0" />
|
||||
<PackageReference Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.14.0" />
|
||||
<PackageReference Include="OpenTelemetry.Extensions.Hosting" Version="1.15.3" />
|
||||
<PackageReference Include="OpenTelemetry.Instrumentation.AspNetCore" Version="1.15.2" />
|
||||
<PackageReference Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.15.3" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -163,6 +163,22 @@ public interface IWireMockAdminApi
|
||||
[Header("Content-Type", "application/json")]
|
||||
Task<StatusModel> PutMappingAsync([Path] Guid guid, [Body] MappingModel mapping, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Enable a mapping based on the guid.
|
||||
/// </summary>
|
||||
/// <param name="guid">The Guid.</param>
|
||||
/// <param name="cancellationToken">The optional cancellationToken.</param>
|
||||
[Put("mappings/{guid}/enable")]
|
||||
Task<StatusModel> EnableMappingAsync([Path] Guid guid, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Disable a mapping based on the guid.
|
||||
/// </summary>
|
||||
/// <param name="guid">The Guid.</param>
|
||||
/// <param name="cancellationToken">The optional cancellationToken.</param>
|
||||
[Put("mappings/{guid}/disable")]
|
||||
Task<StatusModel> DisableMappingAsync([Path] Guid guid, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Delete a mapping based on the guid
|
||||
/// </summary>
|
||||
|
||||
@@ -108,6 +108,14 @@ public interface IMapping
|
||||
/// </value>
|
||||
bool IsProxy { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this mapping is disabled.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// <c>true</c> if this mapping is disabled; otherwise, <c>false</c>.
|
||||
/// </value>
|
||||
bool IsDisabled { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this mapping to be logged.
|
||||
/// </summary>
|
||||
@@ -135,7 +143,7 @@ public interface IMapping
|
||||
/// </summary>
|
||||
object? Data { get; }
|
||||
|
||||
/// <summary>
|
||||
/// <summary>
|
||||
/// The probability when this request should be matched. Value is between 0 and 1. [Optional]
|
||||
/// </summary>
|
||||
double? Probability { get; }
|
||||
|
||||
@@ -69,6 +69,9 @@ public class RequestMessageGraphQLMatcher : IRequestMatcher
|
||||
MatchOperator = matchOperator;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public RequestMatcherType Type => RequestMatcherType.GraphQL;
|
||||
|
||||
/// <inheritdoc />
|
||||
public double GetMatchingScore(IRequestMessage requestMessage, IRequestMatchResult requestMatchResult)
|
||||
{
|
||||
|
||||
@@ -30,6 +30,9 @@ public class RequestMessageProtoBufMatcher : IRequestMatcher
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public RequestMatcherType Type => RequestMatcherType.ProtoBuf;
|
||||
|
||||
/// <inheritdoc />
|
||||
public double GetMatchingScore(IRequestMessage requestMessage, IRequestMatchResult requestMatchResult)
|
||||
{
|
||||
|
||||
@@ -10,7 +10,8 @@ namespace WireMock.RequestBuilders;
|
||||
public interface IRequestBuilder : IClientIPRequestBuilder
|
||||
{
|
||||
/// <summary>
|
||||
/// Adds a request matcher to the builder.
|
||||
/// Adds a request matcher to the builder.<br/>
|
||||
/// If the request matcher is already present, it will be replaced.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the request matcher.</typeparam>
|
||||
/// <param name="requestMatcher">The request matcher to add.</param>
|
||||
@@ -21,4 +22,11 @@ public interface IRequestBuilder : IClientIPRequestBuilder
|
||||
/// The link back to the Mapping.
|
||||
/// </summary>
|
||||
IMapping Mapping { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Chooses the <see cref="IRequestMatcher"/> which immediately returns a mismatch during mappings enumeration.
|
||||
/// </summary>
|
||||
/// <param name="earlyMatcherType">Selected type to choose the matcher from available list.</param>
|
||||
/// <returns>The current <see cref="IRequestBuilder"/> instance.</returns>
|
||||
IRequestBuilder WithEarlyMismatch(RequestMatcherType? earlyMatcherType);
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using RestEase;
|
||||
using WireMock.Admin.Mappings;
|
||||
using WireMock.Client;
|
||||
using WireMock.Server;
|
||||
|
||||
namespace WireMock.Net.Tests.AdminApi;
|
||||
|
||||
public partial class WireMockAdminApiTests
|
||||
{
|
||||
[Fact]
|
||||
public async Task IWireMockAdminApi_PostMappingAsync_WithIsDisabledTrue_DoesNotMatchRequests()
|
||||
{
|
||||
// Arrange
|
||||
var ct = TestContext.Current.CancellationToken;
|
||||
using var server = WireMockServer.StartWithAdminInterface();
|
||||
var api = RestClient.For<IWireMockAdminApi>(server.Urls[0]);
|
||||
var httpClient = server.CreateClient();
|
||||
|
||||
var model = new MappingModel
|
||||
{
|
||||
Request = new RequestModel { Path = "/foo", Methods = ["GET"] },
|
||||
Response = new ResponseModel { Body = "hello", StatusCode = 200 },
|
||||
IsDisabled = true
|
||||
};
|
||||
|
||||
// Act — POST the disabled mapping
|
||||
var postResult = await api.PostMappingAsync(model, ct);
|
||||
postResult.Should().NotBeNull();
|
||||
|
||||
// Assert — request should not be matched (404)
|
||||
var response = await httpClient.GetAsync("/foo", ct);
|
||||
((int)response.StatusCode).Should().Be(404);
|
||||
|
||||
// Assert — mapping exists but IsDisabled is true
|
||||
server.Mappings.Where(m => !m.IsAdminInterface).Should().ContainSingle(m => m.IsDisabled == true);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task IWireMockAdminApi_DisableMappingAsync_PreventsMatching()
|
||||
{
|
||||
// Arrange
|
||||
var ct = TestContext.Current.CancellationToken;
|
||||
using var server = WireMockServer.StartWithAdminInterface();
|
||||
var api = RestClient.For<IWireMockAdminApi>(server.Urls[0]);
|
||||
var httpClient = server.CreateClient();
|
||||
|
||||
var model = new MappingModel
|
||||
{
|
||||
Request = new RequestModel { Path = "/bar", Methods = ["GET"] },
|
||||
Response = new ResponseModel { Body = "world", StatusCode = 200 }
|
||||
};
|
||||
var postResult = await api.PostMappingAsync(model, ct);
|
||||
var guid = postResult.Guid!.Value;
|
||||
|
||||
// Assert — mapping matches before disable
|
||||
var before = await httpClient.GetAsync("/bar", ct);
|
||||
((int)before.StatusCode).Should().Be(200);
|
||||
|
||||
// Act — disable
|
||||
var disableResult = await api.DisableMappingAsync(guid, ct);
|
||||
disableResult.Status.Should().Be("Mapping disabled");
|
||||
|
||||
// Assert — no match after disable
|
||||
var after = await httpClient.GetAsync("/bar", ct);
|
||||
((int)after.StatusCode).Should().Be(404);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task IWireMockAdminApi_EnableMappingAsync_ResumesMatching()
|
||||
{
|
||||
// Arrange
|
||||
var ct = TestContext.Current.CancellationToken;
|
||||
using var server = WireMockServer.StartWithAdminInterface();
|
||||
var api = RestClient.For<IWireMockAdminApi>(server.Urls[0]);
|
||||
var httpClient = server.CreateClient();
|
||||
|
||||
var model = new MappingModel
|
||||
{
|
||||
Request = new RequestModel { Path = "/baz", Methods = ["GET"] },
|
||||
Response = new ResponseModel { Body = "re-enabled", StatusCode = 200 },
|
||||
IsDisabled = true
|
||||
};
|
||||
var postResult = await api.PostMappingAsync(model, ct);
|
||||
var guid = postResult.Guid!.Value;
|
||||
|
||||
// Assert — no match while disabled
|
||||
var before = await httpClient.GetAsync("/baz", ct);
|
||||
((int)before.StatusCode).Should().Be(404);
|
||||
|
||||
// Act — enable
|
||||
var enableResult = await api.EnableMappingAsync(guid, ct);
|
||||
enableResult.Status.Should().Be("Mapping enabled");
|
||||
|
||||
// Assert — mapping matches after enable
|
||||
var after = await httpClient.GetAsync("/baz", ct);
|
||||
((int)after.StatusCode).Should().Be(200);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task IWireMockAdminApi_GetMappingAsync_ReturnsIsDisabledTrue_WhenDisabled()
|
||||
{
|
||||
// Arrange
|
||||
var ct = TestContext.Current.CancellationToken;
|
||||
using var server = WireMockServer.StartWithAdminInterface();
|
||||
var api = RestClient.For<IWireMockAdminApi>(server.Urls[0]);
|
||||
|
||||
var disabledModel = new MappingModel
|
||||
{
|
||||
Request = new RequestModel { Path = "/check-disabled" },
|
||||
Response = new ResponseModel { Body = "x", StatusCode = 200 },
|
||||
IsDisabled = true
|
||||
};
|
||||
var enabledModel = new MappingModel
|
||||
{
|
||||
Request = new RequestModel { Path = "/check-enabled" },
|
||||
Response = new ResponseModel { Body = "y", StatusCode = 200 }
|
||||
};
|
||||
|
||||
var disabledPost = await api.PostMappingAsync(disabledModel, ct);
|
||||
var enabledPost = await api.PostMappingAsync(enabledModel, ct);
|
||||
|
||||
// Act
|
||||
var disabledGot = await api.GetMappingAsync(disabledPost.Guid!.Value, ct);
|
||||
var enabledGot = await api.GetMappingAsync(enabledPost.Guid!.Value, ct);
|
||||
|
||||
// Assert — disabled mapping serializes IsDisabled = true
|
||||
disabledGot.IsDisabled.Should().BeTrue();
|
||||
|
||||
// Assert — enabled mapping omits IsDisabled (null = default not disabled)
|
||||
enabledGot.IsDisabled.Should().BeNull();
|
||||
}
|
||||
}
|
||||
@@ -6,5 +6,5 @@ internal static class Constants
|
||||
{
|
||||
internal const int NumStaticMappings = 10;
|
||||
|
||||
internal const int NumAdminMappings = 37;
|
||||
internal const int NumAdminMappings = 39;
|
||||
}
|
||||
@@ -8,8 +8,11 @@ using ExampleIntegrationTest.Lookup;
|
||||
using Google.Protobuf.WellKnownTypes;
|
||||
using Greet;
|
||||
using Grpc.Net.Client;
|
||||
using Moq;
|
||||
using WireMock.Constants;
|
||||
using WireMock.Matchers;
|
||||
using WireMock.Matchers.Request;
|
||||
using WireMock.Net.Xunit;
|
||||
using WireMock.RequestBuilders;
|
||||
using WireMock.ResponseBuilders;
|
||||
using WireMock.Server;
|
||||
@@ -78,7 +81,7 @@ import ""google/protobuf/empty.proto"";
|
||||
|
||||
service Greeter {
|
||||
rpc Nothing (google.protobuf.Empty) returns (google.protobuf.Empty);
|
||||
|
||||
|
||||
rpc SayHello (HelloRequest) returns (HelloReply);
|
||||
|
||||
rpc SayOther (Other) returns (HelloReply);
|
||||
@@ -731,6 +734,93 @@ message Other {
|
||||
Then_ReplyMessage_Should_BeCorrect(reply);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(true)]
|
||||
[InlineData(false)]
|
||||
public async Task WireMockServer_WithBodyAsProtoBuf_WithEarlyMismatch_ErrorLogs_Issue1442(
|
||||
bool withEarlyMismatch)
|
||||
{
|
||||
// Arrange
|
||||
var greeterId = $"test-greeter-{Guid.NewGuid()}";
|
||||
var policyId = $"test-policy-{Guid.NewGuid()}";
|
||||
var ct = TestContext.Current.CancellationToken;
|
||||
|
||||
var greeterProtoDefinition = ProtoDefinition;
|
||||
var policyProtoDefinition = File.ReadAllText("./Grpc/policy.proto");
|
||||
|
||||
var mockTestOutputHelper = new Mock<ITestOutputHelper>();
|
||||
using var server = WireMockServer.Start(new WireMockServerSettings
|
||||
{
|
||||
UseHttp2 = true,
|
||||
Logger = new TestOutputHelperWireMockLogger(mockTestOutputHelper.Object)
|
||||
});
|
||||
|
||||
server
|
||||
.AddProtoDefinition(greeterId, greeterProtoDefinition)
|
||||
.Given(Request.Create()
|
||||
.UsingPost()
|
||||
.WithHttpVersion("2")
|
||||
.WithPath("/greet.Greeter/SayHello")
|
||||
.WithEarlyMismatch(withEarlyMismatch
|
||||
? RequestMatcherType.Path
|
||||
: null)
|
||||
.WithBodyAsProtoBuf("greet.HelloRequest", new JsonMatcher(new { name = "stef" })))
|
||||
.WithProtoDefinition(greeterId)
|
||||
.RespondWith(Response.Create()
|
||||
.WithHeader("Content-Type", "application/grpc")
|
||||
.WithTrailingHeader("grpc-status", "0")
|
||||
.WithBodyAsProtoBuf("greet.HelloReply",
|
||||
new
|
||||
{
|
||||
message = "hello {{request.BodyAsJson.name}} {{request.method}}"
|
||||
})
|
||||
.WithTransformer());
|
||||
|
||||
server
|
||||
.AddProtoDefinition(policyId, policyProtoDefinition)
|
||||
.Given(Request.Create()
|
||||
.UsingPost()
|
||||
.WithHttpVersion("2")
|
||||
.WithPath("/Policy.PolicyService/GetVersion")
|
||||
.WithEarlyMismatch(withEarlyMismatch
|
||||
? RequestMatcherType.Path
|
||||
: null)
|
||||
.WithBodyAsProtoBuf("ExampleIntegrationTest.Lookup.GetVersionRequest", new NotNullOrEmptyMatcher()))
|
||||
.WithProtoDefinition(policyId)
|
||||
.RespondWith(Response.Create()
|
||||
.WithHeader("Content-Type", "application/grpc")
|
||||
.WithTrailingHeader("grpc-status", "0")
|
||||
.WithBodyAsProtoBuf("ExampleIntegrationTest.Lookup.GetVersionResponse",
|
||||
new GetVersionResponse
|
||||
{
|
||||
Version = "test",
|
||||
DateHired = new Timestamp
|
||||
{
|
||||
Seconds = 1722301323,
|
||||
Nanos = 12300
|
||||
},
|
||||
Client = new ExampleIntegrationTest.Lookup.Client
|
||||
{
|
||||
ClientName = ExampleIntegrationTest.Lookup.Client.Types.Clients.Test,
|
||||
CorrelationId = "correlation"
|
||||
}
|
||||
})
|
||||
.WithTransformer());
|
||||
|
||||
// Act
|
||||
var channel = GrpcChannel.ForAddress(server.Url!);
|
||||
var policyServiceClient = new PolicyService.PolicyServiceClient(channel);
|
||||
var greeterClient = new Greeter.GreeterClient(channel);
|
||||
|
||||
_ = await policyServiceClient.GetVersionAsync(new GetVersionRequest(), cancellationToken: ct);
|
||||
_ = await greeterClient.SayHelloAsync(new HelloRequest { Name = "stef" }, cancellationToken: ct);
|
||||
|
||||
mockTestOutputHelper.Verify(
|
||||
x => x.WriteLine(
|
||||
It.Is<string>(log => log.Contains("[Error]") && log.Contains("Exception"))),
|
||||
withEarlyMismatch ? Times.Never : Times.AtLeastOnce);
|
||||
}
|
||||
|
||||
private static WireMockServer Given_When_ServerStarted_And_RunningOnHttpAndGrpc()
|
||||
{
|
||||
var settings = new WireMockServerSettings
|
||||
|
||||
@@ -59,7 +59,8 @@ public class MappingBuilderTests
|
||||
mappingConverter,
|
||||
mappingToFileSaver,
|
||||
guidUtilsMock.Object,
|
||||
dateTimeUtilsMock.Object
|
||||
dateTimeUtilsMock.Object,
|
||||
new ResponseMessageBuilder(dateTimeUtilsMock.Object)
|
||||
);
|
||||
|
||||
_sut.Given(Request.Create()
|
||||
|
||||
@@ -5,6 +5,7 @@ using Moq;
|
||||
using WireMock.Logging;
|
||||
using WireMock.Owin;
|
||||
using WireMock.Owin.Mappers;
|
||||
using WireMock.Util;
|
||||
|
||||
namespace WireMock.Net.Tests.Owin;
|
||||
|
||||
@@ -12,6 +13,7 @@ public class GlobalExceptionMiddlewareTests
|
||||
{
|
||||
private readonly Mock<IWireMockMiddlewareOptions> _optionsMock;
|
||||
private readonly Mock<IOwinResponseMapper> _responseMapperMock;
|
||||
private readonly IResponseMessageBuilder _responseMessageBuilder;
|
||||
|
||||
private readonly GlobalExceptionMiddleware _sut;
|
||||
|
||||
@@ -23,7 +25,9 @@ public class GlobalExceptionMiddlewareTests
|
||||
_responseMapperMock = new Mock<IOwinResponseMapper>();
|
||||
_responseMapperMock.Setup(m => m.MapAsync(It.IsAny<ResponseMessage?>(), It.IsAny<HttpResponse>())).Returns(Task.FromResult(true));
|
||||
|
||||
_sut = new GlobalExceptionMiddleware(_ => Task.CompletedTask, _optionsMock.Object, _responseMapperMock.Object);
|
||||
_responseMessageBuilder = new ResponseMessageBuilder(new DateTimeUtils());
|
||||
|
||||
_sut = new GlobalExceptionMiddleware(_ => Task.CompletedTask, _optionsMock.Object, _responseMapperMock.Object, _responseMessageBuilder);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -37,7 +41,7 @@ public class GlobalExceptionMiddlewareTests
|
||||
public void GlobalExceptionMiddleware_Invoke_InvalidNext_ShouldCallResponseMapperWith500()
|
||||
{
|
||||
// Arrange
|
||||
var sut = new GlobalExceptionMiddleware(_ => throw new ArgumentException(), _optionsMock.Object, _responseMapperMock.Object);
|
||||
var sut = new GlobalExceptionMiddleware(_ => throw new ArgumentException(), _optionsMock.Object, _responseMapperMock.Object, _responseMessageBuilder);
|
||||
|
||||
// Act
|
||||
sut.Invoke(Mock.Of<HttpContext>());
|
||||
|
||||
@@ -56,6 +56,7 @@ public class MappingMatcherTests
|
||||
{
|
||||
// Assign
|
||||
var mappingMock = new Mock<IMapping>();
|
||||
mappingMock.SetupGet(m => m.IsDisabled).Returns(false);
|
||||
mappingMock.Setup(m => m.GetRequestMatchResult(It.IsAny<RequestMessage>(), It.IsAny<string>())).Throws<Exception>();
|
||||
|
||||
var mappings = new ConcurrentDictionary<Guid, IMapping>();
|
||||
@@ -229,6 +230,35 @@ public class MappingMatcherTests
|
||||
result.Match!.Mapping.Guid.Should().Be(withProbability);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MappingMatcher_FindBestMatch_WhenMappingIsDisabled_ShouldReturnNull()
|
||||
{
|
||||
// Assign
|
||||
var guid = Guid.Parse("00000000-0000-0000-0000-000000000001");
|
||||
var mappingMock = new Mock<IMapping>();
|
||||
mappingMock.SetupGet(m => m.Guid).Returns(guid);
|
||||
mappingMock.SetupGet(m => m.IsDisabled).Returns(true);
|
||||
mappingMock.SetupGet(m => m.Probability).Returns((double?)null);
|
||||
|
||||
var matchResult = new RequestMatchResult();
|
||||
matchResult.AddScore(typeof(object), 1.0, null);
|
||||
mappingMock.Setup(m => m.GetRequestMatchResult(It.IsAny<RequestMessage>(), It.IsAny<string>())).Returns(matchResult);
|
||||
|
||||
var mappings = new ConcurrentDictionary<Guid, IMapping>();
|
||||
mappings.TryAdd(guid, mappingMock.Object);
|
||||
_optionsMock.Setup(o => o.Mappings).Returns(mappings);
|
||||
|
||||
var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "GET", "::1");
|
||||
|
||||
// Act
|
||||
var result = _sut.FindBestMatch(request);
|
||||
|
||||
// Assert
|
||||
result.Match.Should().BeNull();
|
||||
result.Partial.Should().BeNull();
|
||||
mappingMock.Verify(m => m.GetRequestMatchResult(It.IsAny<RequestMessage>(), It.IsAny<string>()), Times.Never);
|
||||
}
|
||||
|
||||
private static ConcurrentDictionary<Guid, IMapping> InitMappings(params (Guid guid, double[] scores, double? probability)[] matches)
|
||||
{
|
||||
var mappings = new ConcurrentDictionary<Guid, IMapping>();
|
||||
@@ -237,6 +267,7 @@ public class MappingMatcherTests
|
||||
{
|
||||
var mappingMock = new Mock<IMapping>();
|
||||
mappingMock.SetupGet(m => m.Guid).Returns(match.guid);
|
||||
mappingMock.SetupGet(m => m.IsDisabled).Returns(false);
|
||||
|
||||
var requestMatchResult = new RequestMatchResult();
|
||||
foreach (var score in match.scores)
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.Diagnostics;
|
||||
using System.Linq.Expressions;
|
||||
using System.Net;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Moq;
|
||||
using WireMock.Admin.Mappings;
|
||||
@@ -38,6 +39,7 @@ public class WireMockMiddlewareTests
|
||||
private readonly Mock<HttpContext> _contextMock;
|
||||
private readonly Mock<IGuidUtils> _guidUtilsMock;
|
||||
private readonly Mock<IDateTimeUtils> _dateTimeUtilsMock;
|
||||
private readonly IResponseMessageBuilder _responseMessageBuilderMock;
|
||||
|
||||
private readonly WireMockMiddleware _sut;
|
||||
|
||||
@@ -51,6 +53,8 @@ public class WireMockMiddlewareTests
|
||||
_dateTimeUtilsMock = new Mock<IDateTimeUtils>();
|
||||
_dateTimeUtilsMock.Setup(d => d.UtcNow).Returns(UtcNow);
|
||||
|
||||
_responseMessageBuilderMock = new ResponseMessageBuilder(_dateTimeUtilsMock.Object);
|
||||
|
||||
_optionsMock = new Mock<IWireMockMiddlewareOptions>();
|
||||
_optionsMock.SetupAllProperties();
|
||||
_optionsMock.Setup(o => o.Mappings).Returns(_mappings);
|
||||
@@ -90,7 +94,8 @@ public class WireMockMiddlewareTests
|
||||
_matcherMock.Object,
|
||||
wireMockMiddlewareLoggerMock.Object,
|
||||
_guidUtilsMock.Object,
|
||||
_dateTimeUtilsMock.Object
|
||||
_dateTimeUtilsMock.Object,
|
||||
_responseMessageBuilderMock
|
||||
);
|
||||
}
|
||||
|
||||
@@ -103,7 +108,10 @@ public class WireMockMiddlewareTests
|
||||
// Assert and Verify
|
||||
_optionsMock.Verify(o => o.Logger.Warn(It.IsAny<string>(), It.IsAny<object[]>()), Times.Once);
|
||||
|
||||
Expression<Func<ResponseMessage, bool>> match = r => (int)r.StatusCode! == 404 && ((StatusModel)r.BodyData!.BodyAsJson!).Status == "No matching mapping found";
|
||||
Expression<Func<ResponseMessage, bool>> match = r =>
|
||||
(int)r.StatusCode! == 404 &&
|
||||
((StatusModel)r.BodyData!.BodyAsJson!).Status == "No matching mapping found" &&
|
||||
r.DateTime == UtcNow;
|
||||
_responseMapperMock.Verify(m => m.MapAsync(It.Is(match), It.IsAny<HttpResponse>()), Times.Once);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using Moq;
|
||||
using WireMock.Constants;
|
||||
using WireMock.Matchers;
|
||||
using WireMock.Matchers.Request;
|
||||
using WireMock.Models;
|
||||
using WireMock.RequestBuilders;
|
||||
|
||||
namespace WireMock.Net.Tests.RequestMatchers;
|
||||
|
||||
@@ -10,8 +13,12 @@ public class RequestMessageCompositeMatcherTests
|
||||
{
|
||||
private class Helper : RequestMessageCompositeMatcher
|
||||
{
|
||||
public Helper(IEnumerable<IRequestMatcher> requestMatchers, CompositeMatcherType type = CompositeMatcherType.And) : base(requestMatchers, type)
|
||||
public Helper(
|
||||
IEnumerable<IRequestMatcher> requestMatchers,
|
||||
CompositeMatcherType type = CompositeMatcherType.And,
|
||||
RequestMatcherType? earlyMatcherType = null) : base(requestMatchers, type)
|
||||
{
|
||||
EarlyMatcherType = earlyMatcherType;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,4 +84,74 @@ public class RequestMessageCompositeMatcherTests
|
||||
requestMatcher1Mock.Verify(rm => rm.GetMatchingScore(It.IsAny<RequestMessage>(), It.IsAny<RequestMatchResult>()), Times.Once);
|
||||
requestMatcher2Mock.Verify(rm => rm.GetMatchingScore(It.IsAny<RequestMessage>(), It.IsAny<RequestMatchResult>()), Times.Once);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RequestMessageCompositeMatcher_GetMatchingScore_EarlyMismatch()
|
||||
{
|
||||
// Assign
|
||||
var requestMatcher1Mock = new Mock<IRequestMatcher>();
|
||||
requestMatcher1Mock.Setup(rm => rm.GetMatchingScore(It.IsAny<RequestMessage>(), It.IsAny<RequestMatchResult>())).Returns(1.0d);
|
||||
var requestMatcher2Mock = new Mock<IRequestMatcher>();
|
||||
requestMatcher2Mock.Setup(rm => rm.GetMatchingScore(It.IsAny<RequestMessage>(), It.IsAny<RequestMatchResult>())).Returns(0.8d);
|
||||
var postMatcher = new RequestMessageMethodMatcher(HttpRequestMethod.POST);
|
||||
|
||||
var requestMessage = new RequestMessage(new UrlDetails("http://localhost"), HttpRequestMethod.GET, "127.0.0.1");
|
||||
var matcher = new Helper(
|
||||
[requestMatcher1Mock.Object, requestMatcher2Mock.Object, postMatcher],
|
||||
earlyMatcherType: RequestMatcherType.Method);
|
||||
|
||||
// Act
|
||||
var result = new RequestMatchResult();
|
||||
double score = matcher.GetMatchingScore(requestMessage, result);
|
||||
|
||||
// Assert
|
||||
score.Should().Be(0.0d);
|
||||
|
||||
// Verify
|
||||
requestMatcher1Mock.Verify(rm => rm.GetMatchingScore(It.IsAny<RequestMessage>(), It.IsAny<RequestMatchResult>()), Times.Never);
|
||||
requestMatcher2Mock.Verify(rm => rm.GetMatchingScore(It.IsAny<RequestMessage>(), It.IsAny<RequestMatchResult>()), Times.Never);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RequestMessageCompositeMatcher_GetMatchingScore_SeveralHeadersEarlyMismatch()
|
||||
{
|
||||
// Assign
|
||||
var headers = new Dictionary<string, string[]>
|
||||
{
|
||||
{ "teST", new[] { "x" } },
|
||||
{ "teST2", new[] { "z" } }
|
||||
};
|
||||
var requestMessage = new RequestMessage(new UrlDetails("http://localhost"), "GET", "127.0.0.1", null, headers);
|
||||
var request = Request.Create()
|
||||
.WithEarlyMismatch(RequestMatcherType.Header)
|
||||
.UsingAnyMethod()
|
||||
.WithHeader("teST", "x")
|
||||
.WithHeader("teST1", ["xx", "yy"])
|
||||
.WithHeader("teST2", ["y", "z"], matchOperator: MatchOperator.And);
|
||||
|
||||
// Act
|
||||
var score = request.GetMatchingScore(requestMessage, new RequestMatchResult());
|
||||
|
||||
// Assert
|
||||
score.Should().Be(0.0d);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RequestMessageCompositeMatcher_GetMatchingScore_SeveralParamEarlyMismatchSuccess()
|
||||
{
|
||||
// Assign
|
||||
var uriWithParams = new Uri("http://localhost?test1=1&test2=2");
|
||||
var requestMessage = new RequestMessage(new UrlDetails(uriWithParams), "GET", "127.0.0.1");
|
||||
var request = Request.Create()
|
||||
.WithEarlyMismatch(RequestMatcherType.Param)
|
||||
.UsingAnyMethod()
|
||||
.WithParam("test1", "1")
|
||||
.WithParam("test2", "2");
|
||||
|
||||
// Act
|
||||
var score = request.GetMatchingScore(requestMessage, new RequestMatchResult());
|
||||
|
||||
// Assert
|
||||
score.Should().Be(1.0d);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using WireMock.Matchers.Request;
|
||||
using WireMock.Net.Tests.VerifyExtensions;
|
||||
using WireMock.RequestBuilders;
|
||||
using WireMock.ResponseBuilders;
|
||||
@@ -101,6 +102,7 @@ public partial class MappingConverterTests
|
||||
var guid = new Guid("8e7b9ab7-e18e-4502-8bc9-11e6679811cc");
|
||||
var request = Request.Create()
|
||||
.UsingGet()
|
||||
.WithEarlyMismatch(RequestMatcherType.Method)
|
||||
.WithPath("/test_path")
|
||||
.WithParam("q", "42")
|
||||
.WithClientIP("112.123.100.99")
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
builder
|
||||
.Given(Request.Create()
|
||||
.WithEarlyMismatch(WireMock.Matchers.Request.RequestMatcherType.Method)
|
||||
.UsingMethod("GET")
|
||||
.WithPath(new WildcardMatcher(WireMock.Matchers.MatchBehaviour.AcceptOnMatch, "/test_path", false, WireMock.Matchers.MatchOperator.Or))
|
||||
.WithParam("q", new ExactMatcher(WireMock.Matchers.MatchBehaviour.AcceptOnMatch, false, WireMock.Matchers.MatchOperator.And, "42"))
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
var builder = new MappingBuilder();
|
||||
builder
|
||||
.Given(Request.Create()
|
||||
.WithEarlyMismatch(WireMock.Matchers.Request.RequestMatcherType.Method)
|
||||
.UsingMethod("GET")
|
||||
.WithPath(new WildcardMatcher(WireMock.Matchers.MatchBehaviour.AcceptOnMatch, "/test_path", false, WireMock.Matchers.MatchOperator.Or))
|
||||
.WithParam("q", new ExactMatcher(WireMock.Matchers.MatchBehaviour.AcceptOnMatch, false, WireMock.Matchers.MatchOperator.And, "42"))
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
server
|
||||
.Given(Request.Create()
|
||||
.WithEarlyMismatch(WireMock.Matchers.Request.RequestMatcherType.Method)
|
||||
.UsingMethod("GET")
|
||||
.WithPath(new WildcardMatcher(WireMock.Matchers.MatchBehaviour.AcceptOnMatch, "/test_path", false, WireMock.Matchers.MatchOperator.Or))
|
||||
.WithParam("q", new ExactMatcher(WireMock.Matchers.MatchBehaviour.AcceptOnMatch, false, WireMock.Matchers.MatchOperator.And, "42"))
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
var server = WireMockServer.Start();
|
||||
server
|
||||
.Given(Request.Create()
|
||||
.WithEarlyMismatch(WireMock.Matchers.Request.RequestMatcherType.Method)
|
||||
.UsingMethod("GET")
|
||||
.WithPath(new WildcardMatcher(WireMock.Matchers.MatchBehaviour.AcceptOnMatch, "/test_path", false, WireMock.Matchers.MatchOperator.Or))
|
||||
.WithParam("q", new ExactMatcher(WireMock.Matchers.MatchBehaviour.AcceptOnMatch, false, WireMock.Matchers.MatchOperator.And, "42"))
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
id:ID!
|
||||
firstName:String
|
||||
lastName:String
|
||||
fullName:String
|
||||
fullName:String
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
{
|
||||
Guid: Guid_1,
|
||||
UpdatedAt: DateTime_1,
|
||||
Title: ,
|
||||
Description: ,
|
||||
Priority: 42,
|
||||
Request: {
|
||||
Path: {
|
||||
Matchers: [
|
||||
{
|
||||
Name: WildcardMatcher,
|
||||
Pattern: 1.2.3.4,
|
||||
IgnoreCase: false
|
||||
}
|
||||
]
|
||||
},
|
||||
Headers: [
|
||||
{
|
||||
Name: x1,
|
||||
Matchers: [
|
||||
{
|
||||
Name: WildcardMatcher,
|
||||
Pattern: y,
|
||||
IgnoreCase: true
|
||||
}
|
||||
],
|
||||
IgnoreCase: true
|
||||
}
|
||||
],
|
||||
EarlyMatcherType: ClientIP
|
||||
},
|
||||
Response: {},
|
||||
UseWebhooksFireAndForget: false
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using WireMock.Matchers;
|
||||
using WireMock.Matchers.Request;
|
||||
using WireMock.Models;
|
||||
using WireMock.RequestBuilders;
|
||||
using WireMock.ResponseBuilders;
|
||||
@@ -538,7 +539,7 @@ message HelloReply {
|
||||
id:ID!
|
||||
firstName:String
|
||||
lastName:String
|
||||
fullName:String
|
||||
fullName:String
|
||||
}";
|
||||
var request = Request.Create().WithGraphQLSchema(schema);
|
||||
var response = Response.Create();
|
||||
@@ -640,4 +641,25 @@ message HelloReply {
|
||||
// Verify
|
||||
return Verify(model);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public Task ToMappingModel_Request_WithEarlyMismatch_ReturnsCorrectModel()
|
||||
{
|
||||
// Arrange
|
||||
var request = Request.Create().WithEarlyMismatch(RequestMatcherType.ClientIP)
|
||||
.WithHeader("x1", "y")
|
||||
.WithClientIP("1.2.3.4");
|
||||
var response = Response.Create();
|
||||
var mapping = new Mapping(_guid, _updatedAt, string.Empty, string.Empty, null, _settings, request, response, 42, null, null, null, null, null, false, null, null);
|
||||
|
||||
// Act
|
||||
var model = _sut.ToMappingModel(mapping);
|
||||
|
||||
// Assert
|
||||
model.Should().NotBeNull();
|
||||
model.Request.EarlyMatcherType.Should().Be(RequestMatcherType.ClientIP);
|
||||
|
||||
// Verify
|
||||
return Verify(model);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user