From 8adf34fb560fa8c6b7563caa9c88d75bda0b85a5 Mon Sep 17 00:00:00 2001 From: DayLightDancer Date: Thu, 17 Aug 2023 18:18:32 +0300 Subject: [PATCH] Update JSONPathMatcher.cs to cover the string path selection to a child (#993) * Update JSONPathMatcher.cs to cover the string path selection to a child The .SelectToken method accept string path selection and JSONPath queries. The current code works only for the queries because the result is JObject. When the string path is selected the result is JValue and event with a valid result the code the code doesn't return valued result. https://www.newtonsoft.com/json/help/html/SelectToken.htm * Added unit tests * Addressed the comments * Addressed the comments * Update JSONPathMatcher.cs --- src/WireMock.Net/Matchers/JSONPathMatcher.cs | 9 +- .../Matchers/JsonPathMatcherTests.cs | 126 +++++++++++++++++- 2 files changed, 132 insertions(+), 3 deletions(-) diff --git a/src/WireMock.Net/Matchers/JSONPathMatcher.cs b/src/WireMock.Net/Matchers/JSONPathMatcher.cs index fda66f49..94d76cca 100644 --- a/src/WireMock.Net/Matchers/JSONPathMatcher.cs +++ b/src/WireMock.Net/Matchers/JSONPathMatcher.cs @@ -123,7 +123,12 @@ public class JsonPathMatcher : IStringMatcher, IObjectMatcher { var array = ConvertJTokenToJArrayIfNeeded(jToken); - return MatchScores.ToScore(_patterns.Select(pattern => array.SelectToken(pattern.GetPattern())?.Any() == true).ToArray(), MatchOperator); + // The SelectToken method can accept a string path to a child token ( i.e. "Manufacturers[0].Products[0].Price"). In that case it will return a JValue (some type) which does not implement the IEnumerable interface. + // Using ?.Any() == true relays that we use a JSONPath queries and the SelectToken will return a JObject ( implements the IEnumerable interface). + // So the current code works only when the JSONPath is expression and not when we pass a valid string path to child. + // My suggestion is to roll back to != null to cover the both cases. + + return MatchScores.ToScore(_patterns.Select(pattern => array.SelectToken(pattern.GetPattern()) != null ).ToArray(), MatchOperator); } // https://github.com/WireMock-Net/WireMock.Net/issues/965 @@ -149,4 +154,4 @@ public class JsonPathMatcher : IStringMatcher, IObjectMatcher return jToken; } -} \ No newline at end of file +} diff --git a/test/WireMock.Net.Tests/Matchers/JsonPathMatcherTests.cs b/test/WireMock.Net.Tests/Matchers/JsonPathMatcherTests.cs index d0a8e46c..33a21a28 100644 --- a/test/WireMock.Net.Tests/Matchers/JsonPathMatcherTests.cs +++ b/test/WireMock.Net.Tests/Matchers/JsonPathMatcherTests.cs @@ -200,4 +200,128 @@ public class JsonPathMatcherTests // Assert Check.That(match).IsEqualTo(0.0); } -} \ No newline at end of file + + [Fact] + public void JsonPathMatcher_IsMatch_ArrayOneLevel() + { + // Arrange + var matcher = new JsonPathMatcher("$.arr[0].line1"); + + // Act + double match = matcher.IsMatch(JObject.Parse(@"{ + ""name"": ""PathSelectorTest"", + ""test"": ""test"", + ""test2"": ""test2"", + ""arr"": [{ + ""line1"": ""line1"", + }] + }")); + + // Assert + Check.That(match).IsEqualTo(1.0); + } + + [Fact] + public void JsonPathMatcher_IsMatch_ObjectMatch() + { + // Arrange + var matcher = new JsonPathMatcher("$.test"); + + // Act + double match = matcher.IsMatch(JObject.Parse(@"{ + ""name"": ""PathSelectorTest"", + ""test"": ""test"", + ""test2"": ""test2"", + ""arr"": [ + { + ""line1"": ""line1"", + } + ] + }")); + + // Assert + Check.That(match).IsEqualTo(1.0); + } + + [Fact] + public void JsonPathMatcher_IsMatch_DoesntMatch() + { + // Arrange + var matcher = new JsonPathMatcher("$.test3"); + + // Act + double match = matcher.IsMatch(JObject.Parse(@"{ + ""name"": ""PathSelectorTest"", + ""test"": ""test"", + ""test2"": ""test2"", + ""arr"": [ + { + ""line1"": ""line1"", + } + ] + }")); + + // Assert + Check.That(match).IsEqualTo(0.0); + } + + [Fact] + public void JsonPathMatcher_IsMatch_DoesntMatchInArray() + { + // Arrange + var matcher = new JsonPathMatcher("$arr[0].line1"); + + // Act + double match = matcher.IsMatch(JObject.Parse(@"{ + ""name"": ""PathSelectorTest"", + ""test"": ""test"", + ""test2"": ""test2"", + ""arr"": [] + }")); + + // Assert + Check.That(match).IsEqualTo(0.0); + } + + [Fact] + public void JsonPathMatcher_IsMatch_DoesntMatchNoObjectsInArray() + { + // Arrange + var matcher = new JsonPathMatcher("$arr[2].line1"); + + // Act + double match = matcher.IsMatch(JObject.Parse(@"{ + ""name"": ""PathSelectorTest"", + ""test"": ""test"", + ""test2"": ""test2"", + ""arr"": [] + }")); + + // Assert + Check.That(match).IsEqualTo(0.0); + } + + [Fact] + public void JsonPathMatcher_IsMatch_NestedArrays() + { + // Arrange + var matcher = new JsonPathMatcher("$.arr[0].sub[0].subline1"); + + // Act + double match = matcher.IsMatch(JObject.Parse(@"{ + ""name"": ""PathSelectorTest"", + ""test"": ""test"", + ""test2"": ""test2"", + ""arr"": [{ + ""line1"": ""line1"", + ""sub"":[ + { + ""subline1"":""subline1"" + }] + }] + }")); + + // Assert + Check.That(match).IsEqualTo(1.0); + } +}