WildcardMatcher and other fixes

This commit is contained in:
Stef Heyenrath
2017-01-20 23:06:59 +01:00
parent f4ce2dbeb3
commit 32f9171d01
16 changed files with 366 additions and 83 deletions

View File

@@ -17,7 +17,7 @@ namespace WireMock.Matchers
/// Initializes a new instance of the <see cref="RegexMatcher"/> class.
/// </summary>
/// <param name="pattern">The pattern.</param>
public RegexMatcher([NotNull] string pattern)
public RegexMatcher([NotNull, RegexPattern] string pattern)
{
Check.NotNull(pattern, nameof(pattern));

View File

@@ -0,0 +1,18 @@
namespace WireMock.Matchers.Request
{
/// <summary>
/// CompositeMatcherType
/// </summary>
public enum CompositeMatcherType
{
/// <summary>
/// And
/// </summary>
And = 0,
/// <summary>
/// Or
/// </summary>
Or = 1
}
}

View File

@@ -10,7 +10,12 @@ namespace WireMock.Matchers.Request
public class RequestMessageBodyMatcher : IRequestMatcher
{
/// <summary>
/// The bodyRegex.
/// The body.
/// </summary>
private readonly string _body;
/// <summary>
/// The body as byte[].
/// </summary>
private readonly byte[] _bodyData;
@@ -35,10 +40,10 @@ namespace WireMock.Matchers.Request
/// <param name="body">
/// The body Regex pattern.
/// </param>
public RequestMessageBodyMatcher([NotNull, RegexPattern] string body)
public RequestMessageBodyMatcher([NotNull] string body)
{
Check.NotNull(body, nameof(body));
_matcher = new RegexMatcher(body);
_body = body;
}
/// <summary>
@@ -101,6 +106,9 @@ namespace WireMock.Matchers.Request
if (_matcher != null)
return _matcher.IsMatch(requestMessage.Body);
if (_body != null)
return requestMessage.Body == _body;
if (_bodyData != null)
return requestMessage.BodyAsBytes == _bodyData;

View File

@@ -1,13 +1,16 @@
using System.Collections.Generic;
using System.Linq;
using JetBrains.Annotations;
namespace WireMock.Matchers.Request
{
/// <summary>
/// The composite request matcher.
/// </summary>
public abstract class RequestMessageCompositeMatcher : IRequestMatcher
public class RequestMessageCompositeMatcher : IRequestMatcher
{
private readonly CompositeMatcherType _type;
/// <summary>
/// Gets the request matchers.
/// </summary>
@@ -17,14 +20,13 @@ namespace WireMock.Matchers.Request
public IEnumerable<IRequestMatcher> RequestMatchers { get; }
/// <summary>
/// Initializes a new instance of the <see cref="RequestMessageCompositeMatcher"/> class.
/// The constructor.
/// Initializes a new instance of the <see cref="RequestMessageCompositeMatcher"/> class.
/// </summary>
/// <param name="requestMatchers">
/// The <see cref="IEnumerable&lt;IRequestMatcher&gt;"/> request matchers.
/// </param>
protected RequestMessageCompositeMatcher(IEnumerable<IRequestMatcher> requestMatchers)
/// <param name="requestMatchers">The request matchers.</param>
/// <param name="type">The CompositeMatcherType type (Defaults to 'And')</param>
public RequestMessageCompositeMatcher([NotNull] IEnumerable<IRequestMatcher> requestMatchers, CompositeMatcherType type = CompositeMatcherType.And)
{
_type = type;
RequestMatchers = requestMatchers;
}
@@ -37,7 +39,9 @@ namespace WireMock.Matchers.Request
/// </returns>
public bool IsMatch(RequestMessage requestMessage)
{
return RequestMatchers.All(spec => spec.IsMatch(requestMessage));
return _type == CompositeMatcherType.And ?
RequestMatchers.All(spec => spec.IsMatch(requestMessage)) :
RequestMatchers.Any(spec => spec.IsMatch(requestMessage));
}
}
}

View File

@@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using JetBrains.Annotations;
using WireMock.Validation;
@@ -17,9 +16,9 @@ namespace WireMock.Matchers.Request
private readonly string _name;
/// <summary>
/// The patternRegex.
/// The matcher.
/// </summary>
private readonly Regex _patternRegex;
private readonly IMatcher _matcher;
/// <summary>
/// The header function
@@ -29,17 +28,16 @@ namespace WireMock.Matchers.Request
/// <summary>
/// Initializes a new instance of the <see cref="RequestMessageHeaderMatcher"/> class.
/// </summary>
/// <param name="name">
/// The name.
/// </param>
/// <param name="pattern">
/// The pattern.
/// </param>
/// <param name="name">The name.</param>
/// <param name="pattern">The pattern.</param>
/// <param name="ignoreCase">The ignoreCase.</param>
public RequestMessageHeaderMatcher([NotNull] string name, [NotNull, RegexPattern] string pattern, bool ignoreCase = true)
public RequestMessageHeaderMatcher([NotNull] string name, [NotNull] string pattern, bool ignoreCase = true)
{
Check.NotNull(name, nameof(name));
Check.NotNull(pattern, nameof(pattern));
_name = name;
_patternRegex = ignoreCase ? new Regex(pattern, RegexOptions.IgnoreCase) : new Regex(pattern);
_matcher = new WildcardMatcher(pattern, ignoreCase);
}
/// <summary>
@@ -63,11 +61,11 @@ namespace WireMock.Matchers.Request
/// </returns>
public bool IsMatch(RequestMessage requestMessage)
{
if (_patternRegex == null)
if (_headerFunc != null)
return _headerFunc(requestMessage.Headers);
string headerValue = requestMessage.Headers[_name];
return _patternRegex.IsMatch(headerValue);
return _matcher.IsMatch(headerValue);
}
}
}

View File

@@ -1,5 +1,4 @@
using System;
using System.Text.RegularExpressions;
using JetBrains.Annotations;
using WireMock.Validation;
@@ -11,32 +10,45 @@ namespace WireMock.Matchers.Request
public class RequestMessagePathMatcher : IRequestMatcher
{
/// <summary>
/// The pathRegex.
/// The matcher.
/// </summary>
private readonly Regex _pathRegex;
private readonly string _path;
/// <summary>
/// The url function
/// The matcher.
/// </summary>
private readonly IMatcher _matcher;
/// <summary>
/// The path function
/// </summary>
private readonly Func<string, bool> _pathFunc;
/// <summary>
/// Initializes a new instance of the <see cref="RequestMessagePathMatcher"/> class.
/// </summary>
/// <param name="path">
/// The path Regex pattern.
/// </param>
public RequestMessagePathMatcher([NotNull, RegexPattern] string path)
/// <param name="path">The path.</param>
public RequestMessagePathMatcher([NotNull] string path)
{
Check.NotNull(path, nameof(path));
_pathRegex = new Regex(path);
_path = path;
}
/// <summary>
/// Initializes a new instance of the <see cref="RequestMessagePathMatcher"/> class.
/// </summary>
/// <param name="matcher">The matcher.</param>
public RequestMessagePathMatcher([NotNull] IMatcher matcher)
{
Check.NotNull(matcher, nameof(matcher));
_matcher = matcher;
}
/// <summary>
/// Initializes a new instance of the <see cref="RequestMessagePathMatcher"/> class.
/// </summary>
/// <param name="func">
/// The url func.
/// The path func.
/// </param>
public RequestMessagePathMatcher([NotNull] Func<string, bool> func)
{
@@ -53,7 +65,16 @@ namespace WireMock.Matchers.Request
/// </returns>
public bool IsMatch(RequestMessage requestMessage)
{
return _pathRegex?.IsMatch(requestMessage.Path) ?? _pathFunc(requestMessage.Path);
if (_path != null)
return string.CompareOrdinal(_path, requestMessage.Path) == 0;
if (_matcher != null)
return _matcher.IsMatch(requestMessage.Path);
if (_pathFunc != null)
return _pathFunc(requestMessage.Path);
return false;
}
}
}

View File

@@ -1,5 +1,4 @@
using System;
using System.Text.RegularExpressions;
using JetBrains.Annotations;
using WireMock.Validation;
@@ -11,9 +10,9 @@ namespace WireMock.Matchers.Request
public class RequestMessageUrlMatcher : IRequestMatcher
{
/// <summary>
/// The urlRegex.
/// The matcher.
/// </summary>
private readonly Regex _urlRegex;
private readonly IMatcher _matcher;
/// <summary>
/// The url function
@@ -23,13 +22,20 @@ namespace WireMock.Matchers.Request
/// <summary>
/// Initializes a new instance of the <see cref="RequestMessageUrlMatcher"/> class.
/// </summary>
/// <param name="url">
/// The url Regex pattern.
/// </param>
public RequestMessageUrlMatcher([NotNull, RegexPattern] string url)
/// <param name="url">The url.</param>
public RequestMessageUrlMatcher([NotNull] string url) : this(new WildcardMatcher(url))
{
Check.NotNull(url, nameof(url));
_urlRegex = new Regex(url);
_matcher = new WildcardMatcher(url);
}
/// <summary>
/// Initializes a new instance of the <see cref="RequestMessageUrlMatcher"/> class.
/// </summary>
/// <param name="matcher">The matcher.</param>
public RequestMessageUrlMatcher([NotNull] IMatcher matcher)
{
Check.NotNull(matcher, nameof(matcher));
_matcher = matcher;
}
/// <summary>
@@ -38,7 +44,7 @@ namespace WireMock.Matchers.Request
/// <param name="func">
/// The url func.
/// </param>
public RequestMessageUrlMatcher(Func<string, bool> func)
public RequestMessageUrlMatcher([NotNull] Func<string, bool> func)
{
Check.NotNull(func, nameof(func));
_urlFunc = func;
@@ -53,7 +59,13 @@ namespace WireMock.Matchers.Request
/// </returns>
public bool IsMatch(RequestMessage requestMessage)
{
return _urlRegex?.IsMatch(requestMessage.Url) ?? _urlFunc(requestMessage.Url);
if (_matcher != null)
return _matcher.IsMatch(requestMessage.Path);
if (_urlFunc != null)
return _urlFunc(requestMessage.Url);
return false;
}
}
}

View File

@@ -0,0 +1,102 @@
using JetBrains.Annotations;
using WireMock.Validation;
namespace WireMock.Matchers
{
/// <summary>
/// WildcardMatcher
/// </summary>
/// <seealso cref="WireMock.Matchers.IMatcher" />
public class WildcardMatcher : IMatcher
{
private readonly string _pattern;
private readonly bool _ignoreCase;
/// <summary>
/// Initializes a new instance of the <see cref="RegexMatcher"/> class.
/// </summary>
/// <param name="pattern">The pattern.</param>
/// <param name="ignoreCase">IgnoreCase</param>
public WildcardMatcher([NotNull] string pattern, bool ignoreCase = false)
{
Check.NotNull(pattern, nameof(pattern));
_pattern = pattern;
_ignoreCase = ignoreCase;
}
/// <summary>
/// Determines whether the specified input is match.
/// </summary>
/// <param name="input">The input.</param>
/// <returns>
/// <c>true</c> if the specified input is match; otherwise, <c>false</c>.
/// </returns>
public bool IsMatch(string input)
{
return MatchWildcardString(_pattern, input);
}
/// <summary>
/// Copy/paste from http://www.codeproject.com/Tips/57304/Use-wildcard-characters-and-to-compare-strings
/// </summary>
private bool MatchWildcardString(string pattern, string input)
{
if (input != null && _ignoreCase)
input = input.ToLower();
if (pattern != null && _ignoreCase)
pattern = pattern.ToLower();
if (string.CompareOrdinal(pattern, input) == 0)
{
return true;
}
if (string.IsNullOrEmpty(input))
{
return string.IsNullOrEmpty(pattern.Trim('*'));
}
if (pattern.Length == 0)
{
return false;
}
if (pattern[0] == '?')
{
return MatchWildcardString(pattern.Substring(1), input.Substring(1));
}
if (pattern[pattern.Length - 1] == '?')
{
return MatchWildcardString(pattern.Substring(0, pattern.Length - 1), input.Substring(0, input.Length - 1));
}
if (pattern[0] == '*')
{
if (MatchWildcardString(pattern.Substring(1), input))
{
return true;
}
return MatchWildcardString(pattern, input.Substring(1));
}
if (pattern[pattern.Length - 1] == '*')
{
if (MatchWildcardString(pattern.Substring(0, pattern.Length - 1), input))
{
return true;
}
return MatchWildcardString(pattern, input.Substring(0, input.Length - 1));
}
if (pattern[0] == input[0])
{
return MatchWildcardString(pattern.Substring(1), input.Substring(1));
}
return false;
}
}
}

View File

@@ -34,6 +34,6 @@ namespace WireMock.RequestBuilders
/// <returns>
/// The <see cref="IHeadersRequestBuilder"/>.
/// </returns>
IHeadersRequestBuilder WithHeader([NotNull] Func<IDictionary<string, string>, bool> func);
IHeadersRequestBuilder WithHeader([NotNull] params Func<IDictionary<string, string>, bool>[] func);
}
}

View File

@@ -1,5 +1,6 @@
using System;
using JetBrains.Annotations;
using WireMock.Matchers;
namespace WireMock.RequestBuilders
{
@@ -8,32 +9,46 @@ namespace WireMock.RequestBuilders
/// </summary>
public interface IUrlAndPathRequestBuilder : IVerbRequestBuilder
{
/// <summary>
/// The with url.
/// </summary>
/// <param name="matcher">The matcher.</param>
/// <returns>The <see cref="IUrlAndPathRequestBuilder"/>.</returns>
IUrlAndPathRequestBuilder WithUrl([NotNull] IMatcher matcher);
/// <summary>
/// The with url.
/// </summary>
/// <param name="url">The url.</param>
/// <returns>The <see cref="IUrlAndPathRequestBuilder"/>.</returns>
IUrlAndPathRequestBuilder WithUrl([NotNull] string url);
IUrlAndPathRequestBuilder WithUrl([NotNull] params string[] url);
/// <summary>
/// The with url.
/// </summary>
/// <param name="func">The url func.</param>
/// <returns>The <see cref="IUrlAndPathRequestBuilder"/>.</returns>
IUrlAndPathRequestBuilder WithUrl([NotNull] Func<string, bool> func);
IUrlAndPathRequestBuilder WithUrl([NotNull] params Func<string, bool>[] func);
/// <summary>
/// The with path.
/// </summary>
/// <param name="matcher">The matcher.</param>
/// <returns>The <see cref="IUrlAndPathRequestBuilder"/>.</returns>
IUrlAndPathRequestBuilder WithPath([NotNull] IMatcher matcher);
/// <summary>
/// The with path.
/// </summary>
/// <param name="path">The path.</param>
/// <returns>The <see cref="IUrlAndPathRequestBuilder"/>.</returns>
IUrlAndPathRequestBuilder WithPath([NotNull] string path);
IUrlAndPathRequestBuilder WithPath([NotNull] params string[] path);
/// <summary>
/// The with path.
/// </summary>
/// <param name="func">The path func.</param>
/// <returns>The <see cref="IUrlAndPathRequestBuilder"/>.</returns>
IUrlAndPathRequestBuilder WithPath([NotNull] Func<string, bool> func);
IUrlAndPathRequestBuilder WithPath([NotNull] params Func<string, bool>[] func);
}
}

View File

@@ -34,44 +34,76 @@ namespace WireMock.RequestBuilders
/// <summary>
/// The with url.
/// </summary>
/// <param name="url">The url.</param>
/// <param name="matcher">The matcher.</param>
/// <returns>The <see cref="IUrlAndPathRequestBuilder"/>.</returns>
public IUrlAndPathRequestBuilder WithUrl(string url)
public IUrlAndPathRequestBuilder WithUrl(IMatcher matcher)
{
_requestMatchers.Add(new RequestMessageUrlMatcher(url));
_requestMatchers.Add(new RequestMessageUrlMatcher(matcher));
return this;
}
/// <summary>
/// The with url.
/// </summary>
/// <param name="func">The url func.</param>
/// <param name="urls">The urls.</param>
/// <returns>The <see cref="IUrlAndPathRequestBuilder"/>.</returns>
public IUrlAndPathRequestBuilder WithUrl(Func<string, bool> func)
public IUrlAndPathRequestBuilder WithUrl(params string[] urls)
{
_requestMatchers.Add(new RequestMessageUrlMatcher(func));
var or = new RequestMessageCompositeMatcher(urls.Select(url => new RequestMessageUrlMatcher(url)), CompositeMatcherType.Or);
_requestMatchers.Add(or);
return this;
}
/// <summary>
/// The with url.
/// </summary>
/// <param name="funcs">The url func.</param>
/// <returns>The <see cref="IUrlAndPathRequestBuilder"/>.</returns>
public IUrlAndPathRequestBuilder WithUrl(params Func<string, bool>[] funcs)
{
var or = new RequestMessageCompositeMatcher(funcs.Select(func => new RequestMessageUrlMatcher(func)), CompositeMatcherType.Or);
_requestMatchers.Add(or);
return this;
}
/// <summary>
/// The with url.
/// </summary>
/// <param name="matcher">The matcher.</param>
/// <returns>The <see cref="IUrlAndPathRequestBuilder"/>.</returns>
public IUrlAndPathRequestBuilder WithPath(IMatcher matcher)
{
_requestMatchers.Add(new RequestMessagePathMatcher(matcher));
return this;
}
/// <summary>
/// The with path.
/// </summary>
/// <param name="path">The path.</param>
/// <param name="paths">The path.</param>
/// <returns>The <see cref="IUrlAndPathRequestBuilder"/>.</returns>
public IUrlAndPathRequestBuilder WithPath(string path)
public IUrlAndPathRequestBuilder WithPath(params string[] paths)
{
_requestMatchers.Add(new RequestMessagePathMatcher(path));
var or = new RequestMessageCompositeMatcher(paths.Select(path => new RequestMessageUrlMatcher(path)), CompositeMatcherType.Or);
_requestMatchers.Add(or);
return this;
}
/// <summary>
/// The with path.
/// </summary>
/// <param name="func">The path func.</param>
/// <param name="funcs">The path func.</param>
/// <returns>The <see cref="IUrlAndPathRequestBuilder"/>.</returns>
public IUrlAndPathRequestBuilder WithPath(Func<string, bool> func)
public IUrlAndPathRequestBuilder WithPath(params Func<string, bool>[] funcs)
{
_requestMatchers.Add(new RequestMessagePathMatcher(func));
var or = new RequestMessageCompositeMatcher(funcs.Select(func => new RequestMessageUrlMatcher(func)), CompositeMatcherType.Or);
_requestMatchers.Add(or);
return this;
}
@@ -295,15 +327,15 @@ namespace WireMock.RequestBuilders
/// <summary>
/// The with header.
/// </summary>
/// <param name="func">
/// The func.
/// </param>
/// <param name="funcs">The func.</param>
/// <returns>
/// The <see cref="IHeadersRequestBuilder"/>.
/// </returns>
public IHeadersRequestBuilder WithHeader(Func<IDictionary<string, string>, bool> func)
public IHeadersRequestBuilder WithHeader(params Func<IDictionary<string, string>, bool>[] funcs)
{
_requestMatchers.Add(new RequestMessageHeaderMatcher(func));
var or = new RequestMessageCompositeMatcher(funcs.Select(func => new RequestMessageHeaderMatcher(func)), CompositeMatcherType.Or);
_requestMatchers.Add(or);
return this;
}
}