Skip to content

Commit 2350bad

Browse files
author
Tom Akehurst
committed
Fixed #1558 - sub-matchers in matchesJsonPath are now compared against each item in the list of results from the expression rather than just the string rendition except if the sub-matcher is equalToJson, in which case this is still the behaviour we want.
1 parent 2e32b1c commit 2350bad

7 files changed

Lines changed: 83 additions & 31 deletions

File tree

src/main/java/com/github/tomakehurst/wiremock/client/WireMock.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,11 @@ public static StringValuePattern matchingXPath(String value, StringValuePattern
217217
return new MatchesXPathPattern(value, valuePattern);
218218
}
219219

220+
// Use this with the date/time matchers to avoid an explicit cast
221+
public static MatchesXPathPattern matchesXPathWithSubMatcher(String value, StringValuePattern valuePattern) {
222+
return new MatchesXPathPattern(value, valuePattern);
223+
}
224+
220225
public static StringValuePattern containing(String value) {
221226
return new ContainsPattern(value);
222227
}

src/main/java/com/github/tomakehurst/wiremock/matching/MatchesJsonPathPattern.java

Lines changed: 23 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,17 @@
1818
import com.fasterxml.jackson.annotation.JsonProperty;
1919
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
2020
import com.github.tomakehurst.wiremock.common.Json;
21+
import com.github.tomakehurst.wiremock.common.ListOrSingle;
2122
import com.jayway.jsonpath.JsonPath;
2223
import com.jayway.jsonpath.PathNotFoundException;
2324

24-
import java.util.Collection;
25-
import java.util.Map;
25+
import java.util.*;
26+
import java.util.function.Function;
27+
import java.util.stream.Collectors;
2628

2729
import static com.github.tomakehurst.wiremock.common.LocalNotifier.notifier;
30+
import static java.util.Collections.singletonList;
31+
import static java.util.stream.Collectors.toList;
2832

2933
@JsonSerialize(using = JsonPathPatternJsonSerializer.class)
3034
public class MatchesJsonPathPattern extends PathPattern {
@@ -84,22 +88,25 @@ protected MatchResult isSimpleMatch(String value) {
8488

8589
protected MatchResult isAdvancedMatch(String value) {
8690
try {
87-
String expressionResult = getExpressionResult(value);
91+
ListOrSingle<String> expressionResult = getExpressionResult(value);
8892

8993
// Bit of a hack, but otherwise empty array results aren't matched as absent()
90-
if ("[ ]".equals(expressionResult) && valuePattern.getClass().isAssignableFrom(AbsentPattern.class)) {
91-
expressionResult = null;
94+
if ((expressionResult == null || expressionResult.isEmpty()) && AbsentPattern.class.isAssignableFrom(valuePattern.getClass())) {
95+
expressionResult = ListOrSingle.of((String) null);
9296
}
9397

94-
return valuePattern.match(expressionResult);
98+
return expressionResult.stream()
99+
.map(valuePattern::match)
100+
.min(Comparator.comparingDouble(MatchResult::getDistance))
101+
.orElse(MatchResult.noMatch());
95102
} catch (SubExpressionException e) {
96103
notifier().info(e.getMessage());
97104
return MatchResult.noMatch();
98105
}
99106
}
100107

101108
@Override
102-
public String getExpressionResult(final String value) {
109+
public ListOrSingle<String> getExpressionResult(final String value) {
103110
// For performance reason, don't try to parse XML value
104111
if (value != null && value.trim().startsWith("<")) {
105112
final String message = String.format(
@@ -128,13 +135,16 @@ public String getExpressionResult(final String value) {
128135
throw new SubExpressionException(message, e);
129136
}
130137

131-
String expressionResult;
132-
if (obj instanceof Number || obj instanceof String || obj instanceof Boolean) {
133-
expressionResult = String.valueOf(obj);
134-
} else if (obj instanceof Map || obj instanceof Collection) {
135-
expressionResult = Json.write(obj);
138+
ListOrSingle<String> expressionResult;
139+
if (obj instanceof Map || EqualToJsonPattern.class.isAssignableFrom(valuePattern.getClass())) {
140+
expressionResult = ListOrSingle.of(Json.write(obj));
141+
} else if (obj instanceof List) {
142+
final List<String> stringValues = ((List<?>) obj).stream().map(Object::toString).collect(toList());
143+
expressionResult = ListOrSingle.of(stringValues);
144+
} else if (obj instanceof Number || obj instanceof String || obj instanceof Boolean) {
145+
expressionResult = ListOrSingle.of(String.valueOf(obj));
136146
} else {
137-
expressionResult = null;
147+
expressionResult = ListOrSingle.of();
138148
}
139149

140150
return expressionResult;

src/main/java/com/github/tomakehurst/wiremock/matching/MatchesXPathPattern.java

Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import java.util.Comparator;
2828
import java.util.Map;
2929
import java.util.SortedSet;
30+
import java.util.stream.Collectors;
3031

3132
import static com.github.tomakehurst.wiremock.common.LocalNotifier.notifier;
3233
import static com.google.common.base.MoreObjects.firstNonNull;
@@ -95,24 +96,17 @@ protected MatchResult isAdvancedMatch(String value) {
9596
}
9697

9798
@Override
98-
public String getExpressionResult(String value) {
99+
public ListOrSingle<String> getExpressionResult(String value) {
99100
ListOrSingle<XmlNode> nodeList = findXmlNodes(value);
100101
if (nodeList == null || nodeList.size() == 0) {
101-
return null;
102-
}
103-
104-
SortedSet<Pair<XmlNode, MatchResult>> results = newTreeSet(new Comparator<Pair<XmlNode, MatchResult>>() {
105-
@Override
106-
public int compare(Pair<XmlNode, MatchResult> one, Pair<XmlNode, MatchResult> two) {
107-
return one.b.compareTo(two.b);
108-
}
109-
});
110-
111-
for (XmlNode node: nodeList) {
112-
results.add(new Pair<>(node, valuePattern.match(node.toString())));
102+
return ListOrSingle.of();
113103
}
114104

115-
return results.last().a.toString();
105+
return ListOrSingle.of(
106+
nodeList.stream()
107+
.map(XmlNode::toString)
108+
.collect(Collectors.toList())
109+
);
116110
}
117111

118112
private ListOrSingle<XmlNode> findXmlNodes(String value) {

src/main/java/com/github/tomakehurst/wiremock/matching/PathPattern.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@
1717

1818
import com.fasterxml.jackson.annotation.JsonIgnore;
1919
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
20+
import com.github.tomakehurst.wiremock.common.ListOrSingle;
2021

22+
import java.util.List;
2123
import java.util.Objects;
2224

2325
public abstract class PathPattern extends StringValuePattern {
@@ -49,7 +51,7 @@ public MatchResult match(String value) {
4951

5052
protected abstract MatchResult isSimpleMatch(String value);
5153
protected abstract MatchResult isAdvancedMatch(String value);
52-
public abstract String getExpressionResult(String value);
54+
public abstract ListOrSingle<String> getExpressionResult(String value);
5355

5456
@Override
5557
public boolean equals(Object o) {

src/main/java/com/github/tomakehurst/wiremock/verification/diff/Diff.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
package com.github.tomakehurst.wiremock.verification.diff;
1717

1818
import com.github.tomakehurst.wiremock.common.Json;
19+
import com.github.tomakehurst.wiremock.common.ListOrSingle;
1920
import com.github.tomakehurst.wiremock.common.Urls;
2021
import com.github.tomakehurst.wiremock.common.xml.Xml;
2122
import com.github.tomakehurst.wiremock.http.*;
@@ -248,13 +249,14 @@ private void addBodySection(List<ContentPattern<?>> bodyPatterns, Body body, Imm
248249
if (PathPattern.class.isAssignableFrom(pattern.getClass())) {
249250
PathPattern pathPattern = (PathPattern) pattern;
250251
if (!pathPattern.isSimple()) {
251-
String expressionResult = pathPattern.getExpressionResult(body.asString());
252+
ListOrSingle<String> expressionResult = pathPattern.getExpressionResult(body.asString());
253+
String expressionResultString = expressionResult != null && !expressionResult.isEmpty() ? expressionResult.toString() : null;
252254
String printedExpectedValue =
253255
pathPattern.getExpected() +
254256
" [" + pathPattern.getValuePattern().getName() + "] " +
255257
pathPattern.getValuePattern().getExpected();
256-
if (expressionResult != null) {
257-
builder.add(new DiffLine<>("Body", pathPattern.getValuePattern(), expressionResult, printedExpectedValue));
258+
if (expressionResultString != null) {
259+
builder.add(new DiffLine<>("Body", pathPattern.getValuePattern(), expressionResultString, printedExpectedValue));
258260
} else {
259261
builder.add(new DiffLine<>("Body", pathPattern, formattedBody, printedExpectedValue));
260262
}

src/test/java/com/github/tomakehurst/wiremock/matching/MatchesJsonPathPatternTest.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -410,6 +410,23 @@ public void treatsAnEmptyArrayExpressionResultAsAbsent() {
410410
assertTrue(result.isExactMatch());
411411
}
412412

413+
@Test
414+
public void matchesCorrectlyWhenSubMatcherIsUsedAndExpressionReturnsASingleItemArray() {
415+
String json = "{\n" +
416+
" \"searchCriteria\": {\n" +
417+
" \"customerId\": \"104903\",\n" +
418+
" \"date\": \"01/01/2021\"\n" +
419+
" }\n" +
420+
"}";
421+
422+
MatchResult result = matchingJsonPath(
423+
"$.searchCriteria[?(@.customerId == '104903')].date",
424+
equalToDateTime("2021-01-01T00:00:00").actualFormat("dd/MM/yyyy"))
425+
.match(json);
426+
427+
assertTrue(result.isExactMatch());
428+
}
429+
413430
private void expectInfoNotification(final String message) {
414431
final Notifier notifier = context.mock(Notifier.class);
415432
context.checking(new Expectations() {{

src/test/java/com/github/tomakehurst/wiremock/matching/MatchesXPathPatternTest.java

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,28 @@ public void matchesComplexElementAgainstValuePattern() {
182182
assertThat(pattern.match(xml).isExactMatch(), is(true));
183183
}
184184

185+
@Test
186+
public void matchesCorrectlyWhenSubMatcherIsDateEquality() {
187+
String xml = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" +
188+
"<soapenv:Envelope>\n" +
189+
" <soapenv:Body>\n" +
190+
" <Retrieve>\n" +
191+
" <Policy>\n" +
192+
" <EffectiveDate Val=\"01/01/2021\" />\n" +
193+
" <Policy Val=\"ABC123\" />\n" +
194+
" </Policy>\n" +
195+
" </Retrieve>\n" +
196+
" </soapenv:Body>\n" +
197+
"</soapenv:Envelope>";
198+
199+
StringValuePattern pattern = WireMock.matchesXPathWithSubMatcher(
200+
"//*[local-name() = 'EffectiveDate']/@Val",
201+
equalToDateTime("2021-01-01T00:00:00").actualFormat("dd/MM/yyyy")
202+
);
203+
204+
assertThat(pattern.match(xml).isExactMatch(), is(true));
205+
}
206+
185207
@Test
186208
public void deserialisesCorrectlyWithoutNamespaces() {
187209
String json = "{ \"matchesXPath\" : \"/stuff:outer/stuff:inner[.=111]\" }";

0 commit comments

Comments
 (0)