From 45aa83ee9109977b1100a7fa43170be5f68210f2 Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Mon, 23 Jan 2017 19:43:30 +0100 Subject: [PATCH] Cookie #9 --- README.md | 1 + src/WireMock/HttpListenerRequestMapper.cs | 6 +- .../Request/RequestMessageCookieMatcher.cs | 65 ++++++++++++++ .../Request/RequestMessageHeaderMatcher.cs | 3 + src/WireMock/Matchers/WildcardMatcher.cs | 4 +- .../IHeadersAndCookiesRequestBuilder.cs | 45 ++++++++++ .../RequestBuilders/IHeadersRequestBuilder.cs | 39 --------- .../RequestBuilders/IVerbRequestBuilder.cs | 30 +++---- src/WireMock/RequestBuilders/Request.cs | 84 +++++++++++-------- src/WireMock/RequestMessage.cs | 10 ++- test/WireMock.Net.Tests/RequestTests.cs | 13 +++ 11 files changed, 208 insertions(+), 92 deletions(-) create mode 100644 src/WireMock/Matchers/Request/RequestMessageCookieMatcher.cs create mode 100644 src/WireMock/RequestBuilders/IHeadersAndCookiesRequestBuilder.cs delete mode 100644 src/WireMock/RequestBuilders/IHeadersRequestBuilder.cs diff --git a/README.md b/README.md index 3f1b98b77..2a33ba839 100644 --- a/README.md +++ b/README.md @@ -57,6 +57,7 @@ WireMock supports matching of requests to stubs and verification queries using t * HTTP Method * Query parameters * Headers +* Cookies * Request body diff --git a/src/WireMock/HttpListenerRequestMapper.cs b/src/WireMock/HttpListenerRequestMapper.cs index 83d714534..9146f99c2 100644 --- a/src/WireMock/HttpListenerRequestMapper.cs +++ b/src/WireMock/HttpListenerRequestMapper.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.IO; using System.Linq; using System.Net; @@ -23,8 +24,11 @@ public RequestMessage Map(HttpListenerRequest listenerRequest) 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(); + 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); } /// diff --git a/src/WireMock/Matchers/Request/RequestMessageCookieMatcher.cs b/src/WireMock/Matchers/Request/RequestMessageCookieMatcher.cs new file mode 100644 index 000000000..690b5d3be --- /dev/null +++ b/src/WireMock/Matchers/Request/RequestMessageCookieMatcher.cs @@ -0,0 +1,65 @@ +using System; +using System.Collections.Generic; +using JetBrains.Annotations; +using WireMock.Validation; + +namespace WireMock.Matchers.Request +{ + /// + /// The request cookie matcher. + /// + public class RequestMessageCookieMatcher : IRequestMatcher + { + private readonly string _name; + + private readonly IMatcher _matcher; + + private readonly Func, bool> _cookieFunc; + + /// + /// Initializes a new instance of the class. + /// + /// The name. + /// The pattern. + /// The ignoreCase. + 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); + } + + /// + /// Initializes a new instance of the class. + /// + /// + /// The func. + /// + public RequestMessageCookieMatcher([NotNull] Func, bool> func) + { + Check.NotNull(func, nameof(func)); + _cookieFunc = func; + } + + /// + /// Determines whether the specified RequestMessage is match. + /// + /// The RequestMessage. + /// + /// true if the specified RequestMessage is match; otherwise, false. + /// + 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); + } + } +} \ No newline at end of file diff --git a/src/WireMock/Matchers/Request/RequestMessageHeaderMatcher.cs b/src/WireMock/Matchers/Request/RequestMessageHeaderMatcher.cs index 35cdbbc7f..13fbf77f0 100644 --- a/src/WireMock/Matchers/Request/RequestMessageHeaderMatcher.cs +++ b/src/WireMock/Matchers/Request/RequestMessageHeaderMatcher.cs @@ -64,6 +64,9 @@ public bool IsMatch(RequestMessage requestMessage) if (_headerFunc != null) return _headerFunc(requestMessage.Headers); + if (requestMessage.Headers == null) + return false; + string headerValue = requestMessage.Headers[_name]; return _matcher.IsMatch(headerValue); } diff --git a/src/WireMock/Matchers/WildcardMatcher.cs b/src/WireMock/Matchers/WildcardMatcher.cs index 8e005dd18..2db63b755 100644 --- a/src/WireMock/Matchers/WildcardMatcher.cs +++ b/src/WireMock/Matchers/WildcardMatcher.cs @@ -40,12 +40,12 @@ public bool IsMatch(string input) /// /// Copy/paste from http://www.codeproject.com/Tips/57304/Use-wildcard-characters-and-to-compare-strings /// - 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) diff --git a/src/WireMock/RequestBuilders/IHeadersAndCookiesRequestBuilder.cs b/src/WireMock/RequestBuilders/IHeadersAndCookiesRequestBuilder.cs new file mode 100644 index 000000000..bfae8478a --- /dev/null +++ b/src/WireMock/RequestBuilders/IHeadersAndCookiesRequestBuilder.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections.Generic; +using JetBrains.Annotations; +using WireMock.Matchers.Request; + +namespace WireMock.RequestBuilders +{ + /// + /// The HeadersAndCookieRequestBuilder interface. + /// + public interface IHeadersAndCookiesRequestBuilder : IBodyRequestBuilder, IRequestMatcher, IParamsRequestBuilder + { + /// + /// The with header. + /// + /// The name. + /// The pattern. + /// ignore Case + /// The . + IHeadersAndCookiesRequestBuilder WithHeader(string name, string pattern, bool ignoreCase = true); + + /// + /// The with header. + /// + /// The headers func. + /// The . + IHeadersAndCookiesRequestBuilder WithHeader([NotNull] params Func, bool>[] func); + + /// + /// The with header. + /// + /// The name. + /// The pattern. + /// ignore Case + /// The . + IHeadersAndCookiesRequestBuilder WithCookie(string name, string pattern, bool ignoreCase = true); + + /// + /// The with header. + /// + /// The func. + /// The . + IHeadersAndCookiesRequestBuilder WithCookie([NotNull] params Func, bool>[] cookieFunc); + } +} \ No newline at end of file diff --git a/src/WireMock/RequestBuilders/IHeadersRequestBuilder.cs b/src/WireMock/RequestBuilders/IHeadersRequestBuilder.cs deleted file mode 100644 index 1d3bebc6e..000000000 --- a/src/WireMock/RequestBuilders/IHeadersRequestBuilder.cs +++ /dev/null @@ -1,39 +0,0 @@ -using System; -using System.Collections.Generic; -using JetBrains.Annotations; -using WireMock.Matchers.Request; - -namespace WireMock.RequestBuilders -{ - /// - /// The HeadersRequestBuilder interface. - /// - public interface IHeadersRequestBuilder : IBodyRequestBuilder, IRequestMatcher, IParamsRequestBuilder - { - /// - /// The with header. - /// - /// - /// The name. - /// - /// - /// The value. - /// - /// ignore Case - /// - /// The . - /// - IHeadersRequestBuilder WithHeader(string name, string value, bool ignoreCase = true); - - /// - /// The with header. - /// - /// - /// The headers func. - /// - /// - /// The . - /// - IHeadersRequestBuilder WithHeader([NotNull] params Func, bool>[] func); - } -} \ No newline at end of file diff --git a/src/WireMock/RequestBuilders/IVerbRequestBuilder.cs b/src/WireMock/RequestBuilders/IVerbRequestBuilder.cs index e42798a86..e89d3cb6c 100644 --- a/src/WireMock/RequestBuilders/IVerbRequestBuilder.cs +++ b/src/WireMock/RequestBuilders/IVerbRequestBuilder.cs @@ -5,61 +5,61 @@ namespace WireMock.RequestBuilders /// /// The VerbRequestBuilder interface. /// - public interface IVerbRequestBuilder : IHeadersRequestBuilder + public interface IVerbRequestBuilder : IHeadersAndCookiesRequestBuilder { /// /// The using get. /// /// - /// The . + /// The . /// - IHeadersRequestBuilder UsingGet(); + IHeadersAndCookiesRequestBuilder UsingGet(); /// /// The using post. /// /// - /// The . + /// The . /// - IHeadersRequestBuilder UsingPost(); + IHeadersAndCookiesRequestBuilder UsingPost(); /// /// The using delete. /// /// - /// The . + /// The . /// - IHeadersRequestBuilder UsingDelete(); + IHeadersAndCookiesRequestBuilder UsingDelete(); /// /// The using put. /// /// - /// The . + /// The . /// - IHeadersRequestBuilder UsingPut(); + IHeadersAndCookiesRequestBuilder UsingPut(); /// /// The using head. /// /// - /// The . + /// The . /// - IHeadersRequestBuilder UsingHead(); + IHeadersAndCookiesRequestBuilder UsingHead(); /// /// The using any verb. /// /// - /// The . + /// The . /// - IHeadersRequestBuilder UsingAnyVerb(); + IHeadersAndCookiesRequestBuilder UsingAnyVerb(); /// /// The using verb. /// /// The verb. - /// The . - IHeadersRequestBuilder UsingVerb([NotNull] params string[] verbs); + /// The . + IHeadersAndCookiesRequestBuilder UsingVerb([NotNull] params string[] verbs); } } \ No newline at end of file diff --git a/src/WireMock/RequestBuilders/Request.cs b/src/WireMock/RequestBuilders/Request.cs index cc47ad94a..ea9f97c92 100644 --- a/src/WireMock/RequestBuilders/Request.cs +++ b/src/WireMock/RequestBuilders/Request.cs @@ -112,9 +112,9 @@ public IUrlAndPathRequestBuilder WithPath(params Func[] funcs) /// The using get. /// /// - /// The . + /// The . /// - public IHeadersRequestBuilder UsingGet() + public IHeadersAndCookiesRequestBuilder UsingGet() { _requestMatchers.Add(new RequestMessageVerbMatcher("get")); return this; @@ -124,9 +124,9 @@ public IHeadersRequestBuilder UsingGet() /// The using post. /// /// - /// The . + /// The . /// - public IHeadersRequestBuilder UsingPost() + public IHeadersAndCookiesRequestBuilder UsingPost() { _requestMatchers.Add(new RequestMessageVerbMatcher("post")); return this; @@ -136,9 +136,9 @@ public IHeadersRequestBuilder UsingPost() /// The using put. /// /// - /// The . + /// The . /// - public IHeadersRequestBuilder UsingPut() + public IHeadersAndCookiesRequestBuilder UsingPut() { _requestMatchers.Add(new RequestMessageVerbMatcher("put")); return this; @@ -148,9 +148,9 @@ public IHeadersRequestBuilder UsingPut() /// The using delete. /// /// - /// The . + /// The . /// - public IHeadersRequestBuilder UsingDelete() + public IHeadersAndCookiesRequestBuilder UsingDelete() { _requestMatchers.Add(new RequestMessageVerbMatcher("delete")); return this; @@ -160,9 +160,9 @@ public IHeadersRequestBuilder UsingDelete() /// The using head. /// /// - /// The . + /// The . /// - public IHeadersRequestBuilder UsingHead() + public IHeadersAndCookiesRequestBuilder UsingHead() { _requestMatchers.Add(new RequestMessageVerbMatcher("head")); return this; @@ -172,9 +172,9 @@ public IHeadersRequestBuilder UsingHead() /// The using any verb. /// /// - /// The . + /// The . /// - public IHeadersRequestBuilder UsingAnyVerb() + public IHeadersAndCookiesRequestBuilder UsingAnyVerb() { var matchers = _requestMatchers.Where(m => m is RequestMessageVerbMatcher).ToList(); foreach (var matcher in matchers) @@ -189,8 +189,8 @@ public IHeadersRequestBuilder UsingAnyVerb() /// The using verb. /// /// The verbs. - /// The . - public IHeadersRequestBuilder UsingVerb(params string[] verbs) + /// The . + 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 @@ public IRequestMatcher WithParam(Func>, } /// - /// The with header. + /// With header. /// - /// - /// The name. - /// - /// - /// The value. - /// - /// ignore Case - /// - /// The . - /// - public IHeadersRequestBuilder WithHeader(string name, string value, bool ignoreCase = true) + /// The name. + /// The pattern. + /// if set to true [ignore case]. + /// + 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; } /// - /// The with header. + /// With header. /// - /// The func. - /// - /// The . - /// - public IHeadersRequestBuilder WithHeader(params Func, bool>[] funcs) + /// The funcs. + /// + public IHeadersAndCookiesRequestBuilder WithHeader(params Func, bool>[] funcs) { var or = new RequestMessageCompositeMatcher(funcs.Select(func => new RequestMessageHeaderMatcher(func)), CompositeMatcherType.Or); _requestMatchers.Add(or); return this; } + + /// + /// With cookie. + /// + /// The name. + /// The pattern. + /// if set to true [ignore case]. + /// + public IHeadersAndCookiesRequestBuilder WithCookie(string name, string pattern, bool ignoreCase = true) + { + _requestMatchers.Add(new RequestMessageCookieMatcher(name, pattern, ignoreCase)); + return this; + } + + /// + /// With header. + /// + /// The funcs. + /// + public IHeadersAndCookiesRequestBuilder WithCookie(params Func, bool>[] funcs) + { + var or = new RequestMessageCompositeMatcher(funcs.Select(func => new RequestMessageCookieMatcher(func)), CompositeMatcherType.Or); + _requestMatchers.Add(or); + + return this; + } } } \ No newline at end of file diff --git a/src/WireMock/RequestMessage.cs b/src/WireMock/RequestMessage.cs index 9625ce5b9..39429eec2 100644 --- a/src/WireMock/RequestMessage.cs +++ b/src/WireMock/RequestMessage.cs @@ -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 @@ public class RequestMessage /// public IDictionary Headers { get; } + /// + /// Gets the cookies. + /// + public IDictionary Cookies { get; } + /// /// Gets the query. /// @@ -56,7 +60,8 @@ public class RequestMessage /// The bodyAsBytes byte[]. /// The body string. /// The headers. - public RequestMessage([NotNull] Uri url, [NotNull] string verb, [CanBeNull] byte[] bodyAsBytes, [CanBeNull] string body, [CanBeNull] IDictionary headers = null) + /// The cookies. + public RequestMessage([NotNull] Uri url, [NotNull] string verb, [CanBeNull] byte[] bodyAsBytes, [CanBeNull] string body, [CanBeNull] IDictionary headers = null, [CanBeNull] IDictionary cookies = null) { Check.NotNull(url, nameof(url)); Check.NotNull(verb, nameof(verb)); @@ -67,6 +72,7 @@ public RequestMessage([NotNull] Uri url, [NotNull] string verb, [CanBeNull] byte BodyAsBytes = bodyAsBytes; Body = body; Headers = headers; + Cookies = cookies; string query = url.Query; if (!string.IsNullOrEmpty(query)) diff --git a/test/WireMock.Net.Tests/RequestTests.cs b/test/WireMock.Net.Tests/RequestTests.cs index 6b4ac7f28..79cc0d0ff 100644 --- a/test/WireMock.Net.Tests/RequestTests.cs +++ b/test/WireMock.Net.Tests/RequestTests.cs @@ -250,6 +250,19 @@ public void Should_specify_requests_matching_given_header_prefix() 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 { { "session", "abc" } }); + + // then + Check.That(spec.IsMatch(request)).IsTrue(); + } + [Test] public void Should_specify_requests_matching_given_body() {