diff --git a/examples/WireMock.Net.ConsoleApplication/MainApp.cs b/examples/WireMock.Net.ConsoleApplication/MainApp.cs index 1174af95..dd33505e 100644 --- a/examples/WireMock.Net.ConsoleApplication/MainApp.cs +++ b/examples/WireMock.Net.ConsoleApplication/MainApp.cs @@ -81,8 +81,29 @@ namespace WireMock.Net.ConsoleApplication .WithParam("$filter", "(substringof(Code, 'WA')") .UsingGet()) .RespondWith(Response.Create() + .WithHeader("Content-Type", "application/json") .WithBody(@"{ ""result"": ""odata""}")); + server + .Given(Request + .Create() + .WithPath(new WildcardMatcher("/param2", true)) + .WithParam("key", "test") + .UsingGet()) + .RespondWith(Response.Create() + .WithHeader("Content-Type", "application/json") + .WithBodyAsJson(new { result = "param2" })); + + server + .Given(Request + .Create() + .WithPath(new WildcardMatcher("/param3", true)) + .WithParam("key", new WildcardMatcher("t*")) + .UsingGet()) + .RespondWith(Response.Create() + .WithHeader("Content-Type", "application/json") + .WithBodyAsJson(new { result = "param3" })); + server .Given(Request.Create().WithPath("/headers", "/headers_test").UsingPost().WithHeader("Content-Type", "application/json*")) .RespondWith(Response.Create() diff --git a/src/WireMock.Net/Admin/Mappings/ParamModel.cs b/src/WireMock.Net/Admin/Mappings/ParamModel.cs index d118c734..1fe25043 100644 --- a/src/WireMock.Net/Admin/Mappings/ParamModel.cs +++ b/src/WireMock.Net/Admin/Mappings/ParamModel.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +// using System.Collections.Generic; namespace WireMock.Admin.Mappings { @@ -12,10 +12,15 @@ namespace WireMock.Admin.Mappings /// public string Name { get; set; } + ///// + ///// Gets or sets the values. + ///// + //public IList Values { get; set; } + /// - /// Gets or sets the values. + /// Gets or sets the matchers. /// - public IList Values { get; set; } + public MatcherModel[] Matchers { get; set; } ///// ///// Gets or sets the functions. diff --git a/src/WireMock.Net/Matchers/Request/RequestMessageParamMatcher.cs b/src/WireMock.Net/Matchers/Request/RequestMessageParamMatcher.cs index be6fcee5..47b4fe74 100644 --- a/src/WireMock.Net/Matchers/Request/RequestMessageParamMatcher.cs +++ b/src/WireMock.Net/Matchers/Request/RequestMessageParamMatcher.cs @@ -25,16 +25,16 @@ namespace WireMock.Matchers.Request public string Key { get; } /// - /// The values + /// The matchers. /// - public IEnumerable Values { get; } + public IReadOnlyList Matchers { get; } /// /// Initializes a new instance of the class. /// /// The match behaviour. /// The key. - public RequestMessageParamMatcher(MatchBehaviour matchBehaviour, [NotNull] string key) : this(matchBehaviour, key, null) + public RequestMessageParamMatcher(MatchBehaviour matchBehaviour, [NotNull] string key) : this(matchBehaviour, key, (IStringMatcher[])null) { } @@ -44,13 +44,23 @@ namespace WireMock.Matchers.Request /// The match behaviour. /// The key. /// The values. - public RequestMessageParamMatcher(MatchBehaviour matchBehaviour, [NotNull] string key, [CanBeNull] IEnumerable values) + public RequestMessageParamMatcher(MatchBehaviour matchBehaviour, [NotNull] string key, [CanBeNull] string[] values) : this(matchBehaviour, key, values?.Select(value => new ExactMatcher(matchBehaviour, value)).Cast().ToArray()) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The match behaviour. + /// The key. + /// The matchers. + public RequestMessageParamMatcher(MatchBehaviour matchBehaviour, [NotNull] string key, [CanBeNull] IStringMatcher[] matchers) { Check.NotNull(key, nameof(key)); _matchBehaviour = matchBehaviour; Key = key; - Values = values; + Matchers = matchers; } /// @@ -78,21 +88,33 @@ namespace WireMock.Matchers.Request return MatchScores.ToScore(requestMessage.Query != null && Funcs.Any(f => f(requestMessage.Query))); } - var values = requestMessage.GetParameter(Key); - if (values == null) + WireMockList valuesPresentInRequestMessage = requestMessage.GetParameter(Key); + if (valuesPresentInRequestMessage == null) { - // Key is not present, just return Mismatch + // Key is not present at all, just return Mismatch return MatchScores.Mismatch; } - if (values.Count == 0 && (Values == null || !Values.Any())) + if (Matchers != null && Matchers.Any()) { - // Key is present, but no values or null, just return Perfect + // Matchers are defined, just use the matchers to calculate the match score. + var scores = new List(); + foreach (string valuePresentInRequestMessage in valuesPresentInRequestMessage) + { + double score = Matchers.Max(m => m.IsMatch(valuePresentInRequestMessage)); + scores.Add(score); + } + + return scores.Any() ? scores.Average() : MatchScores.Mismatch; + } + + if (Matchers == null || !Matchers.Any()) + { + // Matchers are null or not defined, and Key is present, just return Perfect. return MatchScores.Perfect; } - var matches = Values.Select(v => values.Contains(v)); - return MatchScores.ToScore(matches); + return MatchScores.Mismatch; } } } \ No newline at end of file diff --git a/src/WireMock.Net/RequestBuilders/IParamsRequestBuilder.cs b/src/WireMock.Net/RequestBuilders/IParamsRequestBuilder.cs index 0194de0c..ddb21610 100644 --- a/src/WireMock.Net/RequestBuilders/IParamsRequestBuilder.cs +++ b/src/WireMock.Net/RequestBuilders/IParamsRequestBuilder.cs @@ -27,6 +27,14 @@ namespace WireMock.RequestBuilders /// The . IRequestBuilder WithParam([NotNull] string key, [CanBeNull] params string[] values); + /// + /// WithParam: matching on key and matchers. + /// + /// The key. + /// The matchers. + /// The . + IRequestBuilder WithParam([NotNull] string key, [CanBeNull] params IStringMatcher[] matchers); + /// /// WithParam: matching on key, values and matchBehaviour. /// @@ -36,6 +44,15 @@ namespace WireMock.RequestBuilders /// The . IRequestBuilder WithParam([NotNull] string key, MatchBehaviour matchBehaviour, [CanBeNull] params string[] values); + /// + /// WithParam: matching on key, matchers and matchBehaviour. + /// + /// The key. + /// The matchers. + /// The match behaviour. + /// The . + IRequestBuilder WithParam([NotNull] string key, MatchBehaviour matchBehaviour, [CanBeNull] params IStringMatcher[] matchers); + /// /// WithParam: matching on functions. /// diff --git a/src/WireMock.Net/RequestBuilders/Request.cs b/src/WireMock.Net/RequestBuilders/Request.cs index 2cb36900..787c2179 100644 --- a/src/WireMock.Net/RequestBuilders/Request.cs +++ b/src/WireMock.Net/RequestBuilders/Request.cs @@ -306,6 +306,12 @@ namespace WireMock.RequestBuilders return WithParam(key, MatchBehaviour.AcceptOnMatch, values); } + /// + public IRequestBuilder WithParam(string key, params IStringMatcher[] matchers) + { + return WithParam(key, MatchBehaviour.AcceptOnMatch, matchers); + } + /// public IRequestBuilder WithParam(string key, MatchBehaviour matchBehaviour, params string[] values) { @@ -315,6 +321,15 @@ namespace WireMock.RequestBuilders return this; } + /// + public IRequestBuilder WithParam(string key, MatchBehaviour matchBehaviour, params IStringMatcher[] matchers) + { + Check.NotNull(key, nameof(key)); + + _requestMatchers.Add(new RequestMessageParamMatcher(matchBehaviour, key, matchers)); + return this; + } + /// public IRequestBuilder WithParam(params Func>, bool>[] funcs) { diff --git a/src/WireMock.Net/Serialization/MappingConverter.cs b/src/WireMock.Net/Serialization/MappingConverter.cs index 26c4b948..6f7d6c9b 100644 --- a/src/WireMock.Net/Serialization/MappingConverter.cs +++ b/src/WireMock.Net/Serialization/MappingConverter.cs @@ -71,7 +71,7 @@ namespace WireMock.Serialization Params = paramsMatchers != null && paramsMatchers.Any() ? paramsMatchers.Select(pm => new ParamModel { Name = pm.Key, - Values = pm.Values?.ToList() + Matchers = MatcherMapper.Map(pm.Matchers) //Funcs = Map(pm.Funcs) }).ToList() : null, diff --git a/src/WireMock.Net/Server/FluentMockServer.Admin.cs b/src/WireMock.Net/Server/FluentMockServer.Admin.cs index 1401bf9b..13aecb41 100644 --- a/src/WireMock.Net/Server/FluentMockServer.Admin.cs +++ b/src/WireMock.Net/Server/FluentMockServer.Admin.cs @@ -618,9 +618,9 @@ namespace WireMock.Server if (requestModel.Params != null) { - foreach (var paramModel in requestModel.Params) + foreach (var paramModel in requestModel.Params.Where(c => c.Matchers != null)) { - requestBuilder = paramModel.Values == null ? requestBuilder.WithParam(paramModel.Name) : requestBuilder.WithParam(paramModel.Name, paramModel.Values.ToArray()); + requestBuilder = requestBuilder.WithParam(paramModel.Name, paramModel.Matchers.Select(MatcherMapper.Map).Cast().ToArray()); } } diff --git a/test/WireMock.Net.Tests/RequestMatchers/RequestMessageParamMatcherTests.cs b/test/WireMock.Net.Tests/RequestMatchers/RequestMessageParamMatcherTests.cs index fa119662..e464bc8b 100644 --- a/test/WireMock.Net.Tests/RequestMatchers/RequestMessageParamMatcherTests.cs +++ b/test/WireMock.Net.Tests/RequestMatchers/RequestMessageParamMatcherTests.cs @@ -9,7 +9,7 @@ namespace WireMock.Net.Tests.RequestMatchers public class RequestMessageParamMatcherTests { [Fact] - public void RequestMessageParamMatcher_GetMatchingScore_AllMatch() + public void RequestMessageParamMatcher_GetMatchingScore_KeyWithValuesPresentInUrl_MatchExactOnStringValues() { // Assign var requestMessage = new RequestMessage(new Uri("http://localhost?key=test1,test2"), "GET", "127.0.0.1"); @@ -24,7 +24,22 @@ namespace WireMock.Net.Tests.RequestMatchers } [Fact] - public void RequestMessageParamMatcher_GetMatchingScore_PartialMatch() + public void RequestMessageParamMatcher_GetMatchingScore_KeyWithValuesPresentInUrl_MatchExactOnExactMatchers() + { + // Assign + var requestMessage = new RequestMessage(new Uri("http://localhost?key=test1,test2"), "GET", "127.0.0.1"); + var matcher = new RequestMessageParamMatcher(MatchBehaviour.AcceptOnMatch, "key", new IStringMatcher[] { new ExactMatcher("test1"), new ExactMatcher("test2") }); + + // Act + var result = new RequestMatchResult(); + double score = matcher.GetMatchingScore(requestMessage, result); + + // Assert + Check.That(score).IsEqualTo(1.0d); + } + + [Fact] + public void RequestMessageParamMatcher_GetMatchingScore_KeyWithValuesPresentInUrl_MatchOnKeyWithValues_PartialMatch() { // Assign var requestMessage = new RequestMessage(new Uri("http://localhost?key=test0,test2"), "GET", "127.0.0.1"); @@ -39,7 +54,7 @@ namespace WireMock.Net.Tests.RequestMatchers } [Fact] - public void RequestMessageParamMatcher_GetMatchingScore_OnlyKeyPresent() + public void RequestMessageParamMatcher_GetMatchingScore_OnlyKeyPresentInUrl_MatchOnKeyWithValues_Fails() { // Assign var requestMessage = new RequestMessage(new Uri("http://localhost?key"), "GET", "127.0.0.1"); @@ -54,7 +69,7 @@ namespace WireMock.Net.Tests.RequestMatchers } [Fact] - public void RequestMessageParamMatcher_GetMatchingScore_OnlyKeyPresent_WithNull() + public void RequestMessageParamMatcher_GetMatchingScore_OnlyKeyPresentInUrl_MatchOnKey() { // Assign var requestMessage = new RequestMessage(new Uri("http://localhost?key"), "GET", "127.0.0.1"); @@ -69,7 +84,7 @@ namespace WireMock.Net.Tests.RequestMatchers } [Fact] - public void RequestMessageParamMatcher_GetMatchingScore_OnlyKeyPresent_WithEmptyArray() + public void RequestMessageParamMatcher_GetMatchingScore_OnlyKeyPresentInUrl_MatchOnKeyWithEmptyArray() { // Assign var requestMessage = new RequestMessage(new Uri("http://localhost?key"), "GET", "127.0.0.1"); @@ -82,5 +97,20 @@ namespace WireMock.Net.Tests.RequestMatchers // Assert Check.That(score).IsEqualTo(1.0d); } + + [Fact] + public void RequestMessageParamMatcher_GetMatchingScore_KeyWithValuePresentInUrl_MatchOnKey() + { + // Assign + var requestMessage = new RequestMessage(new Uri("http://localhost?key=frank@contoso.com"), "GET", "127.0.0.1"); + var matcher = new RequestMessageParamMatcher(MatchBehaviour.AcceptOnMatch, "key"); + + // Act + var result = new RequestMatchResult(); + double score = matcher.GetMatchingScore(requestMessage, result); + + // Assert + Check.That(score).IsEqualTo(1.0d); + } } } \ No newline at end of file