This commit is contained in:
Stef Heyenrath
2026-01-11 11:53:55 +01:00
parent 960399127e
commit 95d3978c7c
39 changed files with 216 additions and 178 deletions

View File

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

View File

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

View File

@@ -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 />

View File

@@ -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)

View File

@@ -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 />

View File

@@ -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 />

View File

@@ -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 />

View File

@@ -24,12 +24,17 @@ 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 });
MatchDetails.Add(new MatchDetail
{
MatcherType = matcherType,
Score = score,
Exception = exception
});
return score;
}

View File

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

View File

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

View File

@@ -14,6 +14,8 @@ namespace WireMock.Matchers.Request;
/// </summary>
public class RequestMessageClientIPMatcher : IRequestMatcher
{
private const string _name = nameof(RequestMessageClientIPMatcher);
/// <summary>
/// The matchers
/// </summary>
@@ -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);
}
}

View File

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

View File

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

View File

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

View File

@@ -62,7 +62,7 @@ public class RequestMessageMultiPartMatcher : IRequestMatcher
var score = MatchScores.Mismatch;
Exception? exception = null;
if (Matchers?.Any() != true)
if (Matchers == null)
{
return requestMatchResult.AddScore(GetType(), score, null);
}

View File

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

View File

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

View File

@@ -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 />

View File

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