From a2444550d546f9f5663074d280475e72ed8cdf1d Mon Sep 17 00:00:00 2001 From: Marcin Grzejszczak Date: Wed, 26 Apr 2023 14:03:18 +0200 Subject: [PATCH] Does not do JSON assertions for byte payloads without this change we're doing json assertions for payload that should read payloads as bytes this is a workaround around the issues like https://github.com/spring-cloud/spring-cloud-contract/issues/1701 and https://github.com/spring-cloud/spring-cloud-contract/issues/1627 . The problem is that due to the fact that we accept GString payloads where you can have complex json structures with consumer / producer parts, we are doing recursive analysis of that json structure. We're parsing the keys and values and then we're creating the final Json payload map. The issue is that sometimes the value is actually textual json which we shouldn't parse. We don't know that when the processing happens. The workaround is about telling the framework to read the payload from a file as bytes and then simple equality check will happen without additional JSON assertions. We will need to look into some better fixes for that in the future (e.g. 5.0.0). fixes gh-1627 fixes gh-1701 --- .../verifier/builder/GenericJsonBodyThen.java | 5 + .../MessagingMethodBodyBuilderSpec.groovy | 113 ++++++++++++++++++ .../verifier/builder/messageResponse.json | 3 + .../src/test/resources/messageResponse.json | 3 + 4 files changed, 124 insertions(+) create mode 100644 spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/builder/messageResponse.json create mode 100644 spring-cloud-contract-verifier/src/test/resources/messageResponse.json diff --git a/spring-cloud-contract-verifier/src/main/java/org/springframework/cloud/contract/verifier/builder/GenericJsonBodyThen.java b/spring-cloud-contract-verifier/src/main/java/org/springframework/cloud/contract/verifier/builder/GenericJsonBodyThen.java index 669780a57d..435e0be37a 100644 --- a/spring-cloud-contract-verifier/src/main/java/org/springframework/cloud/contract/verifier/builder/GenericJsonBodyThen.java +++ b/spring-cloud-contract-verifier/src/main/java/org/springframework/cloud/contract/verifier/builder/GenericJsonBodyThen.java @@ -27,6 +27,7 @@ import org.apache.commons.text.StringEscapeUtils; import org.springframework.cloud.contract.spec.ContractTemplate; import org.springframework.cloud.contract.spec.internal.BodyMatchers; import org.springframework.cloud.contract.spec.internal.ExecutionProperty; +import org.springframework.cloud.contract.spec.internal.FromFileProperty; import org.springframework.cloud.contract.verifier.config.TestFramework; import org.springframework.cloud.contract.verifier.file.SingleContractMetadata; import org.springframework.cloud.contract.verifier.template.HandlebarsTemplateProcessor; @@ -195,6 +196,10 @@ class GenericJsonBodyThen implements Then { @Override public boolean accept(SingleContractMetadata metadata) { + Object responseBody = this.bodyParser.responseBody(metadata).getServerValue(); + if (responseBody instanceof FromFileProperty) { + return !((FromFileProperty) responseBody).isByte(); + } ContentType outputTestContentType = metadata.getOutputTestContentType(); return JSON == outputTestContentType || mostLikelyJson(outputTestContentType, metadata); } diff --git a/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/builder/MessagingMethodBodyBuilderSpec.groovy b/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/builder/MessagingMethodBodyBuilderSpec.groovy index 5927d5ae5d..57fd94e07a 100644 --- a/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/builder/MessagingMethodBodyBuilderSpec.groovy +++ b/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/builder/MessagingMethodBodyBuilderSpec.groovy @@ -1679,6 +1679,119 @@ public class FooTest { assertThatJson(parsedJson).field("['uid']").matches("[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}"); } +} +""" + } + + @Issue('#1701') + def "should generate output for message whose body is json and contains string that looks like json [#methodBuilderName]"() { + given: + Contract contractDsl = org.springframework.cloud.contract.spec.Contract.make { + name "foo" + description 'issue #650' + label 'trigger' + input { + triggeredBy('toString()') + } + outputMessage { + sentTo("foo") + headers { + messagingContentType(applicationJson()) + } + body(fileAsBytes('messageResponse.json')) + } + } + methodBuilder() + when: + String test = singleTestGenerator(contractDsl) + then: + !test.contains('cursor') + !test.contains('REGEXP>>') + test.trim() == expectedTest.trim() + where: + methodBuilderName | methodBuilder | expectedTest + "spock" | { properties.testFramework = TestFramework.SPOCK } | """\ +package com.example + +import com.jayway.jsonpath.DocumentContext +import com.jayway.jsonpath.JsonPath +import spock.lang.Specification +import javax.inject.Inject +import org.springframework.cloud.contract.verifier.messaging.internal.ContractVerifierObjectMapper +import org.springframework.cloud.contract.verifier.messaging.internal.ContractVerifierMessage +import org.springframework.cloud.contract.verifier.messaging.internal.ContractVerifierMessaging + +import static org.springframework.cloud.contract.verifier.assertion.SpringCloudContractAssertions.assertThat +import static org.springframework.cloud.contract.verifier.util.ContractVerifierUtil.* +import static com.toomuchcoding.jsonassert.JsonAssertion.assertThatJson +import static org.springframework.cloud.contract.verifier.messaging.util.ContractVerifierMessagingUtil.headers +import static org.springframework.cloud.contract.verifier.util.ContractVerifierUtil.fileToBytes + +@SuppressWarnings("rawtypes") +class FooSpec extends Specification { + @Inject ContractVerifierMessaging contractVerifierMessaging + @Inject ContractVerifierObjectMapper contractVerifierObjectMapper + + def validate_foo() throws Exception { + when: + toString() + + then: + ContractVerifierMessage response = contractVerifierMessaging.receive("foo", + contract(this, "foo.yml")) + response != null + + and: + response.getHeader("contentType") != null + response.getHeader("contentType").toString() == 'application/json' + + and: + response.getPayloadAsByteArray() == fileToBytes(this, "foo_response_messageResponse.json") + } + +} +""" + "junit" | { properties.testFramework = TestFramework.JUNIT } | """\ +package com.example; + +import com.jayway.jsonpath.DocumentContext; +import com.jayway.jsonpath.JsonPath; +import org.junit.Test; +import org.junit.Rule; +import javax.inject.Inject; +import org.springframework.cloud.contract.verifier.messaging.internal.ContractVerifierObjectMapper; +import org.springframework.cloud.contract.verifier.messaging.internal.ContractVerifierMessage; +import org.springframework.cloud.contract.verifier.messaging.internal.ContractVerifierMessaging; + +import static org.springframework.cloud.contract.verifier.assertion.SpringCloudContractAssertions.assertThat; +import static org.springframework.cloud.contract.verifier.util.ContractVerifierUtil.*; +import static com.toomuchcoding.jsonassert.JsonAssertion.assertThatJson; +import static org.springframework.cloud.contract.verifier.messaging.util.ContractVerifierMessagingUtil.headers; +import static org.springframework.cloud.contract.verifier.util.ContractVerifierUtil.fileToBytes; + +@SuppressWarnings("rawtypes") +public class FooTest { + @Inject ContractVerifierMessaging contractVerifierMessaging; + @Inject ContractVerifierObjectMapper contractVerifierObjectMapper; + + @Test + public void validate_foo() throws Exception { + // when: + toString(); + + // then: + ContractVerifierMessage response = contractVerifierMessaging.receive("foo", + contract(this, "foo.yml")); + assertThat(response).isNotNull(); + + // and: + assertThat(response.getHeader("contentType")).isNotNull(); + assertThat(response.getHeader("contentType").toString()).isEqualTo("application/json"); + + // and: + assertThat(response.getPayloadAsByteArray()).isEqualTo(fileToBytes(this, "foo_response_messageResponse.json")); + } + } """ } diff --git a/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/builder/messageResponse.json b/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/builder/messageResponse.json new file mode 100644 index 0000000000..f9504caa62 --- /dev/null +++ b/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/builder/messageResponse.json @@ -0,0 +1,3 @@ +{ + "payload": "{\"propsA\":\"valueA\",\"propsB\":\"valueB\"}" +} diff --git a/spring-cloud-contract-verifier/src/test/resources/messageResponse.json b/spring-cloud-contract-verifier/src/test/resources/messageResponse.json new file mode 100644 index 0000000000..f9504caa62 --- /dev/null +++ b/spring-cloud-contract-verifier/src/test/resources/messageResponse.json @@ -0,0 +1,3 @@ +{ + "payload": "{\"propsA\":\"valueA\",\"propsB\":\"valueB\"}" +}