From 9f17948e9faf24aba17921825c1e03342bdd8e6c Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Fri, 31 Aug 2018 20:41:42 +0200 Subject: [PATCH] Add LinqMatcher (#195) * LinqMatcher * LinqMatcher : revert * LinqMatcher --- .../MainApp.cs | 8 ++ src/WireMock.Net/Matchers/LinqMatcher.cs | 99 ++++++++++++++++ .../Serialization/MatcherMapper.cs | 3 + src/WireMock.Net/WireMock.Net.csproj | 1 + test/WireMock.Net.Tests/MatcherMapperTests.cs | 97 +++++++++++++++ .../Matchers/LinqMatcherTests.cs | 112 ++++++++++++++++++ 6 files changed, 320 insertions(+) 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..b741c5f98 --- /dev/null +++ b/src/WireMock.Net/Matchers/LinqMatcher.cs @@ -0,0 +1,99 @@ +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 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))); + + 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() + { + return _patterns; + } + + /// + public string Name => "LinqMatcher"; + } +} diff --git a/src/WireMock.Net/Serialization/MatcherMapper.cs b/src/WireMock.Net/Serialization/MatcherMapper.cs index 8d513718b..53aabf27f 100644 --- a/src/WireMock.Net/Serialization/MatcherMapper.cs +++ b/src/WireMock.Net/Serialization/MatcherMapper.cs @@ -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); 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..429c310a8 --- /dev/null +++ b/test/WireMock.Net.Tests/Matchers/LinqMatcherTests.cs @@ -0,0 +1,112 @@ +using NFluent; +using WireMock.Matchers; +using Xunit; + +namespace WireMock.Net.Tests.Matchers +{ + public class LinqMatcherTests + { + [Fact] + public void LinqMatcher_For_String_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_For_String_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_For_String_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_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() + { + // 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