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
* Query parameters
* Headers
* Cookies
* Request body

View File

@@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
@@ -23,8 +24,11 @@ namespace WireMock
string bodyAsString = body != null ? listenerRequest.ContentEncoding.GetString(body) : null;
var listenerHeaders = listenerRequest.Headers;
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>

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)
return _headerFunc(requestMessage.Headers);
if (requestMessage.Headers == null)
return false;
string headerValue = requestMessage.Headers[_name];
return _matcher.IsMatch(headerValue);
}

View File

@@ -40,12 +40,12 @@ namespace WireMock.Matchers
/// <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)
private bool MatchWildcardString([NotNull] string pattern, string input)
{
if (input != null && _ignoreCase)
input = input.ToLower();
if (pattern != null && _ignoreCase)
if (_ignoreCase)
pattern = pattern.ToLower();
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>
/// The VerbRequestBuilder interface.
/// </summary>
public interface IVerbRequestBuilder : IHeadersRequestBuilder
public interface IVerbRequestBuilder : IHeadersAndCookiesRequestBuilder
{
/// <summary>
/// The using get.
/// </summary>
/// <returns>
/// The <see cref="IHeadersRequestBuilder"/>.
/// The <see cref="IHeadersAndCookiesRequestBuilder"/>.
/// </returns>
IHeadersRequestBuilder UsingGet();
IHeadersAndCookiesRequestBuilder UsingGet();
/// <summary>
/// The using post.
/// </summary>
/// <returns>
/// The <see cref="IHeadersRequestBuilder"/>.
/// The <see cref="IHeadersAndCookiesRequestBuilder"/>.
/// </returns>
IHeadersRequestBuilder UsingPost();
IHeadersAndCookiesRequestBuilder UsingPost();
/// <summary>
/// The using delete.
/// </summary>
/// <returns>
/// The <see cref="IHeadersRequestBuilder"/>.
/// The <see cref="IHeadersAndCookiesRequestBuilder"/>.
/// </returns>
IHeadersRequestBuilder UsingDelete();
IHeadersAndCookiesRequestBuilder UsingDelete();
/// <summary>
/// The using put.
/// </summary>
/// <returns>
/// The <see cref="IHeadersRequestBuilder"/>.
/// The <see cref="IHeadersAndCookiesRequestBuilder"/>.
/// </returns>
IHeadersRequestBuilder UsingPut();
IHeadersAndCookiesRequestBuilder UsingPut();
/// <summary>
/// The using head.
/// </summary>
/// <returns>
/// The <see cref="IHeadersRequestBuilder"/>.
/// The <see cref="IHeadersAndCookiesRequestBuilder"/>.
/// </returns>
IHeadersRequestBuilder UsingHead();
IHeadersAndCookiesRequestBuilder UsingHead();
/// <summary>
/// The using any verb.
/// </summary>
/// <returns>
/// The <see cref="IHeadersRequestBuilder"/>.
/// The <see cref="IHeadersAndCookiesRequestBuilder"/>.
/// </returns>
IHeadersRequestBuilder UsingAnyVerb();
IHeadersAndCookiesRequestBuilder UsingAnyVerb();
/// <summary>
/// The using verb.
/// </summary>
/// <param name="verbs">The verb.</param>
/// <returns>The <see cref="IHeadersRequestBuilder"/>.</returns>
IHeadersRequestBuilder UsingVerb([NotNull] params string[] verbs);
/// <returns>The <see cref="IHeadersAndCookiesRequestBuilder"/>.</returns>
IHeadersAndCookiesRequestBuilder UsingVerb([NotNull] params string[] verbs);
}
}

View File

@@ -112,9 +112,9 @@ namespace WireMock.RequestBuilders
/// The using get.
/// </summary>
/// <returns>
/// The <see cref="IHeadersRequestBuilder"/>.
/// The <see cref="IHeadersAndCookiesRequestBuilder"/>.
/// </returns>
public IHeadersRequestBuilder UsingGet()
public IHeadersAndCookiesRequestBuilder UsingGet()
{
_requestMatchers.Add(new RequestMessageVerbMatcher("get"));
return this;
@@ -124,9 +124,9 @@ namespace WireMock.RequestBuilders
/// The using post.
/// </summary>
/// <returns>
/// The <see cref="IHeadersRequestBuilder"/>.
/// The <see cref="IHeadersAndCookiesRequestBuilder"/>.
/// </returns>
public IHeadersRequestBuilder UsingPost()
public IHeadersAndCookiesRequestBuilder UsingPost()
{
_requestMatchers.Add(new RequestMessageVerbMatcher("post"));
return this;
@@ -136,9 +136,9 @@ namespace WireMock.RequestBuilders
/// The using put.
/// </summary>
/// <returns>
/// The <see cref="IHeadersRequestBuilder"/>.
/// The <see cref="IHeadersAndCookiesRequestBuilder"/>.
/// </returns>
public IHeadersRequestBuilder UsingPut()
public IHeadersAndCookiesRequestBuilder UsingPut()
{
_requestMatchers.Add(new RequestMessageVerbMatcher("put"));
return this;
@@ -148,9 +148,9 @@ namespace WireMock.RequestBuilders
/// The using delete.
/// </summary>
/// <returns>
/// The <see cref="IHeadersRequestBuilder"/>.
/// The <see cref="IHeadersAndCookiesRequestBuilder"/>.
/// </returns>
public IHeadersRequestBuilder UsingDelete()
public IHeadersAndCookiesRequestBuilder UsingDelete()
{
_requestMatchers.Add(new RequestMessageVerbMatcher("delete"));
return this;
@@ -160,9 +160,9 @@ namespace WireMock.RequestBuilders
/// The using head.
/// </summary>
/// <returns>
/// The <see cref="IHeadersRequestBuilder"/>.
/// The <see cref="IHeadersAndCookiesRequestBuilder"/>.
/// </returns>
public IHeadersRequestBuilder UsingHead()
public IHeadersAndCookiesRequestBuilder UsingHead()
{
_requestMatchers.Add(new RequestMessageVerbMatcher("head"));
return this;
@@ -172,9 +172,9 @@ namespace WireMock.RequestBuilders
/// The using any verb.
/// </summary>
/// <returns>
/// The <see cref="IHeadersRequestBuilder"/>.
/// The <see cref="IHeadersAndCookiesRequestBuilder"/>.
/// </returns>
public IHeadersRequestBuilder UsingAnyVerb()
public IHeadersAndCookiesRequestBuilder UsingAnyVerb()
{
var matchers = _requestMatchers.Where(m => m is RequestMessageVerbMatcher).ToList();
foreach (var matcher in matchers)
@@ -189,8 +189,8 @@ namespace WireMock.RequestBuilders
/// The using verb.
/// </summary>
/// <param name="verbs">The verbs.</param>
/// <returns>The <see cref="IHeadersRequestBuilder"/>.</returns>
public IHeadersRequestBuilder UsingVerb(params string[] verbs)
/// <returns>The <see cref="IHeadersAndCookiesRequestBuilder"/>.</returns>
public IHeadersAndCookiesRequestBuilder UsingVerb(params string[] verbs)
{
var or = new RequestMessageCompositeMatcher(verbs.Select(verb => new RequestMessageVerbMatcher(verb)), CompositeMatcherType.Or);
_requestMatchers.Add(or);
@@ -305,37 +305,55 @@ namespace WireMock.RequestBuilders
}
/// <summary>
/// The with header.
/// 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>
public IHeadersRequestBuilder WithHeader(string name, string value, bool ignoreCase = true)
/// <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 WithHeader(string name, string pattern, bool ignoreCase = true)
{
_requestMatchers.Add(new RequestMessageHeaderMatcher(name, value, ignoreCase));
_requestMatchers.Add(new RequestMessageHeaderMatcher(name, pattern, ignoreCase));
return this;
}
/// <summary>
/// The with header.
/// With header.
/// </summary>
/// <param name="funcs">The func.</param>
/// <returns>
/// The <see cref="IHeadersRequestBuilder"/>.
/// </returns>
public IHeadersRequestBuilder WithHeader(params Func<IDictionary<string, string>, bool>[] funcs)
/// <param name="funcs">The funcs.</param>
/// <returns></returns>
public IHeadersAndCookiesRequestBuilder WithHeader(params Func<IDictionary<string, string>, bool>[] funcs)
{
var or = new RequestMessageCompositeMatcher(funcs.Select(func => new RequestMessageHeaderMatcher(func)), CompositeMatcherType.Or);
_requestMatchers.Add(or);
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.Linq;
using JetBrains.Annotations;
using WireMock.Extensions;
using WireMock.Util;
using WireMock.Validation;
@@ -33,6 +32,11 @@ namespace WireMock
/// </summary>
public IDictionary<string, string> Headers { get; }
/// <summary>
/// Gets the cookies.
/// </summary>
public IDictionary<string, string> Cookies { get; }
/// <summary>
/// Gets the query.
/// </summary>
@@ -56,7 +60,8 @@ namespace WireMock
/// <param name="bodyAsBytes">The bodyAsBytes byte[].</param>
/// <param name="body">The body string.</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(verb, nameof(verb));
@@ -67,6 +72,7 @@ namespace WireMock
BodyAsBytes = bodyAsBytes;
Body = body;
Headers = headers;
Cookies = cookies;
string query = url.Query;
if (!string.IsNullOrEmpty(query))

View File

@@ -250,6 +250,19 @@ namespace WireMock.Net.Tests
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]
public void Should_specify_requests_matching_given_body()
{