This commit is contained in:
Stef Heyenrath
2017-01-23 19:43:30 +01:00
parent 363d96e615
commit 45aa83ee91
11 changed files with 208 additions and 92 deletions

View File

@@ -57,6 +57,7 @@ WireMock supports matching of requests to stubs and verification queries using t
* HTTP Method * HTTP Method
* Query parameters * Query parameters
* Headers * Headers
* Cookies
* Request body * Request body

View File

@@ -1,4 +1,5 @@
using System; using System;
using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Net; using System.Net;
@@ -23,8 +24,11 @@ namespace WireMock
string bodyAsString = body != null ? listenerRequest.ContentEncoding.GetString(body) : null; string bodyAsString = body != null ? listenerRequest.ContentEncoding.GetString(body) : null;
var listenerHeaders = listenerRequest.Headers; var listenerHeaders = listenerRequest.Headers;
var headers = listenerHeaders.AllKeys.ToDictionary(k => k, k => listenerHeaders[k]); var headers = listenerHeaders.AllKeys.ToDictionary(k => k, k => listenerHeaders[k]);
var cookies = new Dictionary<string, string>();
foreach (Cookie cookie in listenerRequest.Cookies)
cookies.Add(cookie.Name, cookie.Value);
return new RequestMessage(url, verb, body, bodyAsString, headers); return new RequestMessage(url, verb, body, bodyAsString, headers, cookies);
} }
/// <summary> /// <summary>

View File

@@ -0,0 +1,65 @@
using System;
using System.Collections.Generic;
using JetBrains.Annotations;
using WireMock.Validation;
namespace WireMock.Matchers.Request
{
/// <summary>
/// The request cookie matcher.
/// </summary>
public class RequestMessageCookieMatcher : IRequestMatcher
{
private readonly string _name;
private readonly IMatcher _matcher;
private readonly Func<IDictionary<string, string>, bool> _cookieFunc;
/// <summary>
/// Initializes a new instance of the <see cref="RequestMessageCookieMatcher"/> class.
/// </summary>
/// <param name="name">The name.</param>
/// <param name="pattern">The pattern.</param>
/// <param name="ignoreCase">The ignoreCase.</param>
public RequestMessageCookieMatcher([NotNull] string name, [NotNull] string pattern, bool ignoreCase = true)
{
Check.NotNull(name, nameof(name));
Check.NotNull(pattern, nameof(pattern));
_name = name;
_matcher = new WildcardMatcher(pattern, ignoreCase);
}
/// <summary>
/// Initializes a new instance of the <see cref="RequestMessageCookieMatcher"/> class.
/// </summary>
/// <param name="func">
/// The func.
/// </param>
public RequestMessageCookieMatcher([NotNull] Func<IDictionary<string, string>, bool> func)
{
Check.NotNull(func, nameof(func));
_cookieFunc = func;
}
/// <summary>
/// Determines whether the specified RequestMessage is match.
/// </summary>
/// <param name="requestMessage">The RequestMessage.</param>
/// <returns>
/// <c>true</c> if the specified RequestMessage is match; otherwise, <c>false</c>.
/// </returns>
public bool IsMatch(RequestMessage requestMessage)
{
if (_cookieFunc != null)
return _cookieFunc(requestMessage.Cookies);
if (requestMessage.Cookies == null)
return false;
string headerValue = requestMessage.Cookies[_name];
return _matcher.IsMatch(headerValue);
}
}
}

View File

@@ -64,6 +64,9 @@ namespace WireMock.Matchers.Request
if (_headerFunc != null) if (_headerFunc != null)
return _headerFunc(requestMessage.Headers); return _headerFunc(requestMessage.Headers);
if (requestMessage.Headers == null)
return false;
string headerValue = requestMessage.Headers[_name]; string headerValue = requestMessage.Headers[_name];
return _matcher.IsMatch(headerValue); return _matcher.IsMatch(headerValue);
} }

View File

@@ -40,12 +40,12 @@ namespace WireMock.Matchers
/// <summary> /// <summary>
/// Copy/paste from http://www.codeproject.com/Tips/57304/Use-wildcard-characters-and-to-compare-strings /// Copy/paste from http://www.codeproject.com/Tips/57304/Use-wildcard-characters-and-to-compare-strings
/// </summary> /// </summary>
private bool MatchWildcardString(string pattern, string input) private bool MatchWildcardString([NotNull] string pattern, string input)
{ {
if (input != null && _ignoreCase) if (input != null && _ignoreCase)
input = input.ToLower(); input = input.ToLower();
if (pattern != null && _ignoreCase) if (_ignoreCase)
pattern = pattern.ToLower(); pattern = pattern.ToLower();
if (string.CompareOrdinal(pattern, input) == 0) if (string.CompareOrdinal(pattern, input) == 0)

View File

@@ -0,0 +1,45 @@
using System;
using System.Collections.Generic;
using JetBrains.Annotations;
using WireMock.Matchers.Request;
namespace WireMock.RequestBuilders
{
/// <summary>
/// The HeadersAndCookieRequestBuilder interface.
/// </summary>
public interface IHeadersAndCookiesRequestBuilder : IBodyRequestBuilder, IRequestMatcher, IParamsRequestBuilder
{
/// <summary>
/// The with header.
/// </summary>
/// <param name="name">The name.</param>
/// <param name="pattern">The pattern.</param>
/// <param name="ignoreCase">ignore Case</param>
/// <returns>The <see cref="IHeadersAndCookiesRequestBuilder"/>.</returns>
IHeadersAndCookiesRequestBuilder WithHeader(string name, string pattern, bool ignoreCase = true);
/// <summary>
/// The with header.
/// </summary>
/// <param name="func">The headers func.</param>
/// <returns>The <see cref="IHeadersAndCookiesRequestBuilder"/>.</returns>
IHeadersAndCookiesRequestBuilder WithHeader([NotNull] params Func<IDictionary<string, string>, bool>[] func);
/// <summary>
/// The with header.
/// </summary>
/// <param name="name">The name.</param>
/// <param name="pattern">The pattern.</param>
/// <param name="ignoreCase">ignore Case</param>
/// <returns>The <see cref="IHeadersAndCookiesRequestBuilder"/>.</returns>
IHeadersAndCookiesRequestBuilder WithCookie(string name, string pattern, bool ignoreCase = true);
/// <summary>
/// The with header.
/// </summary>
/// <param name="cookieFunc">The func.</param>
/// <returns>The <see cref="IHeadersAndCookiesRequestBuilder"/>.</returns>
IHeadersAndCookiesRequestBuilder WithCookie([NotNull] params Func<IDictionary<string, string>, bool>[] cookieFunc);
}
}

View File

@@ -1,39 +0,0 @@
using System;
using System.Collections.Generic;
using JetBrains.Annotations;
using WireMock.Matchers.Request;
namespace WireMock.RequestBuilders
{
/// <summary>
/// The HeadersRequestBuilder interface.
/// </summary>
public interface IHeadersRequestBuilder : IBodyRequestBuilder, IRequestMatcher, IParamsRequestBuilder
{
/// <summary>
/// The with header.
/// </summary>
/// <param name="name">
/// The name.
/// </param>
/// <param name="value">
/// The value.
/// </param>
/// <param name="ignoreCase">ignore Case</param>
/// <returns>
/// The <see cref="IHeadersRequestBuilder"/>.
/// </returns>
IHeadersRequestBuilder WithHeader(string name, string value, bool ignoreCase = true);
/// <summary>
/// The with header.
/// </summary>
/// <param name="func">
/// The headers func.
/// </param>
/// <returns>
/// The <see cref="IHeadersRequestBuilder"/>.
/// </returns>
IHeadersRequestBuilder WithHeader([NotNull] params Func<IDictionary<string, string>, bool>[] func);
}
}

View File

@@ -5,61 +5,61 @@ namespace WireMock.RequestBuilders
/// <summary> /// <summary>
/// The VerbRequestBuilder interface. /// The VerbRequestBuilder interface.
/// </summary> /// </summary>
public interface IVerbRequestBuilder : IHeadersRequestBuilder public interface IVerbRequestBuilder : IHeadersAndCookiesRequestBuilder
{ {
/// <summary> /// <summary>
/// The using get. /// The using get.
/// </summary> /// </summary>
/// <returns> /// <returns>
/// The <see cref="IHeadersRequestBuilder"/>. /// The <see cref="IHeadersAndCookiesRequestBuilder"/>.
/// </returns> /// </returns>
IHeadersRequestBuilder UsingGet(); IHeadersAndCookiesRequestBuilder UsingGet();
/// <summary> /// <summary>
/// The using post. /// The using post.
/// </summary> /// </summary>
/// <returns> /// <returns>
/// The <see cref="IHeadersRequestBuilder"/>. /// The <see cref="IHeadersAndCookiesRequestBuilder"/>.
/// </returns> /// </returns>
IHeadersRequestBuilder UsingPost(); IHeadersAndCookiesRequestBuilder UsingPost();
/// <summary> /// <summary>
/// The using delete. /// The using delete.
/// </summary> /// </summary>
/// <returns> /// <returns>
/// The <see cref="IHeadersRequestBuilder"/>. /// The <see cref="IHeadersAndCookiesRequestBuilder"/>.
/// </returns> /// </returns>
IHeadersRequestBuilder UsingDelete(); IHeadersAndCookiesRequestBuilder UsingDelete();
/// <summary> /// <summary>
/// The using put. /// The using put.
/// </summary> /// </summary>
/// <returns> /// <returns>
/// The <see cref="IHeadersRequestBuilder"/>. /// The <see cref="IHeadersAndCookiesRequestBuilder"/>.
/// </returns> /// </returns>
IHeadersRequestBuilder UsingPut(); IHeadersAndCookiesRequestBuilder UsingPut();
/// <summary> /// <summary>
/// The using head. /// The using head.
/// </summary> /// </summary>
/// <returns> /// <returns>
/// The <see cref="IHeadersRequestBuilder"/>. /// The <see cref="IHeadersAndCookiesRequestBuilder"/>.
/// </returns> /// </returns>
IHeadersRequestBuilder UsingHead(); IHeadersAndCookiesRequestBuilder UsingHead();
/// <summary> /// <summary>
/// The using any verb. /// The using any verb.
/// </summary> /// </summary>
/// <returns> /// <returns>
/// The <see cref="IHeadersRequestBuilder"/>. /// The <see cref="IHeadersAndCookiesRequestBuilder"/>.
/// </returns> /// </returns>
IHeadersRequestBuilder UsingAnyVerb(); IHeadersAndCookiesRequestBuilder UsingAnyVerb();
/// <summary> /// <summary>
/// The using verb. /// The using verb.
/// </summary> /// </summary>
/// <param name="verbs">The verb.</param> /// <param name="verbs">The verb.</param>
/// <returns>The <see cref="IHeadersRequestBuilder"/>.</returns> /// <returns>The <see cref="IHeadersAndCookiesRequestBuilder"/>.</returns>
IHeadersRequestBuilder UsingVerb([NotNull] params string[] verbs); IHeadersAndCookiesRequestBuilder UsingVerb([NotNull] params string[] verbs);
} }
} }

View File

@@ -112,9 +112,9 @@ namespace WireMock.RequestBuilders
/// The using get. /// The using get.
/// </summary> /// </summary>
/// <returns> /// <returns>
/// The <see cref="IHeadersRequestBuilder"/>. /// The <see cref="IHeadersAndCookiesRequestBuilder"/>.
/// </returns> /// </returns>
public IHeadersRequestBuilder UsingGet() public IHeadersAndCookiesRequestBuilder UsingGet()
{ {
_requestMatchers.Add(new RequestMessageVerbMatcher("get")); _requestMatchers.Add(new RequestMessageVerbMatcher("get"));
return this; return this;
@@ -124,9 +124,9 @@ namespace WireMock.RequestBuilders
/// The using post. /// The using post.
/// </summary> /// </summary>
/// <returns> /// <returns>
/// The <see cref="IHeadersRequestBuilder"/>. /// The <see cref="IHeadersAndCookiesRequestBuilder"/>.
/// </returns> /// </returns>
public IHeadersRequestBuilder UsingPost() public IHeadersAndCookiesRequestBuilder UsingPost()
{ {
_requestMatchers.Add(new RequestMessageVerbMatcher("post")); _requestMatchers.Add(new RequestMessageVerbMatcher("post"));
return this; return this;
@@ -136,9 +136,9 @@ namespace WireMock.RequestBuilders
/// The using put. /// The using put.
/// </summary> /// </summary>
/// <returns> /// <returns>
/// The <see cref="IHeadersRequestBuilder"/>. /// The <see cref="IHeadersAndCookiesRequestBuilder"/>.
/// </returns> /// </returns>
public IHeadersRequestBuilder UsingPut() public IHeadersAndCookiesRequestBuilder UsingPut()
{ {
_requestMatchers.Add(new RequestMessageVerbMatcher("put")); _requestMatchers.Add(new RequestMessageVerbMatcher("put"));
return this; return this;
@@ -148,9 +148,9 @@ namespace WireMock.RequestBuilders
/// The using delete. /// The using delete.
/// </summary> /// </summary>
/// <returns> /// <returns>
/// The <see cref="IHeadersRequestBuilder"/>. /// The <see cref="IHeadersAndCookiesRequestBuilder"/>.
/// </returns> /// </returns>
public IHeadersRequestBuilder UsingDelete() public IHeadersAndCookiesRequestBuilder UsingDelete()
{ {
_requestMatchers.Add(new RequestMessageVerbMatcher("delete")); _requestMatchers.Add(new RequestMessageVerbMatcher("delete"));
return this; return this;
@@ -160,9 +160,9 @@ namespace WireMock.RequestBuilders
/// The using head. /// The using head.
/// </summary> /// </summary>
/// <returns> /// <returns>
/// The <see cref="IHeadersRequestBuilder"/>. /// The <see cref="IHeadersAndCookiesRequestBuilder"/>.
/// </returns> /// </returns>
public IHeadersRequestBuilder UsingHead() public IHeadersAndCookiesRequestBuilder UsingHead()
{ {
_requestMatchers.Add(new RequestMessageVerbMatcher("head")); _requestMatchers.Add(new RequestMessageVerbMatcher("head"));
return this; return this;
@@ -172,9 +172,9 @@ namespace WireMock.RequestBuilders
/// The using any verb. /// The using any verb.
/// </summary> /// </summary>
/// <returns> /// <returns>
/// The <see cref="IHeadersRequestBuilder"/>. /// The <see cref="IHeadersAndCookiesRequestBuilder"/>.
/// </returns> /// </returns>
public IHeadersRequestBuilder UsingAnyVerb() public IHeadersAndCookiesRequestBuilder UsingAnyVerb()
{ {
var matchers = _requestMatchers.Where(m => m is RequestMessageVerbMatcher).ToList(); var matchers = _requestMatchers.Where(m => m is RequestMessageVerbMatcher).ToList();
foreach (var matcher in matchers) foreach (var matcher in matchers)
@@ -189,8 +189,8 @@ namespace WireMock.RequestBuilders
/// The using verb. /// The using verb.
/// </summary> /// </summary>
/// <param name="verbs">The verbs.</param> /// <param name="verbs">The verbs.</param>
/// <returns>The <see cref="IHeadersRequestBuilder"/>.</returns> /// <returns>The <see cref="IHeadersAndCookiesRequestBuilder"/>.</returns>
public IHeadersRequestBuilder UsingVerb(params string[] verbs) public IHeadersAndCookiesRequestBuilder UsingVerb(params string[] verbs)
{ {
var or = new RequestMessageCompositeMatcher(verbs.Select(verb => new RequestMessageVerbMatcher(verb)), CompositeMatcherType.Or); var or = new RequestMessageCompositeMatcher(verbs.Select(verb => new RequestMessageVerbMatcher(verb)), CompositeMatcherType.Or);
_requestMatchers.Add(or); _requestMatchers.Add(or);
@@ -305,37 +305,55 @@ namespace WireMock.RequestBuilders
} }
/// <summary> /// <summary>
/// The with header. /// With header.
/// </summary> /// </summary>
/// <param name="name"> /// <param name="name">The name.</param>
/// The name. /// <param name="pattern">The pattern.</param>
/// </param> /// <param name="ignoreCase">if set to <c>true</c> [ignore case].</param>
/// <param name="value"> /// <returns></returns>
/// The value. public IHeadersAndCookiesRequestBuilder WithHeader(string name, string pattern, bool ignoreCase = true)
/// </param>
/// <param name="ignoreCase">ignore Case</param>
/// <returns>
/// The <see cref="IHeadersRequestBuilder"/>.
/// </returns>
public IHeadersRequestBuilder WithHeader(string name, string value, bool ignoreCase = true)
{ {
_requestMatchers.Add(new RequestMessageHeaderMatcher(name, value, ignoreCase)); _requestMatchers.Add(new RequestMessageHeaderMatcher(name, pattern, ignoreCase));
return this; return this;
} }
/// <summary> /// <summary>
/// The with header. /// With header.
/// </summary> /// </summary>
/// <param name="funcs">The func.</param> /// <param name="funcs">The funcs.</param>
/// <returns> /// <returns></returns>
/// The <see cref="IHeadersRequestBuilder"/>. public IHeadersAndCookiesRequestBuilder WithHeader(params Func<IDictionary<string, string>, bool>[] funcs)
/// </returns>
public IHeadersRequestBuilder WithHeader(params Func<IDictionary<string, string>, bool>[] funcs)
{ {
var or = new RequestMessageCompositeMatcher(funcs.Select(func => new RequestMessageHeaderMatcher(func)), CompositeMatcherType.Or); var or = new RequestMessageCompositeMatcher(funcs.Select(func => new RequestMessageHeaderMatcher(func)), CompositeMatcherType.Or);
_requestMatchers.Add(or); _requestMatchers.Add(or);
return this; return this;
} }
/// <summary>
/// With cookie.
/// </summary>
/// <param name="name">The name.</param>
/// <param name="pattern">The pattern.</param>
/// <param name="ignoreCase">if set to <c>true</c> [ignore case].</param>
/// <returns></returns>
public IHeadersAndCookiesRequestBuilder WithCookie(string name, string pattern, bool ignoreCase = true)
{
_requestMatchers.Add(new RequestMessageCookieMatcher(name, pattern, ignoreCase));
return this;
}
/// <summary>
/// With header.
/// </summary>
/// <param name="funcs">The funcs.</param>
/// <returns></returns>
public IHeadersAndCookiesRequestBuilder WithCookie(params Func<IDictionary<string, string>, bool>[] funcs)
{
var or = new RequestMessageCompositeMatcher(funcs.Select(func => new RequestMessageCookieMatcher(func)), CompositeMatcherType.Or);
_requestMatchers.Add(or);
return this;
}
} }
} }

View File

@@ -2,7 +2,6 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using JetBrains.Annotations; using JetBrains.Annotations;
using WireMock.Extensions;
using WireMock.Util; using WireMock.Util;
using WireMock.Validation; using WireMock.Validation;
@@ -33,6 +32,11 @@ namespace WireMock
/// </summary> /// </summary>
public IDictionary<string, string> Headers { get; } public IDictionary<string, string> Headers { get; }
/// <summary>
/// Gets the cookies.
/// </summary>
public IDictionary<string, string> Cookies { get; }
/// <summary> /// <summary>
/// Gets the query. /// Gets the query.
/// </summary> /// </summary>
@@ -56,7 +60,8 @@ namespace WireMock
/// <param name="bodyAsBytes">The bodyAsBytes byte[].</param> /// <param name="bodyAsBytes">The bodyAsBytes byte[].</param>
/// <param name="body">The body string.</param> /// <param name="body">The body string.</param>
/// <param name="headers">The headers.</param> /// <param name="headers">The headers.</param>
public RequestMessage([NotNull] Uri url, [NotNull] string verb, [CanBeNull] byte[] bodyAsBytes, [CanBeNull] string body, [CanBeNull] IDictionary<string, string> headers = null) /// <param name="cookies">The cookies.</param>
public RequestMessage([NotNull] Uri url, [NotNull] string verb, [CanBeNull] byte[] bodyAsBytes, [CanBeNull] string body, [CanBeNull] IDictionary<string, string> headers = null, [CanBeNull] IDictionary<string, string> cookies = null)
{ {
Check.NotNull(url, nameof(url)); Check.NotNull(url, nameof(url));
Check.NotNull(verb, nameof(verb)); Check.NotNull(verb, nameof(verb));
@@ -67,6 +72,7 @@ namespace WireMock
BodyAsBytes = bodyAsBytes; BodyAsBytes = bodyAsBytes;
Body = body; Body = body;
Headers = headers; Headers = headers;
Cookies = cookies;
string query = url.Query; string query = url.Query;
if (!string.IsNullOrEmpty(query)) if (!string.IsNullOrEmpty(query))

View File

@@ -250,6 +250,19 @@ namespace WireMock.Net.Tests
Check.That(spec.IsMatch(request)).IsTrue(); Check.That(spec.IsMatch(request)).IsTrue();
} }
[Test]
public void Should_specify_requests_matching_given_cookies()
{
// given
var spec = Request.Create().WithUrl("/foo").UsingAnyVerb().WithCookie("session", "a*");
// when
var request = new RequestMessage(new Uri("http://localhost/foo"), "PUT", null, null, null, new Dictionary<string, string> { { "session", "abc" } });
// then
Check.That(spec.IsMatch(request)).IsTrue();
}
[Test] [Test]
public void Should_specify_requests_matching_given_body() public void Should_specify_requests_matching_given_body()
{ {