From f771f00fbbeefdbf684baf587ae000dc25bae666 Mon Sep 17 00:00:00 2001 From: Marcin Grzejszczak Date: Tue, 10 Apr 2018 16:47:46 +0200 Subject: [PATCH] Invalid equality check of messaging headers in input / output scenario fixes gh-609 --- .../StubRunnerStreamMessageSelector.java | 43 +++++++++++++++---- ...StubRunnerStreamMessageSelectorSpec.groovy | 11 +++-- 2 files changed, 42 insertions(+), 12 deletions(-) diff --git a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/messaging/stream/StubRunnerStreamMessageSelector.java b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/messaging/stream/StubRunnerStreamMessageSelector.java index f28b34cb8a..90541d2353 100644 --- a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/messaging/stream/StubRunnerStreamMessageSelector.java +++ b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/messaging/stream/StubRunnerStreamMessageSelector.java @@ -16,17 +16,21 @@ package org.springframework.cloud.contract.stubrunner.messaging.stream; +import java.util.ArrayList; +import java.util.List; import java.util.Map; import java.util.regex.Pattern; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; import org.springframework.cloud.contract.spec.Contract; import org.springframework.cloud.contract.spec.internal.BodyMatcher; import org.springframework.cloud.contract.spec.internal.BodyMatchers; import org.springframework.cloud.contract.spec.internal.Header; -import org.springframework.cloud.contract.verifier.util.MapConverter; import org.springframework.cloud.contract.verifier.messaging.internal.ContractVerifierObjectMapper; import org.springframework.cloud.contract.verifier.util.JsonPaths; import org.springframework.cloud.contract.verifier.util.JsonToJsonPathsConverter; +import org.springframework.cloud.contract.verifier.util.MapConverter; import org.springframework.cloud.contract.verifier.util.MethodBufferingJsonVerifiable; import org.springframework.integration.core.MessageSelector; import org.springframework.messaging.Message; @@ -43,6 +47,8 @@ import com.toomuchcoding.jsonassert.JsonAssertion; */ class StubRunnerStreamMessageSelector implements MessageSelector { + private static final Log log = LogFactory.getLog(StubRunnerStreamMessageSelector.class); + private final Contract groovyDsl; private final ContractVerifierObjectMapper objectMapper = new ContractVerifierObjectMapper(); @@ -52,7 +58,11 @@ class StubRunnerStreamMessageSelector implements MessageSelector { @Override public boolean accept(Message message) { - if (!headersMatch(message)) { + List unmatchedHeaders = headersMatch(message); + if (!unmatchedHeaders.isEmpty()) { + if (log.isDebugEnabled()) { + log.debug("Contract [" + this.groovyDsl + "] hasn't matched the following headers " + unmatchedHeaders); + } return false; } Object inputMessage = message.getPayload(); @@ -70,29 +80,37 @@ class StubRunnerStreamMessageSelector implements MessageSelector { catch (JsonProcessingException e) { throw new IllegalStateException("Cannot serialize to JSON", e); } + List unmatchedJsonPath = new ArrayList<>(); boolean matches = true; for (MethodBufferingJsonVerifiable path : jsonPaths) { - matches &= matchesJsonPath(parsedJson, path.jsonPath()); + matches &= matchesJsonPath(unmatchedJsonPath, parsedJson, path.jsonPath()); } if (matchers != null && matchers.hasMatchers()) { for (BodyMatcher matcher : matchers.jsonPathMatchers()) { String jsonPath = JsonToJsonPathsConverter.convertJsonPathAndRegexToAJsonPath(matcher, dslBody); - matches &= matchesJsonPath(parsedJson, jsonPath); + matches &= matchesJsonPath(unmatchedJsonPath, parsedJson, jsonPath); + } + } + if (!unmatchedJsonPath.isEmpty()) { + if (log.isDebugEnabled()) { + log.debug("Contract [" + this.groovyDsl + "] didn't much the body due to " + unmatchedJsonPath); } } return matches; } - private boolean matchesJsonPath(DocumentContext parsedJson, String jsonPath) { + private boolean matchesJsonPath(List unmatchedJsonPath, DocumentContext parsedJson, String jsonPath) { try { JsonAssertion.assertThat(parsedJson).matchesJsonPath(jsonPath); return true; } catch (Exception e) { + unmatchedJsonPath.add(e.getLocalizedMessage()); return false; } } - private boolean headersMatch(Message message) { + private List headersMatch(Message message) { + List unmatchedHeaders = new ArrayList<>(); Map headers = message.getHeaders(); for (Header it : this.groovyDsl.getInput().getMessageHeaders().getEntries()) { String name = it.getName(); @@ -103,12 +121,19 @@ class StubRunnerStreamMessageSelector implements MessageSelector { Pattern pattern = (Pattern) value; matches = pattern.matcher(valueInHeader.toString()).matches(); } else { - matches = valueInHeader!=null && valueInHeader.equals(value); + matches = valueInHeader != null && valueInHeader.toString().equals(value.toString()); } if (!matches) { - return false; + unmatchedHeaders.add("Header with name [" + name + "] was supposed to " + + unmatchedText(value) + " but the value is [" + (valueInHeader != null ? + valueInHeader.toString() : "null") + "]"); } } - return true; + return unmatchedHeaders; + } + + private String unmatchedText(Object expectedValue) { + return expectedValue instanceof Pattern ? "match pattern [" + ((Pattern) expectedValue).pattern() + "]" : + "be equal to [" + expectedValue + "]"; } } diff --git a/spring-cloud-contract-stub-runner/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/stream/StubRunnerStreamMessageSelectorSpec.groovy b/spring-cloud-contract-stub-runner/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/stream/StubRunnerStreamMessageSelectorSpec.groovy index 3f85df542f..1ea831526d 100644 --- a/spring-cloud-contract-stub-runner/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/stream/StubRunnerStreamMessageSelectorSpec.groovy +++ b/spring-cloud-contract-stub-runner/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/stream/StubRunnerStreamMessageSelectorSpec.groovy @@ -1,9 +1,9 @@ package org.springframework.cloud.contract.stubrunner.messaging.stream import org.springframework.cloud.contract.spec.Contract +import org.springframework.http.MediaType import org.springframework.messaging.Message import spock.lang.Specification - /** * @author Marcin Grzejszczak */ @@ -86,15 +86,20 @@ class StubRunnerStreamMessageSelectorSpec extends Specification { messageFrom "foo" messageHeaders { header("foo", 123) + header("bar", "bar") + messagingContentType(applicationJsonUtf8()) + header("regex", regex("234")) } messageBody(foo: $(c(regex("[0-9]{3}")), p(123))) - } } and: StubRunnerStreamMessageSelector predicate = new StubRunnerStreamMessageSelector(dsl) message.headers >> [ - foo: 123 + foo: 123, + bar: "bar", + contentType: MediaType.APPLICATION_JSON_UTF8, + regex: 234 ] message.payload >> [ foo: 123