From 68101645788c620307be5da65d842c000d866ae7 Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Fri, 31 Aug 2018 18:23:29 +0200 Subject: [PATCH 1/3] LinqMatcher --- .../MainApp.cs | 8 ++ src/WireMock.Net/Matchers/LinqMatcher.cs | 75 ++++++++++++++ .../Serialization/MatcherMapper.cs | 7 +- src/WireMock.Net/WireMock.Net.csproj | 1 + test/WireMock.Net.Tests/MatcherMapperTests.cs | 97 +++++++++++++++++++ .../Matchers/LinqMatcherTests.cs | 74 ++++++++++++++ 6 files changed, 260 insertions(+), 2 deletions(-) create mode 100644 src/WireMock.Net/Matchers/LinqMatcher.cs create mode 100644 test/WireMock.Net.Tests/MatcherMapperTests.cs create mode 100644 test/WireMock.Net.Tests/Matchers/LinqMatcherTests.cs diff --git a/examples/WireMock.Net.Console.Net452.Classic/MainApp.cs b/examples/WireMock.Net.Console.Net452.Classic/MainApp.cs index aad6a3912..e1574a65e 100644 --- a/examples/WireMock.Net.Console.Net452.Classic/MainApp.cs +++ b/examples/WireMock.Net.Console.Net452.Classic/MainApp.cs @@ -350,6 +350,14 @@ public static void Run() .WithTransformer() ); + // https://stackoverflow.com/questions/51985089/wiremock-request-matching-with-comparison-between-two-query-parameters + server + .Given(Request.Create().WithPath("/linq") + .WithParam("from", new LinqMatcher("DateTime.Parse(it) > \"2018-03-01 00:00:00\""))) + .RespondWith(Response.Create() + .WithBody("linq match !!!") + ); + System.Console.WriteLine("Press any key to stop the server"); System.Console.ReadKey(); server.Stop(); diff --git a/src/WireMock.Net/Matchers/LinqMatcher.cs b/src/WireMock.Net/Matchers/LinqMatcher.cs new file mode 100644 index 000000000..70490b398 --- /dev/null +++ b/src/WireMock.Net/Matchers/LinqMatcher.cs @@ -0,0 +1,75 @@ +using System.Linq; +using System.Linq.Dynamic.Core; +using JetBrains.Annotations; + +namespace WireMock.Matchers +{ + /// + /// System.Linq.Dynamic.Core Expression Matcher + /// + /// + public class LinqMatcher : IStringMatcher + { + private readonly string[] _patterns; + + /// + public MatchBehaviour MatchBehaviour { get; } + + /// + /// Initializes a new instance of the class. + /// + /// The pattern. + public LinqMatcher([NotNull] string pattern) : this(new[] { pattern }) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The patterns. + public LinqMatcher([NotNull] string[] patterns) : this(MatchBehaviour.AcceptOnMatch, patterns) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The match behaviour. + /// The pattern. + public LinqMatcher(MatchBehaviour matchBehaviour, [NotNull] string pattern) : this(matchBehaviour, new[] { pattern }) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The match behaviour. + /// The patterns. + public LinqMatcher(MatchBehaviour matchBehaviour, [NotNull] string[] patterns) + { + MatchBehaviour = matchBehaviour; + _patterns = patterns; + } + + /// + public double IsMatch(string input) + { + // Convert a single input string to a Queryable list with 1 entry. + var queryable = new[] { input }.AsQueryable(); + + // Use the Any(...) method to check if the result matches + double match = MatchScores.ToScore(_patterns.Select(pattern => queryable.Any(pattern))); + + return MatchBehaviourHelper.Convert(MatchBehaviour, match); + } + + /// + public string[] GetPatterns() + { + return _patterns; + } + + /// + public string Name => "LinqMatcher"; + } +} diff --git a/src/WireMock.Net/Serialization/MatcherMapper.cs b/src/WireMock.Net/Serialization/MatcherMapper.cs index 8d513718b..16a5f08c8 100644 --- a/src/WireMock.Net/Serialization/MatcherMapper.cs +++ b/src/WireMock.Net/Serialization/MatcherMapper.cs @@ -17,7 +17,7 @@ public static IMatcher Map([CanBeNull] MatcherModel matcher) return null; } - string[] parts = matcher.Name.Split('.'); + string[] parts = matcher.Name?.Split('.'); string matcherName = parts[0]; string matcherType = parts.Length > 1 ? parts[1] : null; @@ -26,6 +26,9 @@ public static IMatcher Map([CanBeNull] MatcherModel matcher) switch (matcherName) { + case "LinqMatcher": + return new LinqMatcher(matchBehaviour, stringPatterns); + case "ExactMatcher": return new ExactMatcher(matchBehaviour, stringPatterns); @@ -54,7 +57,7 @@ public static IMatcher Map([CanBeNull] MatcherModel matcher) return new SimMetricsMatcher(matchBehaviour, (string)matcher.Pattern, type); default: - throw new NotSupportedException($"Matcher '{matcherName}' is not supported."); + return new WildcardMatcher(matchBehaviour, stringPatterns, matcher.IgnoreCase == true); } } diff --git a/src/WireMock.Net/WireMock.Net.csproj b/src/WireMock.Net/WireMock.Net.csproj index 05436bef9..c8060606a 100644 --- a/src/WireMock.Net/WireMock.Net.csproj +++ b/src/WireMock.Net/WireMock.Net.csproj @@ -50,6 +50,7 @@ + diff --git a/test/WireMock.Net.Tests/MatcherMapperTests.cs b/test/WireMock.Net.Tests/MatcherMapperTests.cs new file mode 100644 index 000000000..d18685d36 --- /dev/null +++ b/test/WireMock.Net.Tests/MatcherMapperTests.cs @@ -0,0 +1,97 @@ +using System; +using NFluent; +using WireMock.Admin.Mappings; +using WireMock.Matchers; +using WireMock.Serialization; +using Xunit; + +namespace WireMock.Net.Tests +{ + public class MatcherMapperTests + { + [Fact] + public void MatcherMapper_Map_MatcherModel_Null() + { + // Act + var result = MatcherMapper.Map((MatcherModel)null); + + // Assert + Check.That(result).IsNull(); + } + + [Fact] + public void MatcherMapper_Map_MatcherModel_Exception() + { + // Assign + var model = new MatcherModel { Name = "test" }; + + // Act and Assert + Check.ThatCode(() => MatcherMapper.Map(model)).Throws(); + } + + [Fact] + public void MatcherMapper_Map_MatcherModel_LinqMatcher_Pattern() + { + // Assign + var model = new MatcherModel + { + Name = "LinqMatcher", + Pattern = "p" + }; + + // Act + var matcher = MatcherMapper.Map(model) as LinqMatcher; + + // Assert + Check.That(matcher).IsNotNull(); + Check.That(matcher.MatchBehaviour).IsEqualTo(MatchBehaviour.AcceptOnMatch); + Check.That(matcher.GetPatterns()).ContainsExactly("p"); + } + + [Fact] + public void MatcherMapper_Map_MatcherModel_LinqMatcher_Patterns() + { + // Assign + var model = new MatcherModel + { + Name = "LinqMatcher", + Patterns = new[] { "p1", "p2" } + }; + + // Act + var matcher = MatcherMapper.Map(model) as LinqMatcher; + + // Assert + Check.That(matcher).IsNotNull(); + Check.That(matcher.MatchBehaviour).IsEqualTo(MatchBehaviour.AcceptOnMatch); + Check.That(matcher.GetPatterns()).Contains(new[] { "p1", "p2" }); + } + + [Fact] + public void MatcherMapper_Map_IMatcher_Null() + { + // Act + var result = MatcherMapper.Map((IMatcher)null); + + // Assert + Check.That(result).IsNull(); + } + + [Fact] + public void MatcherMapper_Map_IMatcher_LinqMatcher_Pattern() + { + // Assign + var matcher = new LinqMatcher(MatchBehaviour.AcceptOnMatch, "p"); + + // Act + var result = MatcherMapper.Map(matcher); + + // Assert + Check.That(result).IsNotNull(); + Check.That(result.Name).IsEqualTo("LinqMatcher"); + Check.That(result.IgnoreCase).IsNull(); + Check.That(result.Pattern).IsEqualTo("p"); + Check.That(result.Patterns).IsNull(); + } + } +} \ No newline at end of file diff --git a/test/WireMock.Net.Tests/Matchers/LinqMatcherTests.cs b/test/WireMock.Net.Tests/Matchers/LinqMatcherTests.cs new file mode 100644 index 000000000..a0df136d3 --- /dev/null +++ b/test/WireMock.Net.Tests/Matchers/LinqMatcherTests.cs @@ -0,0 +1,74 @@ +using NFluent; +using WireMock.Matchers; +using Xunit; + +namespace WireMock.Net.Tests.Matchers +{ + public class LinqMatcherTests + { + [Fact] + public void LinqMatcher_SinglePattern_IsMatch_Positive() + { + // Assign + string input = "2018-08-31 13:59:59"; + + // Act + var matcher = new LinqMatcher("DateTime.Parse(it) > \"2018-08-01 13:50:00\""); + + // Assert + Check.That(matcher.IsMatch(input)).IsEqualTo(1.0d); + } + + [Fact] + public void LinqMatcher_IsMatch_Negative() + { + // Assign + string input = "2018-08-31 13:59:59"; + + // Act + var matcher = new LinqMatcher("DateTime.Parse(it) > \"2019-01-01 00:00:00\""); + + // Assert + Check.That(matcher.IsMatch(input)).IsEqualTo(0.0d); + } + + [Fact] + public void LinqMatcher_IsMatch_RejectOnMatch() + { + // Assign + string input = "2018-08-31 13:59:59"; + + // Act + var matcher = new LinqMatcher(MatchBehaviour.RejectOnMatch, "DateTime.Parse(it) > \"2018-08-01 13:50:00\""); + + // Assert + Check.That(matcher.IsMatch(input)).IsEqualTo(0.0d); + } + + [Fact] + public void LinqMatcher_GetName() + { + // Assign + var matcher = new LinqMatcher("x"); + + // Act + string name = matcher.Name; + + // Assert + Check.That(name).Equals("LinqMatcher"); + } + + [Fact] + public void LinqMatcher_GetPatterns() + { + // Assign + var matcher = new LinqMatcher("x"); + + // Act + string[] patterns = matcher.GetPatterns(); + + // Assert + Check.That(patterns).ContainsExactly("x"); + } + } +} \ No newline at end of file From 656fbf6eca8c06be8500fcc9c81f286bc477adeb Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Fri, 31 Aug 2018 18:34:02 +0200 Subject: [PATCH 2/3] LinqMatcher : revert --- src/WireMock.Net/Serialization/MatcherMapper.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/WireMock.Net/Serialization/MatcherMapper.cs b/src/WireMock.Net/Serialization/MatcherMapper.cs index 16a5f08c8..53aabf27f 100644 --- a/src/WireMock.Net/Serialization/MatcherMapper.cs +++ b/src/WireMock.Net/Serialization/MatcherMapper.cs @@ -17,7 +17,7 @@ public static IMatcher Map([CanBeNull] MatcherModel matcher) return null; } - string[] parts = matcher.Name?.Split('.'); + string[] parts = matcher.Name.Split('.'); string matcherName = parts[0]; string matcherType = parts.Length > 1 ? parts[1] : null; @@ -57,7 +57,7 @@ public static IMatcher Map([CanBeNull] MatcherModel matcher) return new SimMetricsMatcher(matchBehaviour, (string)matcher.Pattern, type); default: - return new WildcardMatcher(matchBehaviour, stringPatterns, matcher.IgnoreCase == true); + throw new NotSupportedException($"Matcher '{matcherName}' is not supported."); } } From 000c7cd157cebee4b0054324ea7d8273d6bff0ac Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Fri, 31 Aug 2018 20:38:59 +0200 Subject: [PATCH 3/3] LinqMatcher --- src/WireMock.Net/Matchers/LinqMatcher.cs | 28 +++++++++++- .../Matchers/LinqMatcherTests.cs | 44 +++++++++++++++++-- 2 files changed, 67 insertions(+), 5 deletions(-) diff --git a/src/WireMock.Net/Matchers/LinqMatcher.cs b/src/WireMock.Net/Matchers/LinqMatcher.cs index 70490b398..b741c5f98 100644 --- a/src/WireMock.Net/Matchers/LinqMatcher.cs +++ b/src/WireMock.Net/Matchers/LinqMatcher.cs @@ -54,8 +54,8 @@ public LinqMatcher(MatchBehaviour matchBehaviour, [NotNull] string[] patterns) /// public double IsMatch(string input) { - // Convert a single input string to a Queryable list with 1 entry. - var queryable = new[] { input }.AsQueryable(); + // Convert a single input string to a Queryable string-list with 1 entry. + IQueryable queryable = new[] { input }.AsQueryable(); // Use the Any(...) method to check if the result matches double match = MatchScores.ToScore(_patterns.Select(pattern => queryable.Any(pattern))); @@ -63,6 +63,30 @@ public double IsMatch(string input) return MatchBehaviourHelper.Convert(MatchBehaviour, match); } + ///// + //public double IsMatch(object input) + //{ + // object value; + // switch (input) + // { + // case JObject valueAsJObject: + // value = valueAsJObject.ToObject(); + // break; + + // default: + // value = input; + // break; + // } + + // // Convert a single object to a Queryable object-list with 1 entry. + // IQueryable queryable = new[] { value }.AsQueryable().Select("new (it as x)"); + + // // Use the Any(...) method to check if the result matches + // double match = MatchScores.ToScore(_patterns.Select(pattern => queryable.Any(pattern))); + + // return MatchBehaviourHelper.Convert(MatchBehaviour, match); + //} + /// public string[] GetPatterns() { diff --git a/test/WireMock.Net.Tests/Matchers/LinqMatcherTests.cs b/test/WireMock.Net.Tests/Matchers/LinqMatcherTests.cs index a0df136d3..429c310a8 100644 --- a/test/WireMock.Net.Tests/Matchers/LinqMatcherTests.cs +++ b/test/WireMock.Net.Tests/Matchers/LinqMatcherTests.cs @@ -7,7 +7,7 @@ namespace WireMock.Net.Tests.Matchers public class LinqMatcherTests { [Fact] - public void LinqMatcher_SinglePattern_IsMatch_Positive() + public void LinqMatcher_For_String_SinglePattern_IsMatch_Positive() { // Assign string input = "2018-08-31 13:59:59"; @@ -20,7 +20,7 @@ public void LinqMatcher_SinglePattern_IsMatch_Positive() } [Fact] - public void LinqMatcher_IsMatch_Negative() + public void LinqMatcher_For_String_IsMatch_Negative() { // Assign string input = "2018-08-31 13:59:59"; @@ -33,7 +33,7 @@ public void LinqMatcher_IsMatch_Negative() } [Fact] - public void LinqMatcher_IsMatch_RejectOnMatch() + public void LinqMatcher_For_String_IsMatch_RejectOnMatch() { // Assign string input = "2018-08-31 13:59:59"; @@ -45,6 +45,44 @@ public void LinqMatcher_IsMatch_RejectOnMatch() Check.That(matcher.IsMatch(input)).IsEqualTo(0.0d); } + //[Fact] + //public void LinqMatcher_For_Object_IsMatch() + //{ + // // Assign + // var input = new + // { + // Id = 9, + // Name = "Test" + // }; + + // // Act + // var matcher = new LinqMatcher("Id > 1 AND Name == \"Test\""); + + // double match = matcher.IsMatch(input); + + // // Assert + // Assert.Equal(1.0, match); + //} + + //[Fact] + //public void LinqMatcher_For_JObject_IsMatch() + //{ + // // Assign + // var input = new JObject + // { + // { "Id", new JValue(9) }, + // { "Name", new JValue("Test") } + // }; + + // // Act + // var matcher = new LinqMatcher("it.Id > 1 AND it.Name == \"Test\""); + + // double match = matcher.IsMatch(input); + + // // Assert + // Assert.Equal(1.0, match); + //} + [Fact] public void LinqMatcher_GetName() {