diff --git a/examples/WireMock.Net.ConsoleApplication/Program.cs b/examples/WireMock.Net.ConsoleApplication/Program.cs index 969eee4a..ae4297de 100644 --- a/examples/WireMock.Net.ConsoleApplication/Program.cs +++ b/examples/WireMock.Net.ConsoleApplication/Program.cs @@ -1,5 +1,8 @@ using System; using Newtonsoft.Json; +using WireMock.RequestBuilders; +using WireMock.ResponseBuilders; + namespace WireMock.Net.ConsoleApplication { @@ -16,12 +19,12 @@ namespace WireMock.Net.ConsoleApplication server .Given( - Requests + RequestBuilder .WithUrl("/*") .UsingGet() ) .RespondWith( - Responses + ResponseBuilder .WithStatusCode(200) .WithHeader("Content-Type", "application/json") .WithBody(@"{ ""msg"": ""Hello world!""}") diff --git a/src/WireMock/HttpListenerRequestMapper.cs b/src/WireMock/HttpListenerRequestMapper.cs index d31588ec..051e47db 100644 --- a/src/WireMock/HttpListenerRequestMapper.cs +++ b/src/WireMock/HttpListenerRequestMapper.cs @@ -2,6 +2,7 @@ using System.IO; using System.Linq; using System.Net; +using WireMock.RequestBuilders; [module: SuppressMessage("StyleCop.CSharp.ReadabilityRules", @@ -26,7 +27,7 @@ namespace WireMock /// The listener request. /// /// - /// The . + /// The . /// public Request Map(HttpListenerRequest listenerRequest) { diff --git a/src/WireMock/ISpecifyRequests.cs b/src/WireMock/ISpecifyRequests.cs index d3515cba..cdee79ea 100644 --- a/src/WireMock/ISpecifyRequests.cs +++ b/src/WireMock/ISpecifyRequests.cs @@ -1,4 +1,5 @@ using System.Diagnostics.CodeAnalysis; +using JetBrains.Annotations; [module: SuppressMessage("StyleCop.CSharp.DocumentationRules", @@ -21,6 +22,6 @@ namespace WireMock /// /// The . /// - bool IsSatisfiedBy(Request request); + bool IsSatisfiedBy([NotNull] Request request); } } diff --git a/src/WireMock/Request.cs b/src/WireMock/Request.cs index f7bc0c79..cb5cc4b6 100644 --- a/src/WireMock/Request.cs +++ b/src/WireMock/Request.cs @@ -75,7 +75,7 @@ namespace WireMock } Path = path; - Headers = headers.ToDictionary(kv => kv.Key.ToLower(), kv => kv.Value.ToLower()); + Headers = headers; //.ToDictionary(kv => kv.Key.ToLower(), kv => kv.Value.ToLower()); Verb = verb.ToLower(); Body = body?.Trim() ?? string.Empty; } diff --git a/src/WireMock/RequestBodySpec.cs b/src/WireMock/RequestBodySpec.cs index f5388f5d..0c8f391c 100644 --- a/src/WireMock/RequestBodySpec.cs +++ b/src/WireMock/RequestBodySpec.cs @@ -1,4 +1,7 @@ using System.Diagnostics.CodeAnalysis; +using System.Text.RegularExpressions; +using JetBrains.Annotations; +using WireMock.Validation; [module: SuppressMessage("StyleCop.CSharp.ReadabilityRules", @@ -22,19 +25,20 @@ namespace WireMock public class RequestBodySpec : ISpecifyRequests { /// - /// The _body. + /// The bodyRegex. /// - private readonly string _body; + private readonly Regex bodyRegex; /// /// Initializes a new instance of the class. /// /// - /// The body. + /// The body Regex pattern. /// - public RequestBodySpec(string body) + public RequestBodySpec([NotNull, RegexPattern] string body) { - _body = body.Trim(); + Check.NotNull(body, nameof(body)); + bodyRegex = new Regex(body); } /// @@ -48,7 +52,7 @@ namespace WireMock /// public bool IsSatisfiedBy(Request request) { - return WildcardPatternMatcher.MatchWildcardString(_body, request.Body.Trim()); + return bodyRegex.IsMatch(request.Body); } } } diff --git a/src/WireMock/RequestBuilders/IBodyRequestBuilder.cs b/src/WireMock/RequestBuilders/IBodyRequestBuilder.cs new file mode 100644 index 00000000..b1134a3c --- /dev/null +++ b/src/WireMock/RequestBuilders/IBodyRequestBuilder.cs @@ -0,0 +1,19 @@ +namespace WireMock.RequestBuilders +{ + /// + /// The BodyRequestBuilder interface. + /// + public interface IBodyRequestBuilder + { + /// + /// The with body. + /// + /// + /// The body. + /// + /// + /// The . + /// + ISpecifyRequests WithBody(string body); + } +} \ No newline at end of file diff --git a/src/WireMock/RequestBuilders/IHeadersRequestBuilder.cs b/src/WireMock/RequestBuilders/IHeadersRequestBuilder.cs new file mode 100644 index 00000000..7595f30a --- /dev/null +++ b/src/WireMock/RequestBuilders/IHeadersRequestBuilder.cs @@ -0,0 +1,23 @@ +namespace WireMock.RequestBuilders +{ + /// + /// The HeadersRequestBuilder interface. + /// + public interface IHeadersRequestBuilder : IBodyRequestBuilder, ISpecifyRequests, IParamsRequestBuilder + { + /// + /// The with header. + /// + /// + /// The name. + /// + /// + /// The value. + /// + /// ignore Case + /// + /// The . + /// + IHeadersRequestBuilder WithHeader(string name, string value, bool ignoreCase = true); + } +} \ No newline at end of file diff --git a/src/WireMock/RequestBuilders/IParamsRequestBuilder.cs b/src/WireMock/RequestBuilders/IParamsRequestBuilder.cs new file mode 100644 index 00000000..2a639694 --- /dev/null +++ b/src/WireMock/RequestBuilders/IParamsRequestBuilder.cs @@ -0,0 +1,22 @@ +namespace WireMock.RequestBuilders +{ + /// + /// The ParametersRequestBuilder interface. + /// + public interface IParamsRequestBuilder + { + /// + /// The with parameters. + /// + /// + /// The key. + /// + /// + /// The values. + /// + /// + /// The . + /// + ISpecifyRequests WithParam(string key, params string[] values); + } +} \ No newline at end of file diff --git a/src/WireMock/RequestBuilders/IVerbRequestBuilder.cs b/src/WireMock/RequestBuilders/IVerbRequestBuilder.cs new file mode 100644 index 00000000..df8465a0 --- /dev/null +++ b/src/WireMock/RequestBuilders/IVerbRequestBuilder.cs @@ -0,0 +1,59 @@ +namespace WireMock.RequestBuilders +{ + /// + /// The VerbRequestBuilder interface. + /// + public interface IVerbRequestBuilder : ISpecifyRequests, IHeadersRequestBuilder + { + /// + /// The using get. + /// + /// + /// The . + /// + IHeadersRequestBuilder UsingGet(); + + /// + /// The using post. + /// + /// + /// The . + /// + IHeadersRequestBuilder UsingPost(); + + /// + /// The using put. + /// + /// + /// The . + /// + IHeadersRequestBuilder UsingPut(); + + /// + /// The using head. + /// + /// + /// The . + /// + IHeadersRequestBuilder UsingHead(); + + /// + /// The using any verb. + /// + /// + /// The . + /// + IHeadersRequestBuilder UsingAnyVerb(); + + /// + /// The using verb. + /// + /// + /// The verb. + /// + /// + /// The . + /// + IHeadersRequestBuilder UsingVerb(string verb); + } +} \ No newline at end of file diff --git a/src/WireMock/Requests.cs b/src/WireMock/RequestBuilders/RequestBuilder.cs similarity index 90% rename from src/WireMock/Requests.cs rename to src/WireMock/RequestBuilders/RequestBuilder.cs index bd5b2fe4..2abc75a2 100644 --- a/src/WireMock/Requests.cs +++ b/src/WireMock/RequestBuilders/RequestBuilder.cs @@ -20,12 +20,12 @@ using System.Linq; Justification = "Reviewed. Suppression is OK here, as unknown copyright and company.")] // ReSharper disable ArrangeThisQualifier // ReSharper disable InconsistentNaming -namespace WireMock +namespace WireMock.RequestBuilders { /// /// The requests. /// - public class Requests : CompositeRequestSpec, IVerbRequestBuilder, IHeadersRequestBuilder, IParamsRequestBuilder + public class RequestBuilder : CompositeRequestSpec, IVerbRequestBuilder, IHeadersRequestBuilder, IParamsRequestBuilder { /// /// The _request specs. @@ -33,12 +33,12 @@ namespace WireMock private readonly IList _requestSpecs; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// /// The request specs. /// - private Requests(IList requestSpecs) : base(requestSpecs) + private RequestBuilder(IList requestSpecs) : base(requestSpecs) { _requestSpecs = requestSpecs; } @@ -55,7 +55,7 @@ namespace WireMock public static IVerbRequestBuilder WithUrl(string url) { var specs = new List(); - var requests = new Requests(specs); + var requests = new RequestBuilder(specs); specs.Add(new RequestUrlSpec(url)); return requests; } @@ -72,7 +72,7 @@ namespace WireMock public static IVerbRequestBuilder WithPath(string path) { var specs = new List(); - var requests = new Requests(specs); + var requests = new RequestBuilder(specs); specs.Add(new RequestPathSpec(path)); return requests; } @@ -188,17 +188,18 @@ namespace WireMock /// The with header. /// /// - /// The name. + /// The name. /// /// - /// The value. + /// The value. /// + /// ignore Case /// /// The . /// - public IHeadersRequestBuilder WithHeader(string name, string value) + public IHeadersRequestBuilder WithHeader(string name, string value, bool ignoreCase = true) { - _requestSpecs.Add(new RequestHeaderSpec(name, value)); + _requestSpecs.Add(new RequestHeaderSpec(name, value, ignoreCase)); return this; } } diff --git a/src/WireMock/RequestHeaderSpec.cs b/src/WireMock/RequestHeaderSpec.cs index 91961c01..a3a30a68 100644 --- a/src/WireMock/RequestHeaderSpec.cs +++ b/src/WireMock/RequestHeaderSpec.cs @@ -1,4 +1,6 @@ using System.Diagnostics.CodeAnalysis; +using System.Text.RegularExpressions; +using JetBrains.Annotations; [module: SuppressMessage("StyleCop.CSharp.ReadabilityRules", @@ -22,14 +24,14 @@ namespace WireMock public class RequestHeaderSpec : ISpecifyRequests { /// - /// The _name. + /// The name. /// - private readonly string _name; + private readonly string name; /// - /// The _pattern. + /// The patternRegex. /// - private readonly string _pattern; + private readonly Regex patternRegex; /// /// Initializes a new instance of the class. @@ -40,10 +42,11 @@ namespace WireMock /// /// The pattern. /// - public RequestHeaderSpec(string name, string pattern) + /// The ignoreCase. + public RequestHeaderSpec([NotNull] string name, [NotNull, RegexPattern] string pattern, bool ignoreCase = true) { - _name = name.ToLower(); - _pattern = pattern.ToLower(); + this.name = name; + patternRegex = ignoreCase ? new Regex(pattern, RegexOptions.IgnoreCase) : new Regex(pattern); } /// @@ -55,10 +58,10 @@ namespace WireMock /// /// The . /// - public bool IsSatisfiedBy(Request request) + public bool IsSatisfiedBy([NotNull] Request request) { - string headerValue = request.Headers[_name]; - return WildcardPatternMatcher.MatchWildcardString(_pattern, headerValue); + string headerValue = request.Headers[name]; + return patternRegex.IsMatch(headerValue); } } } \ No newline at end of file diff --git a/src/WireMock/RequestParamSpec.cs b/src/WireMock/RequestParamSpec.cs index 19de981b..b7949d9e 100644 --- a/src/WireMock/RequestParamSpec.cs +++ b/src/WireMock/RequestParamSpec.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; +using JetBrains.Annotations; [module: SuppressMessage("StyleCop.CSharp.ReadabilityRules", @@ -57,7 +58,7 @@ namespace WireMock /// /// The . /// - public bool IsSatisfiedBy(Request request) + public bool IsSatisfiedBy([NotNull] Request request) { return request.GetParameter(_key).Intersect(_values).Count() == _values.Count; } diff --git a/src/WireMock/RequestPathSpec.cs b/src/WireMock/RequestPathSpec.cs index a5b45bea..19744018 100644 --- a/src/WireMock/RequestPathSpec.cs +++ b/src/WireMock/RequestPathSpec.cs @@ -1,4 +1,7 @@ using System.Diagnostics.CodeAnalysis; +using System.Text.RegularExpressions; +using JetBrains.Annotations; +using WireMock.Validation; [module: SuppressMessage("StyleCop.CSharp.ReadabilityRules", @@ -24,17 +27,18 @@ namespace WireMock /// /// The _path. /// - private readonly string _path; + private readonly Regex _path; /// /// Initializes a new instance of the class. /// /// - /// The path. + /// The path Regex pattern. /// - public RequestPathSpec(string path) + public RequestPathSpec([NotNull, RegexPattern] string path) { - _path = path; + Check.NotNull(path, nameof(path)); + _path = new Regex(path); } /// @@ -46,9 +50,9 @@ namespace WireMock /// /// The . /// - public bool IsSatisfiedBy(Request request) + public bool IsSatisfiedBy([NotNull] Request request) { - return WildcardPatternMatcher.MatchWildcardString(_path, request.Path); + return _path.IsMatch(request.Path); } } } diff --git a/src/WireMock/RequestSpecBuilder.cs b/src/WireMock/RequestSpecBuilder.cs deleted file mode 100644 index f61eaa53..00000000 --- a/src/WireMock/RequestSpecBuilder.cs +++ /dev/null @@ -1,123 +0,0 @@ -using System.Diagnostics.CodeAnalysis; - -[module: - SuppressMessage("StyleCop.CSharp.DocumentationRules", - "SA1633:FileMustHaveHeader", - Justification = "Reviewed. Suppression is OK here, as unknown copyright and company.")] - -namespace WireMock -{ - /// - /// The VerbRequestBuilder interface. - /// - public interface IVerbRequestBuilder : ISpecifyRequests, IHeadersRequestBuilder - { - /// - /// The using get. - /// - /// - /// The . - /// - IHeadersRequestBuilder UsingGet(); - - /// - /// The using post. - /// - /// - /// The . - /// - IHeadersRequestBuilder UsingPost(); - - /// - /// The using put. - /// - /// - /// The . - /// - IHeadersRequestBuilder UsingPut(); - - /// - /// The using head. - /// - /// - /// The . - /// - IHeadersRequestBuilder UsingHead(); - - /// - /// The using any verb. - /// - /// - /// The . - /// - IHeadersRequestBuilder UsingAnyVerb(); - - /// - /// The using verb. - /// - /// - /// The verb. - /// - /// - /// The . - /// - IHeadersRequestBuilder UsingVerb(string verb); - } - - /// - /// The HeadersRequestBuilder interface. - /// - public interface IHeadersRequestBuilder : IBodyRequestBuilder, ISpecifyRequests, IParamsRequestBuilder - { - /// - /// The with header. - /// - /// - /// The name. - /// - /// - /// The value. - /// - /// - /// The . - /// - IHeadersRequestBuilder WithHeader(string name, string value); - } - - /// - /// The BodyRequestBuilder interface. - /// - public interface IBodyRequestBuilder - { - /// - /// The with body. - /// - /// - /// The body. - /// - /// - /// The . - /// - ISpecifyRequests WithBody(string body); - } - - /// - /// The ParametersRequestBuilder interface. - /// - public interface IParamsRequestBuilder - { - /// - /// The with parameters. - /// - /// - /// The key. - /// - /// - /// The values. - /// - /// - /// The . - /// - ISpecifyRequests WithParam(string key, params string[] values); - } -} diff --git a/src/WireMock/RequestUrlSpec.cs b/src/WireMock/RequestUrlSpec.cs index b3ea9f78..32af08a9 100644 --- a/src/WireMock/RequestUrlSpec.cs +++ b/src/WireMock/RequestUrlSpec.cs @@ -1,4 +1,7 @@ using System.Diagnostics.CodeAnalysis; +using JetBrains.Annotations; +using System.Text.RegularExpressions; +using WireMock.Validation; [module: SuppressMessage("StyleCop.CSharp.ReadabilityRules", @@ -22,19 +25,20 @@ namespace WireMock public class RequestUrlSpec : ISpecifyRequests { /// - /// The _url. + /// The urlRegex. /// - private readonly string _url; + private readonly Regex urlRegex; /// /// Initializes a new instance of the class. /// /// - /// The url. + /// The url Regex pattern. /// - public RequestUrlSpec(string url) + public RequestUrlSpec([NotNull, RegexPattern] string url) { - _url = url; + Check.NotNull(url, nameof(url)); + urlRegex = new Regex(url); } /// @@ -48,7 +52,7 @@ namespace WireMock /// public bool IsSatisfiedBy(Request request) { - return WildcardPatternMatcher.MatchWildcardString(_url, request.Url); + return urlRegex.IsMatch(request.Url); } } } diff --git a/src/WireMock/RequestVerbSpec.cs b/src/WireMock/RequestVerbSpec.cs index cb362cf9..9f153534 100644 --- a/src/WireMock/RequestVerbSpec.cs +++ b/src/WireMock/RequestVerbSpec.cs @@ -1,4 +1,5 @@ using System.Diagnostics.CodeAnalysis; +using JetBrains.Annotations; [module: SuppressMessage("StyleCop.CSharp.ReadabilityRules", @@ -46,7 +47,7 @@ namespace WireMock /// /// The . /// - public bool IsSatisfiedBy(Request request) + public bool IsSatisfiedBy([NotNull] Request request) { return request.Verb == _verb; } diff --git a/src/WireMock/ResponseBuilder.cs b/src/WireMock/ResponseBuilder.cs deleted file mode 100644 index 9c23a1d5..00000000 --- a/src/WireMock/ResponseBuilder.cs +++ /dev/null @@ -1,64 +0,0 @@ -using System; -using System.Diagnostics.CodeAnalysis; - -[module: - SuppressMessage("StyleCop.CSharp.DocumentationRules", - "SA1633:FileMustHaveHeader", - Justification = "Reviewed. Suppression is OK here, as unknown copyright and company.")] - -namespace WireMock -{ - /// - /// The HeadersResponseBuilder interface. - /// - public interface IHeadersResponseBuilder : IBodyResponseBuilder - { - /// - /// The with header. - /// - /// - /// The name. - /// - /// - /// The value. - /// - /// - /// The . - /// - IHeadersResponseBuilder WithHeader(string name, string value); - } - - /// - /// The BodyResponseBuilder interface. - /// - public interface IBodyResponseBuilder : IDelayResponseBuilder - { - /// - /// The with body. - /// - /// - /// The body. - /// - /// - /// The . - /// - IDelayResponseBuilder WithBody(string body); - } - - /// - /// The DelayResponseBuilder interface. - /// - public interface IDelayResponseBuilder : IProvideResponses - { - /// - /// The after delay. - /// - /// - /// The delay. - /// - /// - /// The . - /// - IProvideResponses AfterDelay(TimeSpan delay); - } -} diff --git a/src/WireMock/ResponseBuilders/IBodyResponseBuilder.cs b/src/WireMock/ResponseBuilders/IBodyResponseBuilder.cs new file mode 100644 index 00000000..1cb611b8 --- /dev/null +++ b/src/WireMock/ResponseBuilders/IBodyResponseBuilder.cs @@ -0,0 +1,19 @@ +namespace WireMock.ResponseBuilders +{ + /// + /// The BodyResponseBuilder interface. + /// + public interface IBodyResponseBuilder : IDelayResponseBuilder + { + /// + /// The with body. + /// + /// + /// The body. + /// + /// + /// The . + /// + IDelayResponseBuilder WithBody(string body); + } +} \ No newline at end of file diff --git a/src/WireMock/ResponseBuilders/IDelayResponseBuilder.cs b/src/WireMock/ResponseBuilders/IDelayResponseBuilder.cs new file mode 100644 index 00000000..12efae83 --- /dev/null +++ b/src/WireMock/ResponseBuilders/IDelayResponseBuilder.cs @@ -0,0 +1,21 @@ +using System; + +namespace WireMock.ResponseBuilders +{ + /// + /// The DelayResponseBuilder interface. + /// + public interface IDelayResponseBuilder : IProvideResponses + { + /// + /// The after delay. + /// + /// + /// The delay. + /// + /// + /// The . + /// + IProvideResponses AfterDelay(TimeSpan delay); + } +} \ No newline at end of file diff --git a/src/WireMock/ResponseBuilders/IHeadersResponseBuilder.cs b/src/WireMock/ResponseBuilders/IHeadersResponseBuilder.cs new file mode 100644 index 00000000..eaf8dd76 --- /dev/null +++ b/src/WireMock/ResponseBuilders/IHeadersResponseBuilder.cs @@ -0,0 +1,22 @@ +namespace WireMock.ResponseBuilders +{ + /// + /// The HeadersResponseBuilder interface. + /// + public interface IHeadersResponseBuilder : IBodyResponseBuilder + { + /// + /// The with header. + /// + /// + /// The name. + /// + /// + /// The value. + /// + /// + /// The . + /// + IHeadersResponseBuilder WithHeader(string name, string value); + } +} \ No newline at end of file diff --git a/src/WireMock/Responses.cs b/src/WireMock/ResponseBuilders/ResponseBuilder.cs similarity index 94% rename from src/WireMock/Responses.cs rename to src/WireMock/ResponseBuilders/ResponseBuilder.cs index f74ba8c8..0070176f 100644 --- a/src/WireMock/Responses.cs +++ b/src/WireMock/ResponseBuilders/ResponseBuilder.cs @@ -16,12 +16,12 @@ using System.Threading.Tasks; Justification = "Reviewed. Suppression is OK here, as unknown copyright and company.")] // ReSharper disable ArrangeThisQualifier // ReSharper disable InconsistentNaming -namespace WireMock +namespace WireMock.ResponseBuilders { /// /// The responses. /// - public class Responses : IHeadersResponseBuilder + public class ResponseBuilder : IHeadersResponseBuilder { /// /// The _response. @@ -34,12 +34,12 @@ namespace WireMock private TimeSpan _delay = TimeSpan.Zero; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// /// The response. /// - public Responses(Response response) + public ResponseBuilder(Response response) { _response = response; } @@ -74,7 +74,7 @@ namespace WireMock public static IHeadersResponseBuilder WithStatusCode(int code) { var response = new Response { StatusCode = code }; - return new Responses(response); + return new ResponseBuilder(response); } /// diff --git a/src/WireMock/Validation/Check.cs b/src/WireMock/Validation/Check.cs new file mode 100644 index 00000000..05e9e7ab --- /dev/null +++ b/src/WireMock/Validation/Check.cs @@ -0,0 +1,141 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Reflection; +using JetBrains.Annotations; + +// Copied from https://github.com/aspnet/EntityFramework/blob/dev/src/Shared/Check.cs +namespace WireMock.Validation +{ + [DebuggerStepThrough] + internal static class Check + { + [ContractAnnotation("value:null => halt")] + public static T Condition([NoEnumeration] T value, [NotNull] Predicate condition, [InvokerParameterName] [NotNull] string parameterName) + { + NotNull(condition, nameof(condition)); + NotNull(value, nameof(value)); + + if (!condition(value)) + { + NotEmpty(parameterName, nameof(parameterName)); + + throw new ArgumentOutOfRangeException(parameterName); + } + + return value; + } + + [ContractAnnotation("value:null => halt")] + public static T NotNull([NoEnumeration] T value, [InvokerParameterName] [NotNull] string parameterName) + { + if (ReferenceEquals(value, null)) + { + NotEmpty(parameterName, nameof(parameterName)); + + throw new ArgumentNullException(parameterName); + } + + return value; + } + + [ContractAnnotation("value:null => halt")] + public static T NotNull( + [NoEnumeration] T value, + [InvokerParameterName] [NotNull] string parameterName, + [NotNull] string propertyName) + { + if (ReferenceEquals(value, null)) + { + NotEmpty(parameterName, nameof(parameterName)); + NotEmpty(propertyName, nameof(propertyName)); + + throw new ArgumentException(CoreStrings.ArgumentPropertyNull(propertyName, parameterName)); + } + + return value; + } + + [ContractAnnotation("value:null => halt")] + public static IList NotEmpty(IList value, [InvokerParameterName] [NotNull] string parameterName) + { + NotNull(value, parameterName); + + if (value.Count == 0) + { + NotEmpty(parameterName, nameof(parameterName)); + + throw new ArgumentException(CoreStrings.CollectionArgumentIsEmpty(parameterName)); + } + + return value; + } + + [ContractAnnotation("value:null => halt")] + public static string NotEmpty(string value, [InvokerParameterName] [NotNull] string parameterName) + { + Exception e = null; + if (ReferenceEquals(value, null)) + { + e = new ArgumentNullException(parameterName); + } + else if (value.Trim().Length == 0) + { + e = new ArgumentException(CoreStrings.ArgumentIsEmpty(parameterName)); + } + + if (e != null) + { + NotEmpty(parameterName, nameof(parameterName)); + + throw e; + } + + return value; + } + + public static string NullButNotEmpty(string value, [InvokerParameterName] [NotNull] string parameterName) + { + if (!ReferenceEquals(value, null) + && (value.Length == 0)) + { + NotEmpty(parameterName, nameof(parameterName)); + + throw new ArgumentException(CoreStrings.ArgumentIsEmpty(parameterName)); + } + + return value; + } + + public static IList HasNoNulls(IList value, [InvokerParameterName] [NotNull] string parameterName) + where T : class + { + NotNull(value, parameterName); + + if (value.Any(e => e == null)) + { + NotEmpty(parameterName, nameof(parameterName)); + + throw new ArgumentException(parameterName); + } + + return value; + } + + public static Type ValidEntityType(Type value, [InvokerParameterName] [NotNull] string parameterName) + { + if (!value.GetTypeInfo().IsClass) + { + NotEmpty(parameterName, nameof(parameterName)); + + throw new ArgumentException(CoreStrings.InvalidEntityType(value, parameterName)); + } + + return value; + } + } +} \ No newline at end of file diff --git a/src/WireMock/Validation/CoreStrings.cs b/src/WireMock/Validation/CoreStrings.cs new file mode 100644 index 00000000..c6f259d9 --- /dev/null +++ b/src/WireMock/Validation/CoreStrings.cs @@ -0,0 +1,42 @@ +using System; +using System.Globalization; +using JetBrains.Annotations; + +// copied from https://github.com/aspnet/EntityFramework/blob/dev/src/Microsoft.EntityFrameworkCore/Properties/CoreStrings.resx +namespace WireMock.Validation +{ + internal static class CoreStrings + { + /// + /// The property '{property}' of the argument '{argument}' cannot be null. + /// + public static string ArgumentPropertyNull([CanBeNull] string property, [CanBeNull] string argument) + { + return string.Format(CultureInfo.CurrentCulture, $"The property '{property}' of the argument '{argument}' cannot be null.", property, argument); + } + + /// + /// The string argument '{argumentName}' cannot be empty. + /// + public static string ArgumentIsEmpty([CanBeNull] string argumentName) + { + return string.Format(CultureInfo.CurrentCulture, $"The string argument '{argumentName}' cannot be empty.", argumentName); + } + + /// + /// The entity type '{type}' provided for the argument '{argumentName}' must be a reference type. + /// + public static string InvalidEntityType([CanBeNull] Type type, [CanBeNull] string argumentName) + { + return string.Format(CultureInfo.CurrentCulture, $"The entity type '{type}' provided for the argument '{argumentName}' must be a reference type.", type, argumentName); + } + + /// + /// The collection argument '{argumentName}' must contain at least one element. + /// + public static string CollectionArgumentIsEmpty([CanBeNull] string argumentName) + { + return string.Format(CultureInfo.CurrentCulture, $"The collection argument '{argumentName}' must contain at least one element.", argumentName); + } + } +} \ No newline at end of file diff --git a/src/WireMock/WildcardPatternMatcher.cs b/src/WireMock/WildcardPatternMatcher.cs deleted file mode 100644 index 5c54eaae..00000000 --- a/src/WireMock/WildcardPatternMatcher.cs +++ /dev/null @@ -1,74 +0,0 @@ -using System.Diagnostics.CodeAnalysis; - -[module: - SuppressMessage("StyleCop.CSharp.DocumentationRules", - "SA1633:FileMustHaveHeader", - Justification = "Reviewed. Suppression is OK here, as unknown copyright and company.")] -[module: - SuppressMessage("StyleCop.CSharp.DocumentationRules", - "SA1650:ElementDocumentationMustBeSpelledCorrectly", - Justification = "Reviewed. Suppression is OK here.")] - -namespace WireMock -{ - /// - /// The wildcard pattern matcher. - /// - public static class WildcardPatternMatcher - { - /// - /// The match wildcard string. - /// - /// - /// The pattern. - /// - /// - /// The input. - /// - /// - /// The . - /// - /// - /// Copy/paste from http://www.codeproject.com/Tips/57304/Use-wildcard-characters-and-to-compare-strings - /// - public static bool MatchWildcardString(string pattern, string input) - { - 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] == '*') - { - return MatchWildcardString(pattern.Substring(1), input) || MatchWildcardString(pattern, input.Substring(1)); - } - - if (pattern[pattern.Length - 1] == '*') - { - return MatchWildcardString(pattern.Substring(0, pattern.Length - 1), input) || MatchWildcardString(pattern, input.Substring(0, input.Length - 1)); - } - - return pattern[0] == input[0] && MatchWildcardString(pattern.Substring(1), input.Substring(1)); - } - } -} diff --git a/test/WireMock.Net.Tests/FluentMockServerTests.cs b/test/WireMock.Net.Tests/FluentMockServerTests.cs index 0093c816..c8c2726f 100644 --- a/test/WireMock.Net.Tests/FluentMockServerTests.cs +++ b/test/WireMock.Net.Tests/FluentMockServerTests.cs @@ -7,6 +7,8 @@ using System.Net.Http; using System.Threading.Tasks; using NFluent; using NUnit.Framework; +using WireMock.RequestBuilders; +using WireMock.ResponseBuilders; [module: SuppressMessage("StyleCop.CSharp.ReadabilityRules", @@ -41,10 +43,10 @@ namespace WireMock.Net.Tests _server = FluentMockServer.Start(); _server - .Given(Requests + .Given(RequestBuilder .WithUrl("/foo") .UsingGet()) - .RespondWith(Responses + .RespondWith(ResponseBuilder .WithStatusCode(200) .WithBody(@"{ msg: ""Hello world!""}")); @@ -98,7 +100,7 @@ namespace WireMock.Net.Tests await new HttpClient().GetAsync("http://localhost:" + _server.Port + "/bar"); // then - var result = _server.SearchLogsFor(Requests.WithUrl("/b*")); + var result = _server.SearchLogsFor(RequestBuilder.WithUrl("/b.*")); Check.That(result).HasSize(1); var requestLogged = result.First(); Check.That(requestLogged.Url).IsEqualTo("/bar"); @@ -125,10 +127,10 @@ namespace WireMock.Net.Tests _server = FluentMockServer.Start(); _server - .Given(Requests + .Given(RequestBuilder .WithUrl("/foo") .UsingGet()) - .RespondWith(Responses + .RespondWith(ResponseBuilder .WithStatusCode(200) .WithBody(@"{ msg: ""Hello world!""}")); @@ -147,17 +149,17 @@ namespace WireMock.Net.Tests _server = FluentMockServer.Start(); _server - .Given(Requests + .Given(RequestBuilder .WithUrl("/foo") .UsingGet()) - .RespondWith(Responses + .RespondWith(ResponseBuilder .WithStatusCode(307) .WithHeader("Location", "/bar")); _server - .Given(Requests + .Given(RequestBuilder .WithUrl("/bar") .UsingGet()) - .RespondWith(Responses + .RespondWith(ResponseBuilder .WithStatusCode(200) .WithBody("REDIRECT SUCCESSFUL")); @@ -176,9 +178,9 @@ namespace WireMock.Net.Tests _server = FluentMockServer.Start(); _server - .Given(Requests + .Given(RequestBuilder .WithUrl("/*")) - .RespondWith(Responses + .RespondWith(ResponseBuilder .WithStatusCode(200) .WithBody(@"{ msg: ""Hello world!""}") .AfterDelay(TimeSpan.FromMilliseconds(2000))); @@ -200,9 +202,9 @@ namespace WireMock.Net.Tests _server = FluentMockServer.Start(); _server.AddRequestProcessingDelay(TimeSpan.FromMilliseconds(2000)); _server - .Given(Requests + .Given(RequestBuilder .WithUrl("/*")) - .RespondWith(Responses + .RespondWith(ResponseBuilder .WithStatusCode(200) .WithBody(@"{ msg: ""Hello world!""}")); diff --git a/test/WireMock.Net.Tests/HttpListenerRequestMapperTests.cs b/test/WireMock.Net.Tests/HttpListenerRequestMapperTests.cs index 564bd62d..33815db2 100644 --- a/test/WireMock.Net.Tests/HttpListenerRequestMapperTests.cs +++ b/test/WireMock.Net.Tests/HttpListenerRequestMapperTests.cs @@ -94,7 +94,7 @@ namespace WireMock.Net.Tests // then Check.That(MapperServer.LastRequest).IsNotNull(); Check.That(MapperServer.LastRequest.Headers).Not.IsNullOrEmpty(); - Check.That(MapperServer.LastRequest.Headers.Contains(new KeyValuePair("x-alex", "1706"))).IsTrue(); + Check.That(MapperServer.LastRequest.Headers.Contains(new KeyValuePair("X-Alex", "1706"))).IsTrue(); } [Test] @@ -141,7 +141,7 @@ namespace WireMock.Net.Tests public static string UrlPrefix { get; private set; } - public static new MapperServer Start() + public new static MapperServer Start() { var port = Ports.FindFreeTcpPort(); UrlPrefix = "http://localhost:" + port + "/"; diff --git a/test/WireMock.Net.Tests/RequestsTests.cs b/test/WireMock.Net.Tests/RequestsTests.cs index d9606928..7a945d1a 100644 --- a/test/WireMock.Net.Tests/RequestsTests.cs +++ b/test/WireMock.Net.Tests/RequestsTests.cs @@ -2,14 +2,15 @@ using System.Diagnostics.CodeAnalysis; using NFluent; using NUnit.Framework; +using WireMock.RequestBuilders; [module: - SuppressMessage("StyleCop.CSharp.DocumentationRules", - "SA1600:ElementsMustBeDocumented", + 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", + SuppressMessage("StyleCop.CSharp.DocumentationRules", + "SA1633:FileMustHaveHeader", Justification = "Reviewed. Suppression is OK here, as unknown copyright and company.")] // ReSharper disable InconsistentNaming namespace WireMock.Net.Tests @@ -21,7 +22,7 @@ namespace WireMock.Net.Tests public void Should_specify_requests_matching_given_url() { // given - var spec = Requests.WithUrl("/foo"); + var spec = RequestBuilder.WithUrl("/foo"); // when var request = new Request("/foo", string.Empty, "blabla", "whatever", new Dictionary()); @@ -34,7 +35,7 @@ namespace WireMock.Net.Tests public void Should_specify_requests_matching_given_url_prefix() { // given - var spec = Requests.WithUrl("/foo*"); + var spec = RequestBuilder.WithUrl("/foo*"); // when var request = new Request("/foo/bar", string.Empty, "blabla", "whatever", new Dictionary()); @@ -47,7 +48,7 @@ namespace WireMock.Net.Tests public void Should_exclude_requests_not_matching_given_url() { // given - var spec = Requests.WithUrl("/foo"); + var spec = RequestBuilder.WithUrl("/foo"); // when var request = new Request("/bar", string.Empty, "blabla", "whatever", new Dictionary()); @@ -60,7 +61,7 @@ namespace WireMock.Net.Tests public void Should_specify_requests_matching_given_path() { // given - var spec = Requests.WithPath("/foo"); + var spec = RequestBuilder.WithPath("/foo"); // when var request = new Request("/foo", "?param=1", "blabla", "whatever", new Dictionary()); @@ -73,7 +74,7 @@ namespace WireMock.Net.Tests public void Should_specify_requests_matching_given_url_and_method() { // given - var spec = Requests.WithUrl("/foo").UsingPut(); + var spec = RequestBuilder.WithUrl("/foo").UsingPut(); // when var request = new Request("/foo", string.Empty, "PUT", "whatever", new Dictionary()); @@ -86,7 +87,7 @@ namespace WireMock.Net.Tests public void Should_exclude_requests_matching_given_url_but_not_http_method() { // given - var spec = Requests.WithUrl("/foo").UsingPut(); + var spec = RequestBuilder.WithUrl("/foo").UsingPut(); // when var request = new Request("/foo", string.Empty, "POST", "whatever", new Dictionary()); @@ -99,7 +100,7 @@ namespace WireMock.Net.Tests public void Should_exclude_requests_matching_given_http_method_but_not_url() { // given - var spec = Requests.WithUrl("/bar").UsingPut(); + var spec = RequestBuilder.WithUrl("/bar").UsingPut(); // when var request = new Request("/foo", string.Empty, "PUT", "whatever", new Dictionary()); @@ -112,11 +113,11 @@ namespace WireMock.Net.Tests public void Should_specify_requests_matching_given_url_and_headers() { // given - var spec = Requests.WithUrl("/foo").UsingAnyVerb().WithHeader("X-toto", "tata"); + var spec = RequestBuilder.WithUrl("/foo").UsingAnyVerb().WithHeader("X-toto", "tata"); // when var request = new Request("/foo", string.Empty, "PUT", "whatever", new Dictionary { { "X-toto", "tata" } }); - + // then Check.That(spec.IsSatisfiedBy(request)).IsTrue(); } @@ -125,7 +126,7 @@ namespace WireMock.Net.Tests public void Should_exclude_requests_not_matching_given_headers() { // given - var spec = Requests.WithUrl("/foo").UsingAnyVerb().WithHeader("X-toto", "tatata"); + var spec = RequestBuilder.WithUrl("/foo").UsingAnyVerb().WithHeader("X-toto", "tatata"); // when var request = new Request("/foo", string.Empty, "PUT", "whatever", new Dictionary { { "X-toto", "tata" } }); @@ -134,14 +135,27 @@ namespace WireMock.Net.Tests Check.That(spec.IsSatisfiedBy(request)).IsFalse(); } + [Test] + public void Should_exclude_requests_not_matching_given_headers_ignorecase() + { + // given + var spec = RequestBuilder.WithUrl("/foo").UsingAnyVerb().WithHeader("X-toto", "abc", false); + + // when + var request = new Request("/foo", string.Empty, "PUT", "whatever", new Dictionary { { "X-toto", "ABC" } }); + + // then + Check.That(spec.IsSatisfiedBy(request)).IsFalse(); + } + [Test] public void Should_specify_requests_matching_given_header_prefix() { // given - var spec = Requests.WithUrl("/foo").UsingAnyVerb().WithHeader("X-toto", "tata*"); + var spec = RequestBuilder.WithUrl("/foo").UsingAnyVerb().WithHeader("X-toto", "tata*"); // when - var request = new Request("/foo", string.Empty, "PUT", "whatever", new Dictionary { { "X-toto", "tatata" } }); + var request = new Request("/foo", string.Empty, "PUT", "whatever", new Dictionary { { "X-toto", "TaTaTa" } }); // then Check.That(spec.IsSatisfiedBy(request)).IsTrue(); @@ -151,7 +165,7 @@ namespace WireMock.Net.Tests public void Should_specify_requests_matching_given_body() { // given - var spec = Requests.WithUrl("/foo").UsingAnyVerb().WithBody(" Hello world! "); + var spec = RequestBuilder.WithUrl("/foo").UsingAnyVerb().WithBody(".*Hello world!.*"); // when var request = new Request("/foo", string.Empty, "PUT", "Hello world!", new Dictionary { { "X-toto", "tatata" } }); @@ -164,7 +178,7 @@ namespace WireMock.Net.Tests public void Should_specify_requests_matching_given_body_as_wildcard() { // given - var spec = Requests.WithUrl("/foo").UsingAnyVerb().WithBody("H*o wor?d!"); + var spec = RequestBuilder.WithUrl("/foo").UsingAnyVerb().WithBody("H.*o"); // when var request = new Request("/foo", string.Empty, "PUT", "Hello world!", new Dictionary { { "X-toto", "tatata" } }); @@ -177,7 +191,7 @@ namespace WireMock.Net.Tests public void Should_exclude_requests_not_matching_given_body() { // given - var spec = Requests.WithUrl("/foo").UsingAnyVerb().WithBody(" Hello world! "); + var spec = RequestBuilder.WithUrl("/foo").UsingAnyVerb().WithBody(" Hello world! "); // when var request = new Request("/foo", string.Empty, "PUT", "XXXXXXXXXXX", new Dictionary { { "X-toto", "tatata" } }); @@ -190,7 +204,7 @@ namespace WireMock.Net.Tests public void Should_specify_requests_matching_given_params() { // given - var spec = Requests.WithPath("/foo").WithParam("bar", "1", "2"); + var spec = RequestBuilder.WithPath("/foo").WithParam("bar", "1", "2"); // when var request = new Request("/foo", "bar=1&bar=2", "Get", "Hello world!", new Dictionary()); @@ -203,7 +217,7 @@ namespace WireMock.Net.Tests public void Should_exclude_requests_not_matching_given_params() { // given - var spec = Requests.WithPath("/foo").WithParam("bar", "1"); + var spec = RequestBuilder.WithPath("/foo").WithParam("bar", "1"); // when var request = new Request("/foo", string.Empty, "PUT", "XXXXXXXXXXX", new Dictionary()); diff --git a/test/WireMock.Net.Tests/WildcardPatternMatcherTests.cs b/test/WireMock.Net.Tests/WildcardPatternMatcherTests.cs deleted file mode 100644 index 91486bf4..00000000 --- a/test/WireMock.Net.Tests/WildcardPatternMatcherTests.cs +++ /dev/null @@ -1,48 +0,0 @@ -using System.Diagnostics.CodeAnalysis; -using NFluent; -using NUnit.Framework; - -[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] - public class WildcardPatternMatcherTests - { - [Test] - public void Should_evaluate_patterns() - { - // Positive Tests - Check.That(WildcardPatternMatcher.MatchWildcardString("*", string.Empty)).IsTrue(); - Check.That(WildcardPatternMatcher.MatchWildcardString("?", " ")).IsTrue(); - Check.That(WildcardPatternMatcher.MatchWildcardString("*", "a")).IsTrue(); - Check.That(WildcardPatternMatcher.MatchWildcardString("*", "ab")).IsTrue(); - Check.That(WildcardPatternMatcher.MatchWildcardString("?", "a")).IsTrue(); - Check.That(WildcardPatternMatcher.MatchWildcardString("*?", "abc")).IsTrue(); - Check.That(WildcardPatternMatcher.MatchWildcardString("?*", "abc")).IsTrue(); - Check.That(WildcardPatternMatcher.MatchWildcardString("*abc", "abc")).IsTrue(); - Check.That(WildcardPatternMatcher.MatchWildcardString("*abc*", "abc")).IsTrue(); - Check.That(WildcardPatternMatcher.MatchWildcardString("*a*bc*", "aXXXbc")).IsTrue(); - - // Negative Tests - Check.That(WildcardPatternMatcher.MatchWildcardString("*a", string.Empty)).IsFalse(); - Check.That(WildcardPatternMatcher.MatchWildcardString("a*", string.Empty)).IsFalse(); - Check.That(WildcardPatternMatcher.MatchWildcardString("?", string.Empty)).IsFalse(); - Check.That(WildcardPatternMatcher.MatchWildcardString("*b*", "a")).IsFalse(); - Check.That(WildcardPatternMatcher.MatchWildcardString("b*a", "ab")).IsFalse(); - Check.That(WildcardPatternMatcher.MatchWildcardString("??", "a")).IsFalse(); - Check.That(WildcardPatternMatcher.MatchWildcardString("*?", string.Empty)).IsFalse(); - Check.That(WildcardPatternMatcher.MatchWildcardString("??*", "a")).IsFalse(); - Check.That(WildcardPatternMatcher.MatchWildcardString("*abc", "abX")).IsFalse(); - Check.That(WildcardPatternMatcher.MatchWildcardString("*abc*", "Xbc")).IsFalse(); - Check.That(WildcardPatternMatcher.MatchWildcardString("*a*bc*", "ac")).IsFalse(); - } - } -} diff --git a/test/WireMock.Net.Tests/WireMock.Net.Tests.csproj b/test/WireMock.Net.Tests/WireMock.Net.Tests.csproj index abf5ae38..5993db14 100644 --- a/test/WireMock.Net.Tests/WireMock.Net.Tests.csproj +++ b/test/WireMock.Net.Tests/WireMock.Net.Tests.csproj @@ -66,7 +66,6 @@ -