mirror of
https://github.com/wiremock/WireMock.Net.git
synced 2026-02-22 00:37:50 +01:00
Fix MimePartMatcher and add more tests (#1389)
* mp * . * --return * Fixed * -- * ... * fix * ... * .
This commit is contained in:
@@ -8,6 +8,12 @@ namespace WireMock.Admin.Mappings;
|
||||
[FluentBuilder.AutoGenerateBuilder]
|
||||
public class BodyModel
|
||||
{
|
||||
/// <summary>
|
||||
/// The name of the body matcher.
|
||||
/// Currently only "MultiPartMatcher" is supported.
|
||||
/// </summary>
|
||||
public string? MatcherName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the matcher.
|
||||
/// </summary>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System.Collections.Generic;
|
||||
using WireMock.Matchers.Request;
|
||||
|
||||
namespace WireMock.Admin.Requests;
|
||||
|
||||
@@ -47,5 +48,5 @@ public class LogRequestMatchModel
|
||||
/// <value>
|
||||
/// The match details.
|
||||
/// </value>
|
||||
public IList<object> MatchDetails { get; set; }
|
||||
public IList<MatchDetail> MatchDetails { get; set; } = [];
|
||||
}
|
||||
@@ -55,4 +55,11 @@ public interface IRequestMatchResult : IComparable
|
||||
/// <param name="exception">The exception [Optional].</param>
|
||||
/// <returns>The score.</returns>
|
||||
double AddScore(Type matcherType, double score, Exception? exception);
|
||||
|
||||
/// <summary>
|
||||
/// Adds the score.
|
||||
/// </summary>
|
||||
/// <param name="matchDetail">The matchDetail.</param>
|
||||
/// <returns>The score.</returns>
|
||||
double AddMatchDetail(MatchDetail matchDetail);
|
||||
}
|
||||
@@ -12,7 +12,12 @@ public class MatchDetail
|
||||
/// <summary>
|
||||
/// Gets or sets the type of the matcher.
|
||||
/// </summary>
|
||||
public Type MatcherType { get; set; } = null!;
|
||||
public required Type MatcherType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the type of the matcher.
|
||||
/// </summary>
|
||||
public required string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the score between 0.0 and 1.0
|
||||
@@ -24,4 +29,9 @@ public class MatchDetail
|
||||
/// [Optional]
|
||||
/// </summary>
|
||||
public Exception? Exception { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The child MatchResults in case of multiple matchers.
|
||||
/// </summary>
|
||||
public MatchDetail[]? MatchDetails { get; set; }
|
||||
}
|
||||
@@ -140,7 +140,7 @@ public class GraphQLMatcher : IGraphQLMatcher
|
||||
}
|
||||
}
|
||||
|
||||
return new MatchResult(MatchBehaviourHelper.Convert(MatchBehaviour, score), exception);
|
||||
return MatchResult.From(Name, MatchBehaviourHelper.Convert(MatchBehaviour, score), exception);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -97,7 +97,7 @@ public class CSharpCodeMatcher : ICSharpCodeMatcher
|
||||
}
|
||||
}
|
||||
|
||||
return new MatchResult(MatchBehaviourHelper.Convert(MatchBehaviour, score), exception);
|
||||
return MatchResult.From(Name, MatchBehaviourHelper.Convert(MatchBehaviour, score), exception);
|
||||
}
|
||||
|
||||
private bool IsMatch(dynamic input, string pattern)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using WireMock.Matchers.Helpers;
|
||||
using WireMock.Models.Mime;
|
||||
using WireMock.Util;
|
||||
@@ -12,7 +13,7 @@ namespace WireMock.Matchers;
|
||||
/// </summary>
|
||||
public class MimePartMatcher : IMimePartMatcher
|
||||
{
|
||||
private readonly Func<IMimePartData, MatchResult>[] _funcs;
|
||||
private readonly IList<(string Name, Func<IMimePartData, MatchResult> func)> _matcherFunctions;
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Name => nameof(MimePartMatcher);
|
||||
@@ -49,34 +50,47 @@ public class MimePartMatcher : IMimePartMatcher
|
||||
ContentTransferEncodingMatcher = contentTransferEncodingMatcher;
|
||||
ContentMatcher = contentMatcher;
|
||||
|
||||
_funcs =
|
||||
[
|
||||
mp => ContentTypeMatcher?.IsMatch(GetContentTypeAsString(mp.ContentType)) ?? MatchScores.Perfect,
|
||||
mp => ContentDispositionMatcher?.IsMatch(mp.ContentDisposition?.ToString()?.Replace("Content-Disposition: ", string.Empty)) ?? MatchScores.Perfect,
|
||||
mp => ContentTransferEncodingMatcher?.IsMatch(mp.ContentTransferEncoding.ToLowerInvariant()) ?? MatchScores.Perfect,
|
||||
MatchOnContent
|
||||
];
|
||||
_matcherFunctions = [];
|
||||
if (ContentTypeMatcher != null)
|
||||
{
|
||||
_matcherFunctions.Add((nameof(ContentTypeMatcher), mp => ContentTypeMatcher.IsMatch(GetContentTypeAsString(mp.ContentType))));
|
||||
}
|
||||
|
||||
if (ContentDispositionMatcher != null)
|
||||
{
|
||||
_matcherFunctions.Add((nameof(ContentDispositionMatcher), mp => ContentDispositionMatcher.IsMatch(mp.ContentDisposition?.ToString()?.Replace("Content-Disposition: ", string.Empty))));
|
||||
}
|
||||
|
||||
if (ContentTransferEncodingMatcher != null)
|
||||
{
|
||||
_matcherFunctions.Add((nameof(ContentTransferEncodingMatcher), mp => ContentTransferEncodingMatcher.IsMatch(mp.ContentTransferEncoding.ToLowerInvariant())));
|
||||
}
|
||||
|
||||
if (ContentMatcher != null)
|
||||
{
|
||||
_matcherFunctions.Add((ContentMatcher.Name, MatchOnContent));
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public MatchResult IsMatch(IMimePartData value)
|
||||
{
|
||||
var score = MatchScores.Mismatch;
|
||||
Exception? exception = null;
|
||||
var results = new List<MatchResult>();
|
||||
|
||||
try
|
||||
foreach (var matcherFunction in _matcherFunctions)
|
||||
{
|
||||
if (Array.TrueForAll(_funcs, func => func(value).IsPerfect()))
|
||||
try
|
||||
{
|
||||
score = MatchScores.Perfect;
|
||||
var matchResult = matcherFunction.func(value);
|
||||
results.Add(MatchResult.From(matcherFunction.Name, matchResult.Score));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
results.Add(MatchResult.From(matcherFunction.Name, MatchScores.Mismatch, ex));
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
exception = ex;
|
||||
}
|
||||
|
||||
return new MatchResult(MatchBehaviourHelper.Convert(MatchBehaviour, score), exception);
|
||||
return MatchResult.From(nameof(MimePartMatcher), results, MatchOperator.And);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -87,11 +101,6 @@ public class MimePartMatcher : IMimePartMatcher
|
||||
|
||||
private MatchResult MatchOnContent(IMimePartData mimePart)
|
||||
{
|
||||
if (ContentMatcher == null)
|
||||
{
|
||||
return MatchScores.Perfect;
|
||||
}
|
||||
|
||||
var bodyParserSettings = new BodyParserSettings
|
||||
{
|
||||
Stream = mimePart.Open(),
|
||||
@@ -102,7 +111,7 @@ public class MimePartMatcher : IMimePartMatcher
|
||||
};
|
||||
|
||||
var bodyData = BodyParser.ParseAsync(bodyParserSettings).ConfigureAwait(false).GetAwaiter().GetResult();
|
||||
return BodyDataMatchScoreCalculator.CalculateMatchScore(bodyData, ContentMatcher);
|
||||
return BodyDataMatchScoreCalculator.CalculateMatchScore(bodyData, ContentMatcher!);
|
||||
}
|
||||
|
||||
private static string? GetContentTypeAsString(IContentTypeData? contentType)
|
||||
|
||||
@@ -52,7 +52,7 @@ internal class MimeKitUtils : IMimeKitUtils
|
||||
return false;
|
||||
}
|
||||
|
||||
var fixedBytes = FixBytes(bytes, contentTypeHeader[0]);
|
||||
var fixedBytes = PrependContentTypeHeader(bytes, contentTypeHeader[0]);
|
||||
|
||||
mimeMessageData = LoadFromStream(new MemoryStream(fixedBytes));
|
||||
return true;
|
||||
@@ -68,7 +68,10 @@ internal class MimeKitUtils : IMimeKitUtils
|
||||
return contentTypeHeader.Any(ct => ct.TrimStart().StartsWith("multipart/", StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
|
||||
private static byte[] FixBytes(byte[] bytes, WireMockList<string> contentType)
|
||||
/// <summary>
|
||||
/// Prepends the Content-Type header to the byte array to make it a valid MIME message for MimeKit.
|
||||
/// </summary>
|
||||
private static byte[] PrependContentTypeHeader(byte[] bytes, WireMockList<string> contentType)
|
||||
{
|
||||
var contentTypeBytes = Encoding.UTF8.GetBytes($"{HttpKnownHeaderNames.ContentType}: {contentType}\r\n\r\n");
|
||||
|
||||
|
||||
@@ -54,7 +54,7 @@ internal class AzureADAuthenticationMatcher : IStringMatcher
|
||||
{
|
||||
if (string.IsNullOrEmpty(input))
|
||||
{
|
||||
return MatchScores.Mismatch;
|
||||
return MatchResult.From(Name);
|
||||
}
|
||||
|
||||
var token = Regex.Replace(input, BearerPrefix, string.Empty, RegexOptions.IgnoreCase, RegexConstants.DefaultTimeout);
|
||||
@@ -83,11 +83,11 @@ internal class AzureADAuthenticationMatcher : IStringMatcher
|
||||
// Throws an Exception as the token is invalid (expired, invalid-formatted, tenant mismatch, etc.)
|
||||
_jwtSecurityTokenHandler.ValidateToken(token, validationParameters, out _);
|
||||
|
||||
return MatchScores.Perfect;
|
||||
return MatchResult.From(Name, MatchScores.Perfect);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return new MatchResult(MatchScores.Mismatch, ex);
|
||||
return MatchResult.From(Name, ex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -69,7 +69,7 @@ public class WireMockConsoleLogger : IWireMockLogger
|
||||
/// <see cref="IWireMockLogger.DebugRequestResponse"/>
|
||||
public void DebugRequestResponse(LogEntryModel logEntryModel, bool isAdminRequest)
|
||||
{
|
||||
string message = JsonConvert.SerializeObject(logEntryModel, Formatting.Indented);
|
||||
string message = JsonConvert.SerializeObject(logEntryModel, Formatting.Indented, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore });
|
||||
Console.WriteLine(Format("DebugRequestResponse", "Admin[{0}] {1}", isAdminRequest, message));
|
||||
}
|
||||
|
||||
|
||||
46
src/WireMock.Net.Minimal/Matchers/CompositeMatcher.cs
Normal file
46
src/WireMock.Net.Minimal/Matchers/CompositeMatcher.cs
Normal file
@@ -0,0 +1,46 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System;
|
||||
|
||||
namespace WireMock.Matchers;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a matcher that combines multiple matching strategies into a single composite operation.
|
||||
/// </summary>
|
||||
public class CompositeMatcher : IMatcher
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public string Name => nameof(CompositeMatcher);
|
||||
|
||||
/// <summary>
|
||||
/// The logical operator used to combine the results of the matchers.
|
||||
/// </summary>
|
||||
public MatchOperator MatchOperator { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public MatchBehaviour MatchBehaviour { get; }
|
||||
|
||||
/// <summary>
|
||||
/// All matchers.
|
||||
/// </summary>
|
||||
public IMatcher[] Matchers { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the CompositeMatcher class with the specified matchers, operator, and match behaviour.
|
||||
/// </summary>
|
||||
/// <param name="matchers">An array of matchers to be combined. Cannot be null or contain null elements.</param>
|
||||
/// <param name="matchOperator">The logical operator used to combine the results of the matchers.</param>
|
||||
/// <param name="matchBehaviour">The behaviour that determines how the composite matcher interprets the combined results.</param>
|
||||
public CompositeMatcher(IMatcher[] matchers, MatchOperator matchOperator, MatchBehaviour matchBehaviour)
|
||||
{
|
||||
Matchers = matchers;
|
||||
MatchOperator = matchOperator;
|
||||
MatchBehaviour = matchBehaviour;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public string GetCSharpCodeArguments()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
@@ -62,7 +62,7 @@ public class ContentTypeMatcher : WildcardMatcher
|
||||
{
|
||||
if (string.IsNullOrEmpty(input) || !MediaTypeHeaderValue.TryParse(input, out var contentType))
|
||||
{
|
||||
return MatchBehaviourHelper.Convert(MatchBehaviour, MatchScores.Mismatch);
|
||||
return MatchResult.From(Name, MatchBehaviourHelper.Convert(MatchBehaviour, MatchScores.Mismatch));
|
||||
}
|
||||
|
||||
return base.IsMatch(contentType.MediaType);
|
||||
|
||||
@@ -75,7 +75,7 @@ public class ExactMatcher : IStringMatcher, IIgnoreCaseMatcher
|
||||
: pattern => pattern == input;
|
||||
|
||||
var score = MatchScores.ToScore(_values.Select(v => equals(v)).ToArray(), MatchOperator);
|
||||
return new MatchResult(MatchBehaviourHelper.Convert(MatchBehaviour, score));
|
||||
return MatchResult.From(Name, MatchBehaviourHelper.Convert(MatchBehaviour, score));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -106,18 +106,18 @@ public class FormUrlEncodedMatcher : IStringMatcher, IIgnoreCaseMatcher
|
||||
// Input is null or empty and if no patterns defined, return Perfect match.
|
||||
if (string.IsNullOrEmpty(input) && _patterns.Length == 0)
|
||||
{
|
||||
return new MatchResult(MatchScores.Perfect);
|
||||
return MatchResult.From(Name, MatchScores.Perfect);
|
||||
}
|
||||
|
||||
if (!QueryStringParser.TryParse(input, IgnoreCase, out var inputNameValueCollection))
|
||||
{
|
||||
return new MatchResult(MatchScores.Mismatch);
|
||||
return MatchResult.From(Name, MatchScores.Mismatch);
|
||||
}
|
||||
|
||||
var matches = GetMatches(inputNameValueCollection);
|
||||
|
||||
var score = MatchScores.ToScore(matches, MatchOperator);
|
||||
return new MatchResult(MatchBehaviourHelper.Convert(MatchBehaviour, score));
|
||||
return MatchResult.From(Name, MatchBehaviourHelper.Convert(MatchBehaviour, score));
|
||||
}
|
||||
|
||||
private bool[] GetMatches(IDictionary<string, string> inputNameValueCollection)
|
||||
|
||||
@@ -80,7 +80,7 @@ public class JsonPathMatcher : IStringMatcher, IObjectMatcher
|
||||
}
|
||||
}
|
||||
|
||||
return new MatchResult(MatchBehaviourHelper.Convert(MatchBehaviour, score), exception);
|
||||
return MatchResult.From(Name, MatchBehaviourHelper.Convert(MatchBehaviour, score), exception);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -104,7 +104,7 @@ public class JsonPathMatcher : IStringMatcher, IObjectMatcher
|
||||
}
|
||||
}
|
||||
|
||||
return new MatchResult(MatchBehaviourHelper.Convert(MatchBehaviour, score), exception);
|
||||
return MatchResult.From(Name, MatchBehaviourHelper.Convert(MatchBehaviour, score), exception);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -87,7 +87,7 @@ public class JmesPathMatcher : IStringMatcher, IObjectMatcher
|
||||
}
|
||||
}
|
||||
|
||||
return new MatchResult(MatchBehaviourHelper.Convert(MatchBehaviour, score), exception);
|
||||
return MatchResult.From(Name, MatchBehaviourHelper.Convert(MatchBehaviour, score), exception);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -102,7 +102,7 @@ public class JmesPathMatcher : IStringMatcher, IObjectMatcher
|
||||
return IsMatch(inputAsString);
|
||||
}
|
||||
|
||||
return MatchBehaviourHelper.Convert(MatchBehaviour, score);
|
||||
return MatchResult.From(Name, MatchBehaviourHelper.Convert(MatchBehaviour, score));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -96,7 +96,7 @@ public class JsonMatcher : IJsonMatcher
|
||||
}
|
||||
}
|
||||
|
||||
return new MatchResult(MatchBehaviourHelper.Convert(MatchBehaviour, score), error);
|
||||
return MatchResult.From(Name, MatchBehaviourHelper.Convert(MatchBehaviour, score), error);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -24,14 +24,26 @@ public class RequestMatchResult : IRequestMatchResult
|
||||
public double AverageTotalScore => TotalNumber == 0 ? MatchScores.Mismatch : TotalScore / TotalNumber;
|
||||
|
||||
/// <inheritdoc />
|
||||
public IList<MatchDetail> MatchDetails { get; } = new List<MatchDetail>();
|
||||
public IList<MatchDetail> MatchDetails { get; } = [];
|
||||
|
||||
/// <inheritdoc />
|
||||
public double AddScore(Type matcherType, double score, Exception? exception)
|
||||
{
|
||||
MatchDetails.Add(new MatchDetail { MatcherType = matcherType, Score = score, Exception = exception });
|
||||
return AddMatchDetail(new MatchDetail
|
||||
{
|
||||
Name = matcherType.Name.Replace("RequestMessage", string.Empty),
|
||||
MatcherType = matcherType,
|
||||
Score = score,
|
||||
Exception = exception
|
||||
});
|
||||
}
|
||||
|
||||
return score;
|
||||
/// <inheritdoc />
|
||||
public double AddMatchDetail(MatchDetail matchDetail)
|
||||
{
|
||||
MatchDetails.Add(matchDetail);
|
||||
|
||||
return matchDetail.Score;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -17,27 +17,27 @@ public class RequestMessageBodyMatcher : IRequestMatcher
|
||||
/// <summary>
|
||||
/// The body function
|
||||
/// </summary>
|
||||
public Func<string?, bool>? Func { get; }
|
||||
public Func<string?, bool>? MatchOnBodyAsStringFunc { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The body data function for byte[]
|
||||
/// </summary>
|
||||
public Func<byte[]?, bool>? DataFunc { get; }
|
||||
public Func<byte[]?, bool>? MatchOnBodyAsBytesFunc { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The body data function for json
|
||||
/// </summary>
|
||||
public Func<object?, bool>? JsonFunc { get; }
|
||||
public Func<object?, bool>? MatchOnBodyAsJsonFunc { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The body data function for BodyData
|
||||
/// </summary>
|
||||
public Func<IBodyData?, bool>? BodyDataFunc { get; }
|
||||
public Func<IBodyData?, bool>? MatchOnBodyAsBodyDataFunc { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The body data function for FormUrlEncoded
|
||||
/// </summary>
|
||||
public Func<IDictionary<string, string>?, bool>? FormUrlEncodedFunc { get; }
|
||||
public Func<IDictionary<string, string>?, bool>? MatchOnBodyAsFormUrlEncodedFunc { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The matchers.
|
||||
@@ -85,7 +85,7 @@ public class RequestMessageBodyMatcher : IRequestMatcher
|
||||
/// <param name="func">The function.</param>
|
||||
public RequestMessageBodyMatcher(Func<string?, bool> func)
|
||||
{
|
||||
Func = Guard.NotNull(func);
|
||||
MatchOnBodyAsStringFunc = Guard.NotNull(func);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -94,7 +94,7 @@ public class RequestMessageBodyMatcher : IRequestMatcher
|
||||
/// <param name="func">The function.</param>
|
||||
public RequestMessageBodyMatcher(Func<byte[]?, bool> func)
|
||||
{
|
||||
DataFunc = Guard.NotNull(func);
|
||||
MatchOnBodyAsBytesFunc = Guard.NotNull(func);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -103,7 +103,7 @@ public class RequestMessageBodyMatcher : IRequestMatcher
|
||||
/// <param name="func">The function.</param>
|
||||
public RequestMessageBodyMatcher(Func<object?, bool> func)
|
||||
{
|
||||
JsonFunc = Guard.NotNull(func);
|
||||
MatchOnBodyAsJsonFunc = Guard.NotNull(func);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -112,7 +112,7 @@ public class RequestMessageBodyMatcher : IRequestMatcher
|
||||
/// <param name="func">The function.</param>
|
||||
public RequestMessageBodyMatcher(Func<IBodyData?, bool> func)
|
||||
{
|
||||
BodyDataFunc = Guard.NotNull(func);
|
||||
MatchOnBodyAsBodyDataFunc = Guard.NotNull(func);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -121,7 +121,7 @@ public class RequestMessageBodyMatcher : IRequestMatcher
|
||||
/// <param name="func">The function.</param>
|
||||
public RequestMessageBodyMatcher(Func<IDictionary<string, string>?, bool> func)
|
||||
{
|
||||
FormUrlEncodedFunc = Guard.NotNull(func);
|
||||
MatchOnBodyAsFormUrlEncodedFunc = Guard.NotNull(func);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -147,43 +147,43 @@ public class RequestMessageBodyMatcher : IRequestMatcher
|
||||
/// <inheritdoc />
|
||||
public double GetMatchingScore(IRequestMessage requestMessage, IRequestMatchResult requestMatchResult)
|
||||
{
|
||||
var (score, exception) = CalculateMatchScore(requestMessage).Expand();
|
||||
var (score, exception) = CalculateMatchResult(requestMessage).Expand();
|
||||
return requestMatchResult.AddScore(GetType(), score, exception);
|
||||
}
|
||||
|
||||
private MatchResult CalculateMatchScore(IRequestMessage requestMessage)
|
||||
private MatchResult CalculateMatchResult(IRequestMessage requestMessage)
|
||||
{
|
||||
if (Matchers != null && Matchers.Any())
|
||||
{
|
||||
var results = Matchers.Select(matcher => BodyDataMatchScoreCalculator.CalculateMatchScore(requestMessage.BodyData, matcher)).ToArray();
|
||||
return MatchResult.From(results, MatchOperator);
|
||||
return MatchResult.From(nameof(RequestMessageBodyMatcher), results, MatchOperator);
|
||||
}
|
||||
|
||||
if (Func != null)
|
||||
if (MatchOnBodyAsStringFunc != null)
|
||||
{
|
||||
return MatchScores.ToScore(Func(requestMessage.BodyData?.BodyAsString));
|
||||
return MatchResult.From($"{nameof(RequestMessageBodyMatcher)}:{nameof(MatchOnBodyAsStringFunc)}", MatchScores.ToScore(MatchOnBodyAsStringFunc(requestMessage.BodyData?.BodyAsString)));
|
||||
}
|
||||
|
||||
if (FormUrlEncodedFunc != null)
|
||||
if (MatchOnBodyAsFormUrlEncodedFunc != null)
|
||||
{
|
||||
return MatchScores.ToScore(FormUrlEncodedFunc(requestMessage.BodyData?.BodyAsFormUrlEncoded));
|
||||
return MatchResult.From($"{nameof(RequestMessageBodyMatcher)}:{nameof(MatchOnBodyAsFormUrlEncodedFunc)}", MatchScores.ToScore(MatchOnBodyAsFormUrlEncodedFunc(requestMessage.BodyData?.BodyAsFormUrlEncoded)));
|
||||
}
|
||||
|
||||
if (JsonFunc != null)
|
||||
if (MatchOnBodyAsJsonFunc != null)
|
||||
{
|
||||
return MatchScores.ToScore(JsonFunc(requestMessage.BodyData?.BodyAsJson));
|
||||
return MatchResult.From($"{nameof(RequestMessageBodyMatcher)}:{nameof(MatchOnBodyAsJsonFunc)}", MatchScores.ToScore(MatchOnBodyAsJsonFunc(requestMessage.BodyData?.BodyAsJson)));
|
||||
}
|
||||
|
||||
if (DataFunc != null)
|
||||
if (MatchOnBodyAsBytesFunc != null)
|
||||
{
|
||||
return MatchScores.ToScore(DataFunc(requestMessage.BodyData?.BodyAsBytes));
|
||||
return MatchResult.From($"{nameof(RequestMessageBodyMatcher)}:{nameof(MatchOnBodyAsBytesFunc)}", MatchScores.ToScore(MatchOnBodyAsBytesFunc(requestMessage.BodyData?.BodyAsBytes)));
|
||||
}
|
||||
|
||||
if (BodyDataFunc != null)
|
||||
if (MatchOnBodyAsBodyDataFunc != null)
|
||||
{
|
||||
return MatchScores.ToScore(BodyDataFunc(requestMessage.BodyData));
|
||||
return MatchResult.From($"{nameof(RequestMessageBodyMatcher)}:{nameof(MatchOnBodyAsBodyDataFunc)}", MatchScores.ToScore(MatchOnBodyAsBodyDataFunc(requestMessage.BodyData)));
|
||||
}
|
||||
|
||||
return default;
|
||||
return MatchResult.From(nameof(RequestMessageBodyMatcher));
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,8 @@ namespace WireMock.Matchers.Request;
|
||||
/// </summary>
|
||||
public class RequestMessageBodyMatcher<T> : IRequestMatcher
|
||||
{
|
||||
private const string _name = nameof(RequestMessageBodyMatcher<T>);
|
||||
|
||||
/// <summary>
|
||||
/// The body data function for type T
|
||||
/// </summary>
|
||||
@@ -46,15 +48,15 @@ public class RequestMessageBodyMatcher<T> : IRequestMatcher
|
||||
try
|
||||
{
|
||||
var bodyAsT = jsonObject.ToObject<T>();
|
||||
return MatchScores.ToScore(Func(bodyAsT));
|
||||
return MatchResult.From(_name, MatchScores.ToScore(Func(bodyAsT)));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return new MatchResult(ex);
|
||||
return MatchResult.From(_name, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return default;
|
||||
return MatchResult.From(_name);
|
||||
}
|
||||
}
|
||||
@@ -14,6 +14,8 @@ namespace WireMock.Matchers.Request;
|
||||
/// </summary>
|
||||
public class RequestMessageClientIPMatcher : IRequestMatcher
|
||||
{
|
||||
private const string _name = nameof(RequestMessageClientIPMatcher);
|
||||
|
||||
/// <summary>
|
||||
/// The matchers
|
||||
/// </summary>
|
||||
@@ -77,8 +79,8 @@ public class RequestMessageClientIPMatcher : IRequestMatcher
|
||||
/// <inheritdoc />
|
||||
public double GetMatchingScore(IRequestMessage requestMessage, IRequestMatchResult requestMatchResult)
|
||||
{
|
||||
var (score, exception) = GetMatchResult(requestMessage).Expand();
|
||||
return requestMatchResult.AddScore(GetType(), score, exception);
|
||||
var matchDetail = GetMatchResult(requestMessage).ToMatchDetail();
|
||||
return requestMatchResult.AddMatchDetail(matchDetail);
|
||||
}
|
||||
|
||||
private MatchResult GetMatchResult(IRequestMessage requestMessage)
|
||||
@@ -86,15 +88,15 @@ public class RequestMessageClientIPMatcher : IRequestMatcher
|
||||
if (Matchers != null)
|
||||
{
|
||||
var results = Matchers.Select(m => m.IsMatch(requestMessage.ClientIP)).ToArray();
|
||||
return MatchResult.From(results, MatchOperator);
|
||||
return MatchResult.From(_name, results, MatchOperator);
|
||||
}
|
||||
|
||||
if (Funcs != null)
|
||||
{
|
||||
var results = Funcs.Select(func => func(requestMessage.ClientIP)).ToArray();
|
||||
return MatchScores.ToScore(results, MatchOperator);
|
||||
return MatchResult.From(_name, MatchScores.ToScore(results, MatchOperator));
|
||||
}
|
||||
|
||||
return default;
|
||||
return MatchResult.From(_name);
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,8 @@ namespace WireMock.Matchers.Request;
|
||||
/// <inheritdoc cref="IRequestMatcher"/>
|
||||
public class RequestMessageCookieMatcher : IRequestMatcher
|
||||
{
|
||||
private const string _name = nameof(RequestMessageCookieMatcher);
|
||||
|
||||
/// <summary>
|
||||
/// MatchBehaviour
|
||||
/// </summary>
|
||||
@@ -104,7 +106,7 @@ public class RequestMessageCookieMatcher : IRequestMatcher
|
||||
{
|
||||
if (requestMessage.Cookies == null)
|
||||
{
|
||||
return MatchBehaviourHelper.Convert(MatchBehaviour, MatchScores.Mismatch);
|
||||
return MatchResult.From(_name, MatchBehaviourHelper.Convert(MatchBehaviour, MatchScores.Mismatch));
|
||||
}
|
||||
|
||||
// Check if we want to use IgnoreCase to compare the Cookie-Name and Cookie-Value
|
||||
@@ -112,19 +114,19 @@ public class RequestMessageCookieMatcher : IRequestMatcher
|
||||
|
||||
if (Funcs != null)
|
||||
{
|
||||
return MatchScores.ToScore(Funcs.Any(f => f(cookies)));
|
||||
return MatchResult.From(_name, MatchScores.ToScore(Funcs.Any(f => f(cookies))));
|
||||
}
|
||||
|
||||
if (Matchers == null)
|
||||
{
|
||||
return default;
|
||||
return MatchResult.From(_name);
|
||||
}
|
||||
|
||||
if (!cookies.ContainsKey(Name))
|
||||
{
|
||||
return MatchBehaviourHelper.Convert(MatchBehaviour, MatchScores.Mismatch);
|
||||
return MatchResult.From(_name, MatchBehaviourHelper.Convert(MatchBehaviour, MatchScores.Mismatch));
|
||||
}
|
||||
|
||||
return Matchers.Max(m => m.IsMatch(cookies[Name]));
|
||||
return MatchResult.From(_name, Matchers.Max(m => m.IsMatch(cookies[Name]))?.Score ?? MatchScores.Mismatch);
|
||||
}
|
||||
}
|
||||
@@ -14,6 +14,8 @@ namespace WireMock.Matchers.Request;
|
||||
/// <inheritdoc cref="IRequestMatcher"/>
|
||||
public class RequestMessageHeaderMatcher : IRequestMatcher
|
||||
{
|
||||
private const string _name = nameof(RequestMessageCookieMatcher);
|
||||
|
||||
/// <summary>
|
||||
/// MatchBehaviour
|
||||
/// </summary>
|
||||
@@ -117,7 +119,7 @@ public class RequestMessageHeaderMatcher : IRequestMatcher
|
||||
{
|
||||
if (requestMessage.Headers == null)
|
||||
{
|
||||
return MatchBehaviourHelper.Convert(MatchBehaviour, MatchScores.Mismatch);
|
||||
return MatchResult.From(_name, MatchBehaviourHelper.Convert(MatchBehaviour, MatchScores.Mismatch));
|
||||
}
|
||||
|
||||
// Check if we want to use IgnoreCase to compare the Header-Name and Header-Value(s)
|
||||
@@ -126,14 +128,14 @@ public class RequestMessageHeaderMatcher : IRequestMatcher
|
||||
if (Funcs != null)
|
||||
{
|
||||
var funcResults = Funcs.Select(f => f(headers.ToDictionary(entry => entry.Key, entry => entry.Value.ToArray()))).ToArray();
|
||||
return MatchScores.ToScore(funcResults, MatchOperator);
|
||||
return MatchResult.From(_name, MatchScores.ToScore(funcResults, MatchOperator));
|
||||
}
|
||||
|
||||
if (Matchers != null)
|
||||
{
|
||||
if (!headers.ContainsKey(Name))
|
||||
{
|
||||
return MatchBehaviourHelper.Convert(MatchBehaviour, MatchScores.Mismatch);
|
||||
return MatchResult.From(_name, MatchBehaviourHelper.Convert(MatchBehaviour, MatchScores.Mismatch));
|
||||
}
|
||||
|
||||
var results = new List<MatchResult>();
|
||||
@@ -141,12 +143,12 @@ public class RequestMessageHeaderMatcher : IRequestMatcher
|
||||
{
|
||||
var resultsPerMatcher = headers[Name].Select(matcher.IsMatch).ToArray();
|
||||
|
||||
results.Add(MatchResult.From(resultsPerMatcher, MatchOperator.And));
|
||||
results.Add(MatchResult.From(_name, resultsPerMatcher, MatchOperator.And));
|
||||
}
|
||||
|
||||
return MatchResult.From(results, MatchOperator);
|
||||
return MatchResult.From(_name, results, MatchOperator);
|
||||
}
|
||||
|
||||
return MatchBehaviourHelper.Convert(MatchBehaviour, MatchScores.Mismatch);
|
||||
return MatchResult.From(_name, MatchBehaviourHelper.Convert(MatchBehaviour, MatchScores.Mismatch));
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,8 @@ namespace WireMock.Matchers.Request;
|
||||
/// </summary>
|
||||
public class RequestMessageHttpVersionMatcher : IRequestMatcher
|
||||
{
|
||||
private const string _name = nameof(RequestMessageHttpVersionMatcher);
|
||||
|
||||
/// <summary>
|
||||
/// The matcher.
|
||||
/// </summary>
|
||||
@@ -19,7 +21,7 @@ public class RequestMessageHttpVersionMatcher : IRequestMatcher
|
||||
/// <summary>
|
||||
/// The func.
|
||||
/// </summary>
|
||||
public Func<string, bool>? Func { get; }
|
||||
public Func<string, bool>? MatcherOnStringFunc { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="MatchBehaviour"/>
|
||||
@@ -61,7 +63,7 @@ public class RequestMessageHttpVersionMatcher : IRequestMatcher
|
||||
/// <param name="func">The function.</param>
|
||||
public RequestMessageHttpVersionMatcher(Func<string, bool> func)
|
||||
{
|
||||
Func = Guard.NotNull(func);
|
||||
MatcherOnStringFunc = Guard.NotNull(func);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -78,11 +80,11 @@ public class RequestMessageHttpVersionMatcher : IRequestMatcher
|
||||
return Matcher.IsMatch(requestMessage.HttpVersion);
|
||||
}
|
||||
|
||||
if (Func != null)
|
||||
if (MatcherOnStringFunc != null)
|
||||
{
|
||||
return MatchScores.ToScore(Func(requestMessage.HttpVersion));
|
||||
return MatchResult.From($"{_name}:{nameof(MatcherOnStringFunc)}", MatchScores.ToScore(MatcherOnStringFunc(requestMessage.HttpVersion)));
|
||||
}
|
||||
|
||||
return default;
|
||||
return MatchResult.From(_name);
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,11 @@ namespace WireMock.Matchers.Request;
|
||||
/// </summary>
|
||||
public class RequestMessageMultiPartMatcher : IRequestMatcher
|
||||
{
|
||||
/// <summary>
|
||||
/// The name of this matcher.
|
||||
/// </summary>
|
||||
public const string Name = "MultiPartMatcher";
|
||||
|
||||
private readonly IMimeKitUtils _mimeKitUtils = LoadMimeKitUtils();
|
||||
|
||||
/// <summary>
|
||||
@@ -22,7 +27,7 @@ public class RequestMessageMultiPartMatcher : IRequestMatcher
|
||||
/// <summary>
|
||||
/// The <see cref="MatchOperator"/>
|
||||
/// </summary>
|
||||
public MatchOperator MatchOperator { get; } = MatchOperator.Or;
|
||||
public MatchOperator MatchOperator { get; } = MatchOperator.And;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="MatchBehaviour"/>
|
||||
@@ -54,19 +59,20 @@ public class RequestMessageMultiPartMatcher : IRequestMatcher
|
||||
/// <inheritdoc />
|
||||
public double GetMatchingScore(IRequestMessage requestMessage, IRequestMatchResult requestMatchResult)
|
||||
{
|
||||
var score = MatchScores.Mismatch;
|
||||
var matchDetail = MatchResult.From(Name).ToMatchDetail();
|
||||
Exception? exception = null;
|
||||
|
||||
if (Matchers?.Any() != true)
|
||||
if (Matchers == null)
|
||||
{
|
||||
return requestMatchResult.AddScore(GetType(), score, null);
|
||||
return requestMatchResult.AddMatchDetail(matchDetail);
|
||||
}
|
||||
|
||||
if (!_mimeKitUtils.TryGetMimeMessage(requestMessage, out var message))
|
||||
{
|
||||
return requestMatchResult.AddScore(GetType(), score, null);
|
||||
return requestMatchResult.AddMatchDetail(matchDetail);
|
||||
}
|
||||
|
||||
double score = MatchScores.Mismatch;
|
||||
try
|
||||
{
|
||||
foreach (var mimePartMatcher in Matchers.OfType<IMimePartMatcher>().ToArray())
|
||||
@@ -94,7 +100,7 @@ public class RequestMessageMultiPartMatcher : IRequestMatcher
|
||||
exception = ex;
|
||||
}
|
||||
|
||||
return requestMatchResult.AddScore(GetType(), score, exception);
|
||||
return requestMatchResult.AddMatchDetail(MatchResult.From(Name, score, exception).ToMatchDetail());
|
||||
}
|
||||
|
||||
private static IMimeKitUtils LoadMimeKitUtils()
|
||||
|
||||
@@ -77,8 +77,8 @@ public class RequestMessagePathMatcher : IRequestMatcher
|
||||
/// <inheritdoc />
|
||||
public double GetMatchingScore(IRequestMessage requestMessage, IRequestMatchResult requestMatchResult)
|
||||
{
|
||||
var (score, exception) = GetMatchResult(requestMessage).Expand();
|
||||
return requestMatchResult.AddScore(GetType(), score, exception);
|
||||
var matchDetail = GetMatchResult(requestMessage).ToMatchDetail();
|
||||
return requestMatchResult.AddMatchDetail(matchDetail);
|
||||
}
|
||||
|
||||
private MatchResult GetMatchResult(IRequestMessage requestMessage)
|
||||
@@ -86,15 +86,16 @@ public class RequestMessagePathMatcher : IRequestMatcher
|
||||
if (Matchers != null)
|
||||
{
|
||||
var results = Matchers.Select(m => m.IsMatch(requestMessage.Path)).ToArray();
|
||||
return MatchResult.From(results, MatchOperator);
|
||||
return MatchResult.From(nameof(RequestMessagePathMatcher), results, MatchOperator);
|
||||
}
|
||||
|
||||
if (Funcs != null)
|
||||
{
|
||||
var results = Funcs.Select(func => func(requestMessage.Path)).ToArray();
|
||||
return MatchScores.ToScore(results, MatchOperator);
|
||||
var score = MatchScores.ToScore(results, MatchOperator);
|
||||
return MatchResult.From(nameof(RequestMessagePathMatcher), score);
|
||||
}
|
||||
|
||||
return default;
|
||||
return MatchResult.From(nameof(RequestMessagePathMatcher));
|
||||
}
|
||||
}
|
||||
@@ -86,15 +86,16 @@ public class RequestMessageUrlMatcher : IRequestMatcher
|
||||
if (Matchers != null)
|
||||
{
|
||||
var results = Matchers.Select(m => m.IsMatch(requestMessage.Url)).ToArray();
|
||||
return MatchResult.From(results, MatchOperator);
|
||||
return MatchResult.From(nameof(RequestMessageUrlMatcher), results, MatchOperator);
|
||||
}
|
||||
|
||||
if (Funcs != null)
|
||||
{
|
||||
var results = Funcs.Select(func => func(requestMessage.Url)).ToArray();
|
||||
return MatchScores.ToScore(results, MatchOperator);
|
||||
var score = MatchScores.ToScore(results, MatchOperator);
|
||||
return MatchResult.From(nameof(RequestMessageUrlMatcher), score);
|
||||
}
|
||||
|
||||
return default;
|
||||
return MatchResult.From(nameof(RequestMessageUrlMatcher));
|
||||
}
|
||||
}
|
||||
@@ -86,7 +86,7 @@ public class SimMetricsMatcher : IStringMatcher
|
||||
IStringMetric stringMetricType = GetStringMetricType();
|
||||
|
||||
var score = MatchScores.ToScore(_patterns.Select(p => stringMetricType.GetSimilarity(p.GetPattern(), input)).ToArray(), MatchOperator);
|
||||
return MatchBehaviourHelper.Convert(MatchBehaviour, score);
|
||||
return MatchResult.From(Name, MatchBehaviourHelper.Convert(MatchBehaviour, score));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -116,7 +116,7 @@ public class XPathMatcher : IStringMatcher
|
||||
|
||||
private MatchResult CreateMatchResult(double score, Exception? exception = null)
|
||||
{
|
||||
return new MatchResult(MatchBehaviourHelper.Convert(MatchBehaviour, score), exception);
|
||||
return MatchResult.From(Name, MatchBehaviourHelper.Convert(MatchBehaviour, score), exception);
|
||||
}
|
||||
|
||||
private sealed class XPathEvaluator
|
||||
|
||||
@@ -15,7 +15,7 @@ public partial class Request
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IRequestBuilder WithMultiPart(IMatcher[] matchers, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch, MatchOperator matchOperator = MatchOperator.Or)
|
||||
public IRequestBuilder WithMultiPart(IMatcher[] matchers, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch, MatchOperator matchOperator = MatchOperator.And)
|
||||
{
|
||||
_requestMatchers.Add(new RequestMessageMultiPartMatcher(matchBehaviour, matchOperator, matchers));
|
||||
return this;
|
||||
|
||||
@@ -166,11 +166,12 @@ internal class LogEntryMapper
|
||||
TotalScore = matchResult.TotalScore,
|
||||
TotalNumber = matchResult.TotalNumber,
|
||||
AverageTotalScore = matchResult.AverageTotalScore,
|
||||
MatchDetails = matchResult.MatchDetails.Select(md => new
|
||||
{
|
||||
Name = md.MatcherType.Name.Replace("RequestMessage", string.Empty),
|
||||
md.Score
|
||||
} as object).ToList()
|
||||
MatchDetails = matchResult.MatchDetails
|
||||
//MatchDetails = matchResult.MatchDetails.Select(md => new
|
||||
//{
|
||||
// Name = md.MatcherType.Name.Replace("RequestMessage", string.Empty),
|
||||
// md.Score
|
||||
//} as object).ToList()
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -25,9 +25,9 @@ internal class MatcherMapper
|
||||
_settings = Guard.NotNull(settings);
|
||||
}
|
||||
|
||||
public IMatcher[]? Map(IEnumerable<MatcherModel>? matchers)
|
||||
public IMatcher[] Map(IEnumerable<MatcherModel>? matchers)
|
||||
{
|
||||
return matchers?.Select(Map).OfType<IMatcher>().ToArray();
|
||||
return matchers?.Select(Map).OfType<IMatcher>().ToArray() ?? [];
|
||||
}
|
||||
|
||||
public IMatcher? Map(MatcherModel? matcherModel)
|
||||
|
||||
@@ -6,6 +6,7 @@ using System.Linq;
|
||||
using Stef.Validation;
|
||||
using WireMock.Admin.Mappings;
|
||||
using WireMock.Matchers;
|
||||
using WireMock.Matchers.Request;
|
||||
using WireMock.RequestBuilders;
|
||||
using WireMock.ResponseBuilders;
|
||||
using WireMock.Serialization;
|
||||
@@ -253,7 +254,15 @@ public partial class WireMockServer
|
||||
else if (requestModel.Body?.Matchers != null)
|
||||
{
|
||||
var matchOperator = StringUtils.ParseMatchOperator(requestModel.Body.MatchOperator);
|
||||
requestBuilder = requestBuilder.WithBody(_matcherMapper.Map(requestModel.Body.Matchers)!, matchOperator);
|
||||
|
||||
if (requestModel.Body.MatcherName == RequestMessageMultiPartMatcher.Name)
|
||||
{
|
||||
requestBuilder = requestBuilder.WithMultiPart(_matcherMapper.Map(requestModel.Body.Matchers), matchOperator: matchOperator);
|
||||
}
|
||||
else
|
||||
{
|
||||
requestBuilder = requestBuilder.WithBody(_matcherMapper.Map(requestModel.Body.Matchers), matchOperator);
|
||||
}
|
||||
}
|
||||
|
||||
return requestBuilder;
|
||||
|
||||
@@ -57,7 +57,7 @@ public class ProtoBufMatcher : IProtoBufMatcher
|
||||
/// <inheritdoc />
|
||||
public async Task<MatchResult> IsMatchAsync(byte[]? input, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var result = new MatchResult();
|
||||
var result = MatchResult.From(Name);
|
||||
|
||||
if (input != null)
|
||||
{
|
||||
@@ -65,11 +65,11 @@ public class ProtoBufMatcher : IProtoBufMatcher
|
||||
{
|
||||
var instance = await DecodeAsync(input, true, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
result = Matcher?.IsMatch(instance) ?? new MatchResult(MatchScores.Perfect);
|
||||
result = Matcher?.IsMatch(instance) ?? MatchResult.From(Name, MatchScores.Perfect);
|
||||
}
|
||||
catch (Exception e)
|
||||
catch (Exception ex)
|
||||
{
|
||||
result = new MatchResult(MatchScores.Mismatch, e);
|
||||
result = MatchResult.From(Name, ex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -68,7 +68,7 @@ public class ExactObjectMatcher : IObjectMatcher
|
||||
equals = Equals(Value, input);
|
||||
}
|
||||
|
||||
return MatchBehaviourHelper.Convert(MatchBehaviour, MatchScores.ToScore(equals));
|
||||
return MatchResult.From(Name, MatchBehaviourHelper.Convert(MatchBehaviour, MatchScores.ToScore(equals)));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -8,66 +8,68 @@ namespace WireMock.Matchers.Helpers;
|
||||
|
||||
internal static class BodyDataMatchScoreCalculator
|
||||
{
|
||||
internal static MatchResult CalculateMatchScore(IBodyData? requestMessage, IMatcher matcher)
|
||||
private static string _name = nameof(BodyDataMatchScoreCalculator);
|
||||
|
||||
internal static MatchResult CalculateMatchScore(IBodyData? bodyData, IMatcher matcher)
|
||||
{
|
||||
Guard.NotNull(matcher);
|
||||
|
||||
if (requestMessage == null)
|
||||
if (bodyData == null)
|
||||
{
|
||||
return default;
|
||||
return MatchResult.From(_name);
|
||||
}
|
||||
|
||||
if (matcher is NotNullOrEmptyMatcher notNullOrEmptyMatcher)
|
||||
{
|
||||
switch (requestMessage.DetectedBodyType)
|
||||
switch (bodyData.DetectedBodyType)
|
||||
{
|
||||
case BodyType.Json:
|
||||
case BodyType.String:
|
||||
case BodyType.FormUrlEncoded:
|
||||
return notNullOrEmptyMatcher.IsMatch(requestMessage.BodyAsString);
|
||||
return notNullOrEmptyMatcher.IsMatch(bodyData.BodyAsString);
|
||||
|
||||
case BodyType.Bytes:
|
||||
return notNullOrEmptyMatcher.IsMatch(requestMessage.BodyAsBytes);
|
||||
return notNullOrEmptyMatcher.IsMatch(bodyData.BodyAsBytes);
|
||||
|
||||
default:
|
||||
return default;
|
||||
return MatchResult.From(_name);
|
||||
}
|
||||
}
|
||||
|
||||
if (matcher is ExactObjectMatcher { Value: byte[] } exactObjectMatcher)
|
||||
{
|
||||
// If the body is a byte array, try to match.
|
||||
return exactObjectMatcher.IsMatch(requestMessage.BodyAsBytes);
|
||||
return exactObjectMatcher.IsMatch(bodyData.BodyAsBytes);
|
||||
}
|
||||
|
||||
// Check if the matcher is a IObjectMatcher
|
||||
if (matcher is IObjectMatcher objectMatcher)
|
||||
{
|
||||
// If the body is a JSON object, try to match.
|
||||
if (requestMessage.DetectedBodyType == BodyType.Json)
|
||||
if (bodyData.DetectedBodyType == BodyType.Json)
|
||||
{
|
||||
return objectMatcher.IsMatch(requestMessage.BodyAsJson);
|
||||
return objectMatcher.IsMatch(bodyData.BodyAsJson);
|
||||
}
|
||||
|
||||
// If the body is a byte array, try to match.
|
||||
if (requestMessage.DetectedBodyType == BodyType.Bytes)
|
||||
if (bodyData.DetectedBodyType == BodyType.Bytes)
|
||||
{
|
||||
return objectMatcher.IsMatch(requestMessage.BodyAsBytes);
|
||||
return objectMatcher.IsMatch(bodyData.BodyAsBytes);
|
||||
}
|
||||
}
|
||||
|
||||
// In case the matcher is a IStringMatcher and If body is a Json or a String, use the BodyAsString to match on.
|
||||
if (matcher is IStringMatcher stringMatcher && requestMessage.DetectedBodyType is BodyType.Json or BodyType.String or BodyType.FormUrlEncoded)
|
||||
// In case the matcher is a IStringMatcher and if body is a Json or a String, use the BodyAsString to match on.
|
||||
if (matcher is IStringMatcher stringMatcher && bodyData.DetectedBodyType is BodyType.Json or BodyType.String or BodyType.FormUrlEncoded)
|
||||
{
|
||||
return stringMatcher.IsMatch(requestMessage.BodyAsString);
|
||||
return stringMatcher.IsMatch(bodyData.BodyAsString);
|
||||
}
|
||||
|
||||
// In case the matcher is a IProtoBufMatcher, use the BodyAsBytes to match on.
|
||||
if (matcher is IProtoBufMatcher protoBufMatcher)
|
||||
{
|
||||
return protoBufMatcher.IsMatchAsync(requestMessage.BodyAsBytes).GetAwaiter().GetResult();
|
||||
return protoBufMatcher.IsMatchAsync(bodyData.BodyAsBytes).GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
return default;
|
||||
return MatchResult.From(_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -36,6 +36,6 @@ internal static class MatchBehaviourHelper
|
||||
/// <returns>match result</returns>
|
||||
internal static MatchResult Convert(MatchBehaviour matchBehaviour, MatchResult result)
|
||||
{
|
||||
return matchBehaviour == MatchBehaviour.AcceptOnMatch ? result : new MatchResult(Convert(matchBehaviour, result.Score), result.Exception);
|
||||
return matchBehaviour == MatchBehaviour.AcceptOnMatch ? result : MatchResult.From(result.Name, Convert(matchBehaviour, result.Score), result.Exception);
|
||||
}
|
||||
}
|
||||
@@ -5,13 +5,14 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Stef.Validation;
|
||||
using WireMock.Extensions;
|
||||
using WireMock.Matchers.Request;
|
||||
|
||||
namespace WireMock.Matchers;
|
||||
|
||||
/// <summary>
|
||||
/// The MatchResult which contains the score (value between 0.0 - 1.0 of the similarity) and an optional error message.
|
||||
/// </summary>
|
||||
public struct MatchResult
|
||||
public class MatchResult
|
||||
{
|
||||
/// <summary>
|
||||
/// A value between 0.0 - 1.0 of the similarity.
|
||||
@@ -25,46 +26,55 @@ public struct MatchResult
|
||||
public Exception? Exception { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Create a MatchResult
|
||||
/// The name or description of the matcher.
|
||||
/// </summary>
|
||||
/// <param name="score">A value between 0.0 - 1.0 of the similarity.</param>
|
||||
/// <param name="exception">The exception in case the matching fails. [Optional]</param>
|
||||
public MatchResult(double score, Exception? exception = null)
|
||||
{
|
||||
Score = score;
|
||||
Exception = exception;
|
||||
}
|
||||
public required string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Create a MatchResult
|
||||
/// The child MatchResults in case of multiple matchers.
|
||||
/// </summary>
|
||||
/// <param name="exception">The exception in case the matching fails.</param>
|
||||
public MatchResult(Exception exception)
|
||||
{
|
||||
Exception = Guard.NotNull(exception);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Implicitly converts a double to a MatchResult.
|
||||
/// </summary>
|
||||
/// <param name="score">The score</param>
|
||||
public static implicit operator MatchResult(double score)
|
||||
{
|
||||
return new MatchResult(score);
|
||||
}
|
||||
public MatchResult[]? MatchResults { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Is the value a perfect match?
|
||||
/// </summary>
|
||||
public bool IsPerfect() => MatchScores.IsPerfect(Score);
|
||||
|
||||
/// <summary>
|
||||
/// Create a MatchResult.
|
||||
/// </summary>
|
||||
/// <param name="name">The name or description of the matcher.</param>
|
||||
/// <param name="score">A value between 0.0 - 1.0 of the similarity.</param>
|
||||
/// <param name="exception">The exception in case the matching fails. [Optional]</param>
|
||||
public static MatchResult From(string name, double score = 0, Exception? exception = null)
|
||||
{
|
||||
return new MatchResult
|
||||
{
|
||||
Name = name,
|
||||
Score = score,
|
||||
Exception = exception
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a MatchResult from exception.
|
||||
/// </summary>
|
||||
/// <param name="name">The name or description of the matcher.</param>
|
||||
/// <param name="exception">The exception in case the matching fails.</param>
|
||||
/// <returns>MatchResult</returns>
|
||||
public static MatchResult From(string name, Exception exception)
|
||||
{
|
||||
return From(name, MatchScores.Mismatch, exception);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a MatchResult from multiple MatchResults.
|
||||
/// </summary>
|
||||
/// <param name="name">The name or description of the matcher.</param>
|
||||
/// <param name="matchResults">A list of MatchResults.</param>
|
||||
/// <param name="matchOperator">The MatchOperator</param>
|
||||
/// <returns>MatchResult</returns>
|
||||
public static MatchResult From(IReadOnlyList<MatchResult> matchResults, MatchOperator matchOperator)
|
||||
public static MatchResult From(string name, IReadOnlyList<MatchResult> matchResults, MatchOperator matchOperator)
|
||||
{
|
||||
Guard.NotNullOrEmpty(matchResults);
|
||||
|
||||
@@ -75,6 +85,8 @@ public struct MatchResult
|
||||
|
||||
return new MatchResult
|
||||
{
|
||||
Name = name,
|
||||
MatchResults = matchResults.ToArray(),
|
||||
Score = MatchScores.ToScore(matchResults.Select(r => r.Score).ToArray(), matchOperator),
|
||||
Exception = matchResults.Select(m => m.Exception).OfType<Exception>().ToArray().ToException()
|
||||
};
|
||||
@@ -88,4 +100,19 @@ public struct MatchResult
|
||||
{
|
||||
return (Score, Exception);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert to <see cref="MatchResult"/>.
|
||||
/// </summary>
|
||||
public MatchDetail ToMatchDetail()
|
||||
{
|
||||
return new MatchDetail
|
||||
{
|
||||
Name = Name,
|
||||
MatcherType = typeof(MatchResult),
|
||||
Score = Score,
|
||||
Exception = Exception,
|
||||
MatchDetails = MatchResults?.Select(mr => mr.ToMatchDetail()).ToArray()
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using AnyOfTypes;
|
||||
using WireMock.Extensions;
|
||||
@@ -53,7 +52,7 @@ public class NotNullOrEmptyMatcher : IObjectMatcher, IStringMatcher
|
||||
break;
|
||||
}
|
||||
|
||||
return MatchBehaviourHelper.Convert(MatchBehaviour, MatchScores.ToScore(match));
|
||||
return MatchResult.From(Name, MatchBehaviourHelper.Convert(MatchBehaviour, MatchScores.ToScore(match)));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -61,7 +60,7 @@ public class NotNullOrEmptyMatcher : IObjectMatcher, IStringMatcher
|
||||
{
|
||||
var match = !string.IsNullOrEmpty(input);
|
||||
|
||||
return MatchBehaviourHelper.Convert(MatchBehaviour, MatchScores.ToScore(match));
|
||||
return MatchResult.From(Name, MatchBehaviourHelper.Convert(MatchBehaviour, MatchScores.ToScore(match)));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -111,7 +111,7 @@ public class RegexMatcher : IStringMatcher, IIgnoreCaseMatcher
|
||||
}
|
||||
}
|
||||
|
||||
return new MatchResult(MatchBehaviourHelper.Convert(MatchBehaviour, score), exception);
|
||||
return MatchResult.From(Name, MatchBehaviourHelper.Convert(MatchBehaviour, score), exception);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -73,7 +73,7 @@ public class RequestMessageGraphQLMatcher : IRequestMatcher
|
||||
public double GetMatchingScore(IRequestMessage requestMessage, IRequestMatchResult requestMatchResult)
|
||||
{
|
||||
var results = CalculateMatchResults(requestMessage);
|
||||
var (score, exception) = MatchResult.From(results, MatchOperator).Expand();
|
||||
var (score, exception) = MatchResult.From(nameof(RequestMessageGraphQLMatcher), results, MatchOperator).Expand();
|
||||
|
||||
return requestMatchResult.AddScore(GetType(), score, exception);
|
||||
}
|
||||
@@ -86,12 +86,12 @@ public class RequestMessageGraphQLMatcher : IRequestMatcher
|
||||
return stringMatcher.IsMatch(requestMessage.BodyData.BodyAsString);
|
||||
}
|
||||
|
||||
return default;
|
||||
return MatchResult.From(nameof(RequestMessageGraphQLMatcher));
|
||||
}
|
||||
|
||||
private IReadOnlyList<MatchResult> CalculateMatchResults(IRequestMessage requestMessage)
|
||||
{
|
||||
return Matchers == null ? [new MatchResult()] : Matchers.Select(matcher => CalculateMatchResult(requestMessage, matcher)).ToArray();
|
||||
return Matchers == null ? [MatchResult.From(nameof(RequestMessageGraphQLMatcher))] : Matchers.Select(matcher => CalculateMatchResult(requestMessage, matcher)).ToArray();
|
||||
}
|
||||
|
||||
private static IMatcher[] CreateMatcherArray(
|
||||
|
||||
@@ -23,7 +23,7 @@ public interface IMultiPartRequestBuilder : IHttpVersionBuilder
|
||||
/// <param name="matchBehaviour">The <see cref="MatchBehaviour"/> to use.</param>
|
||||
/// <param name="matchOperator">The <see cref="MatchOperator"/> to use.</param>
|
||||
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
|
||||
IRequestBuilder WithMultiPart(IMatcher[] matchers, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch, MatchOperator matchOperator = MatchOperator.Or);
|
||||
IRequestBuilder WithMultiPart(IMatcher[] matchers, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch, MatchOperator matchOperator = MatchOperator.And);
|
||||
|
||||
/// <summary>
|
||||
/// WithMultiPart: MatchBehaviour and IMatcher[]
|
||||
|
||||
Reference in New Issue
Block a user