Skip to content

Commit

Permalink
Add XmlPath2 / RegEx matchers
Browse files Browse the repository at this point in the history
Solves issue #5
  • Loading branch information
StefH committed Jan 19, 2017
1 parent 1b2e536 commit 72335d4
Show file tree
Hide file tree
Showing 8 changed files with 214 additions and 44 deletions.
17 changes: 17 additions & 0 deletions src/WireMock/Matchers/IMatcher.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
namespace WireMock.Matchers
{
/// <summary>
/// IMatcher
/// </summary>
public interface IMatcher
{
/// <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>
bool IsMatch(string input);
}
}
38 changes: 38 additions & 0 deletions src/WireMock/Matchers/RegexMatcher.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
using System.Text.RegularExpressions;
using JetBrains.Annotations;
using WireMock.Validation;

namespace WireMock.Matchers
{
/// <summary>
/// Regular Expression Matcher
/// </summary>
/// <seealso cref="WireMock.Matchers.IMatcher" />
public class RegexMatcher : IMatcher
{
private readonly Regex _expression;

/// <summary>
/// Initializes a new instance of the <see cref="RegexMatcher"/> class.
/// </summary>
/// <param name="pattern">The pattern.</param>
public RegexMatcher([NotNull] string pattern)
{
Check.NotNull(pattern, nameof(pattern));

_expression = new Regex(pattern, RegexOptions.Compiled);
}

/// <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 input != null && _expression.IsMatch(input);
}
}
}
42 changes: 42 additions & 0 deletions src/WireMock/Matchers/XPathMatcher.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
using System.Xml;
using JetBrains.Annotations;
using WireMock.Validation;
using Wmhelp.XPath2;

namespace WireMock.Matchers
{
/// <summary>
/// XPath2Matcher
/// </summary>
/// <seealso cref="WireMock.Matchers.IMatcher" />
public class XPathMatcher : IMatcher
{
private readonly string _pattern;

/// <summary>
/// Initializes a new instance of the <see cref="XPathMatcher"/> class.
/// </summary>
/// <param name="pattern">The pattern.</param>
public XPathMatcher([NotNull] string pattern)
{
Check.NotNull(pattern, nameof(pattern));

_pattern = pattern;
}

/// <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)
{
var nav = new XmlDocument { InnerXml = input }.CreateNavigator();
object result = nav.XPath2Evaluate($"boolean({_pattern})");

return true.Equals(result);
}
}
}
61 changes: 30 additions & 31 deletions src/WireMock/RequestBodySpec.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,9 @@
using System.Diagnostics.CodeAnalysis;
using System.Text.RegularExpressions;
using JetBrains.Annotations;
using WireMock.Matchers;
using WireMock.Validation;

[module:
SuppressMessage("StyleCop.CSharp.ReadabilityRules",
"SA1101:PrefixLocalCallsWithThis",
Justification = "Reviewed. Suppression is OK here, as it conflicts with internal naming rules.")]
[module:
SuppressMessage("StyleCop.CSharp.NamingRules",
"SA1309:FieldNamesMustNotBeginWithUnderscore",
Justification = "Reviewed. Suppression is OK here, as it conflicts with internal naming rules.")]
[module:
SuppressMessage("StyleCop.CSharp.DocumentationRules",
"SA1633:FileMustHaveHeader",
Justification = "Reviewed. Suppression is OK here, as unknown copyright and company.")]
// ReSharper disable ArrangeThisQualifier
// ReSharper disable InconsistentNaming
namespace WireMock
{
/// <summary>
Expand All @@ -28,22 +15,22 @@ public class RequestBodySpec : ISpecifyRequests
/// <summary>
/// The bodyRegex.
/// </summary>
private readonly byte[] bodyData;
private readonly byte[] _bodyData;

/// <summary>
/// The bodyRegex.
/// The matcher.
/// </summary>
private readonly Regex bodyRegex;
private readonly IMatcher _matcher;

/// <summary>
/// The body function
/// </summary>
private readonly Func<string, bool> bodyFunc;
private readonly Func<string, bool> _bodyFunc;

/// <summary>
/// The body data function
/// </summary>
private readonly Func<byte[], bool> bodyDataFunc;
private readonly Func<byte[], bool> _bodyDataFunc;

/// <summary>
/// Initializes a new instance of the <see cref="RequestBodySpec"/> class.
Expand All @@ -54,7 +41,7 @@ public class RequestBodySpec : ISpecifyRequests
public RequestBodySpec([NotNull, RegexPattern] string body)
{
Check.NotNull(body, nameof(body));
bodyRegex = new Regex(body);
_matcher = new RegexMatcher(body);
}

/// <summary>
Expand All @@ -66,7 +53,7 @@ public RequestBodySpec([NotNull, RegexPattern] string body)
public RequestBodySpec([NotNull] byte[] body)
{
Check.NotNull(body, nameof(body));
bodyData = body;
_bodyData = body;
}

/// <summary>
Expand All @@ -78,7 +65,7 @@ public RequestBodySpec([NotNull] byte[] body)
public RequestBodySpec([NotNull] Func<string, bool> func)
{
Check.NotNull(func, nameof(func));
bodyFunc = func;
_bodyFunc = func;
}

/// <summary>
Expand All @@ -90,7 +77,19 @@ public RequestBodySpec([NotNull] Func<string, bool> func)
public RequestBodySpec([NotNull] Func<byte[], bool> func)
{
Check.NotNull(func, nameof(func));
bodyDataFunc = func;
_bodyDataFunc = func;
}

/// <summary>
/// Initializes a new instance of the <see cref="RequestBodySpec"/> class.
/// </summary>
/// <param name="matcher">
/// The body matcher.
/// </param>
public RequestBodySpec([NotNull] IMatcher matcher)
{
Check.NotNull(matcher, nameof(matcher));
_matcher = matcher;
}

/// <summary>
Expand All @@ -104,17 +103,17 @@ public RequestBodySpec([NotNull] Func<byte[], bool> func)
/// </returns>
public bool IsSatisfiedBy(RequestMessage requestMessage)
{
if (bodyRegex != null)
return bodyRegex.IsMatch(requestMessage.BodyAsString);
if (_matcher != null)
return _matcher.IsMatch(requestMessage.BodyAsString);

if (bodyData != null)
return requestMessage.Body == bodyData;
if (_bodyData != null)
return requestMessage.Body == _bodyData;

if (bodyFunc != null)
return bodyFunc(requestMessage.BodyAsString);
if (_bodyFunc != null)
return _bodyFunc(requestMessage.BodyAsString);

if (bodyDataFunc != null)
return bodyDataFunc(requestMessage.Body);
if (_bodyDataFunc != null)
return _bodyDataFunc(requestMessage.Body);

return false;
}
Expand Down
13 changes: 13 additions & 0 deletions src/WireMock/RequestBuilders/IBodyRequestBuilder.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using System;
using JetBrains.Annotations;
using WireMock.Matchers;

namespace WireMock.RequestBuilders
{
Expand All @@ -7,6 +9,17 @@ namespace WireMock.RequestBuilders
/// </summary>
public interface IBodyRequestBuilder
{
/// <summary>
/// The with body.
/// </summary>
/// <param name="matcher">
/// The matcher.
/// </param>
/// <returns>
/// The <see cref="ISpecifyRequests"/>.
/// </returns>
ISpecifyRequests WithBody([NotNull] IMatcher matcher);

/// <summary>
/// The with body.
/// </summary>
Expand Down
14 changes: 14 additions & 0 deletions src/WireMock/RequestBuilders/Request.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using JetBrains.Annotations;
using WireMock.Matchers;

[module:
SuppressMessage("StyleCop.CSharp.ReadabilityRules",
Expand Down Expand Up @@ -255,6 +256,19 @@ public ISpecifyRequests WithBody(Func<byte[], bool> func)
return this;
}

/// <summary>
/// The with body.
/// </summary>
/// <param name="matcher">The matcher.</param>
/// <returns>
/// The <see cref="ISpecifyRequests" />.
/// </returns>
public ISpecifyRequests WithBody(IMatcher matcher)
{
_requestSpecs.Add(new RequestBodySpec(matcher));
return this;
}

/// <summary>
/// The with parameters.
/// </summary>
Expand Down
3 changes: 2 additions & 1 deletion src/WireMock/project.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@
"JetBrains.Annotations": {
"version": "10.2.1",
"type": "build"
}
},
"XPath2": "1.0.3.1"
},

"frameworks": {
Expand Down
70 changes: 58 additions & 12 deletions test/WireMock.Net.Tests/RequestTests.cs
Original file line number Diff line number Diff line change
@@ -1,20 +1,11 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Text;
using NFluent;
using NUnit.Framework;
using WireMock.RequestBuilders;
using WireMock.Matchers;

[module:
SuppressMessage("StyleCop.CSharp.DocumentationRules",
"SA1600:ElementsMustBeDocumented",
Justification = "Reviewed. Suppression is OK here, as it's a tests class.")]
[module:
SuppressMessage("StyleCop.CSharp.DocumentationRules",
"SA1633:FileMustHaveHeader",
Justification = "Reviewed. Suppression is OK here, as unknown copyright and company.")]
// ReSharper disable InconsistentNaming
namespace WireMock.Net.Tests
{
[TestFixture]
Expand Down Expand Up @@ -249,7 +240,7 @@ public void Should_specify_requests_matching_given_header_prefix()
public void Should_specify_requests_matching_given_body()
{
// given
var spec = Request.WithUrl("/foo").UsingAnyVerb().WithBody(".*Hello world!.*");
var spec = Request.WithUrl("/foo").UsingAnyVerb().WithBody("Hello world!");

// when
string bodyAsString = "Hello world!";
Expand All @@ -261,7 +252,7 @@ public void Should_specify_requests_matching_given_body()
}

[Test]
public void Should_specify_requests_matching_given_body_as_wildcard()
public void Should_specify_requests_matching_given_body_as_regex()
{
// given
var spec = Request.WithUrl("/foo").UsingAnyVerb().WithBody("H.*o");
Expand All @@ -275,6 +266,61 @@ public void Should_specify_requests_matching_given_body_as_wildcard()
Check.That(spec.IsSatisfiedBy(request)).IsTrue();
}

[Test]
public void Should_specify_requests_matching_given_body_as_regexmatcher()
{
// given
var spec = Request.WithUrl("/foo").UsingAnyVerb().WithBody(new RegexMatcher("H.*o"));

// when
string bodyAsString = "Hello world!";
byte[] body = Encoding.UTF8.GetBytes(bodyAsString);
var request = new RequestMessage(new Uri("http://localhost/foo"), "PUT", body, bodyAsString);

// then
Check.That(spec.IsSatisfiedBy(request)).IsTrue();
}

[Test]
public void Should_specify_requests_matching_given_body_as_xpathmatcher_true()
{
// given
var spec = Request.WithUrl("/foo").UsingAnyVerb().WithBody(new XPathMatcher("/todo-list[count(todo-item) = 3]"));

// when
string xmlBodyAsString = @"
<todo-list>
<todo-item id='a1'>abc</todo-item>
<todo-item id='a2'>def</todo-item>
<todo-item id='a3'>xyz</todo-item>
</todo-list>";
byte[] body = Encoding.UTF8.GetBytes(xmlBodyAsString);
var request = new RequestMessage(new Uri("http://localhost/foo"), "PUT", body, xmlBodyAsString);

// then
Check.That(spec.IsSatisfiedBy(request)).IsTrue();
}

[Test]
public void Should_specify_requests_matching_given_body_as_xpathmatcher_false()
{
// given
var spec = Request.WithUrl("/foo").UsingAnyVerb().WithBody(new XPathMatcher("/todo-list[count(todo-item) = 99]"));

// when
string xmlBodyAsString = @"
<todo-list>
<todo-item id='a1'>abc</todo-item>
<todo-item id='a2'>def</todo-item>
<todo-item id='a3'>xyz</todo-item>
</todo-list>";
byte[] body = Encoding.UTF8.GetBytes(xmlBodyAsString);
var request = new RequestMessage(new Uri("http://localhost/foo"), "PUT", body, xmlBodyAsString);

// then
Check.That(spec.IsSatisfiedBy(request)).IsFalse();
}

[Test]
public void Should_exclude_requests_not_matching_given_body()
{
Expand Down

0 comments on commit 72335d4

Please sign in to comment.