diff --git a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/MethodBodyBuilder.groovy b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/MethodBodyBuilder.groovy index af7fbd0279..745920bef0 100644 --- a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/MethodBodyBuilder.groovy +++ b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/MethodBodyBuilder.groovy @@ -23,8 +23,6 @@ import groovy.json.JsonOutput import groovy.transform.PackageScope import groovy.transform.TypeChecked import org.apache.commons.lang3.StringEscapeUtils -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.ContractTemplate import org.springframework.cloud.contract.spec.internal.BodyMatcher @@ -47,7 +45,6 @@ import org.springframework.cloud.contract.verifier.util.MapConverter import org.springframework.util.SerializationUtils import org.springframework.util.StringUtils -import java.lang.invoke.MethodHandles import java.util.regex.Pattern import static org.springframework.cloud.contract.verifier.util.ContentUtils.extractValue @@ -64,7 +61,7 @@ import static org.springframework.cloud.contract.verifier.util.ContentUtils.extr @PackageScope abstract class MethodBodyBuilder { - private static final Log log = LogFactory.getLog(MethodHandles.lookup().lookupClass()) + private static final Closure GET_SERVER_VALUE = { it instanceof DslProperty ? it.serverValue : it } protected final ContractVerifierConfigProperties configProperties protected final TemplateProcessor templateProcessor @@ -602,9 +599,11 @@ abstract class MethodBodyBuilder { */ protected Object extractServerValueFromBody(bodyValue) { if (bodyValue instanceof GString) { - bodyValue = extractValue(bodyValue, { DslProperty dslProperty -> dslProperty.serverValue }) + bodyValue = extractValue(bodyValue, ContentType.from(MapConverter.getTestSideValues(this.contract.request.headers?.entries?.find { + it.name.toLowerCase() == "Content-Type".toLowerCase() + }).toString()), GET_SERVER_VALUE) } else { - bodyValue = MapConverter.transformValues(bodyValue, { it instanceof DslProperty ? it.serverValue : it }) + bodyValue = MapConverter.transformValues(bodyValue, GET_SERVER_VALUE) } return bodyValue } diff --git a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/util/ContentType.groovy b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/util/ContentType.groovy index af2212fe59..13ec29fe00 100644 --- a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/util/ContentType.groovy +++ b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/util/ContentType.groovy @@ -17,7 +17,7 @@ package org.springframework.cloud.contract.verifier.util /** - * Represents content type + * Represents content type. Used to pick the way bodies are parsed. * * @since 1.0.0 */ @@ -34,4 +34,19 @@ enum ContentType { this.mimeType = mimeType } + static ContentType from(String header) { + try { + if (header.contains("json")) { + return JSON + } else if (header.contains("xml")) { + return XML + } else if (header.contains("text") || + header.contains("application/x-www-form-urlencoded")) { + // we want both to be treated as text + return TEXT + } + } catch(e) {} + return UNKNOWN + } + } \ No newline at end of file diff --git a/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/builder/MethodBodyBuilderSpec.groovy b/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/builder/MethodBodyBuilderSpec.groovy index fe4941a834..84f9434d63 100644 --- a/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/builder/MethodBodyBuilderSpec.groovy +++ b/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/builder/MethodBodyBuilderSpec.groovy @@ -349,7 +349,7 @@ DocumentContext parsedJson = JsonPath.parse(json); } @Issue("#458") - def "should reference request from body whtn body is a string [#methodBuilderName]"() { + def "should reference request from body when body is a string [#methodBuilderName]"() { given: Contract contractDsl = Contract.make { request { @@ -367,15 +367,17 @@ DocumentContext parsedJson = JsonPath.parse(json); when: builder.appendTo(blockBuilder) then: - SyntaxChecker.tryToCompileWithoutCompileStatic(methodBuilderName, blockBuilder.toString()) + String test = blockBuilder.toString() + SyntaxChecker.tryToCompileWithoutCompileStatic(methodBuilderName, test) + responseAsserter(test) and: stubMappingIsValidWireMockStub(contractDsl) where: methodBuilderName | methodBuilder | responseAsserter - "MockMvcSpockMethodBuilder" | { Contract dsl -> new MockMvcSpockMethodRequestProcessingBodyBuilder(dsl, properties) } | { String string -> assert string.contains('responseBody == "My name"') } - "MockMvcJUnitMethodBuilder" | { Contract dsl -> new MockMvcJUnitMethodBodyBuilder(dsl, properties) } | { String string -> assert string.contains('assertThat(responseBody).isEqualTo("My name");') } - "JaxRsClientSpockMethodRequestProcessingBodyBuilder" | { Contract dsl -> new JaxRsClientSpockMethodRequestProcessingBodyBuilder(dsl, properties) } | { String string -> assert string.contains('responseBody == "My name"') } - "JaxRsClientJUnitMethodBodyBuilder" | { Contract dsl -> new JaxRsClientJUnitMethodBodyBuilder(dsl, properties) } | { String string -> assert string.contains('assertThat(responseBody).isEqualTo("My name");') } + "MockMvcSpockMethodBuilder" | { Contract dsl -> new MockMvcSpockMethodRequestProcessingBodyBuilder(dsl, properties) } | { String string -> string.contains('responseBody == "My name"') } + "MockMvcJUnitMethodBuilder" | { Contract dsl -> new MockMvcJUnitMethodBodyBuilder(dsl, properties) } | { String string -> string.contains('assertThat(responseBody).isEqualTo("My name");') } + "JaxRsClientSpockMethodRequestProcessingBodyBuilder" | { Contract dsl -> new JaxRsClientSpockMethodRequestProcessingBodyBuilder(dsl, properties) } | { String string -> string.contains('responseBody == "My name"') } + "JaxRsClientJUnitMethodBodyBuilder" | { Contract dsl -> new JaxRsClientJUnitMethodBodyBuilder(dsl, properties) } | { String string -> string.contains('assertThat(responseBody).isEqualTo("My name");') } } def "should use fixed delay milliseconds in the generated test [#methodBuilderName]"() { @@ -408,4 +410,38 @@ DocumentContext parsedJson = JsonPath.parse(json); "MockMvcJUnitMethodBuilder" | { Contract dsl -> new MockMvcJUnitMethodBodyBuilder(dsl, properties) } } + @Issue("#493") + def "should not escape a form URL encoded request body [#methodBuilderName]"() { + given: + Contract contractDsl = Contract.make { + request { + method 'POST' + url '/api/form-endpoint' + headers { + header("Content-Type": 'application/x-www-form-urlencoded') + } + body('a=abc&b=123') + } + response { + status 200 + } + } + MethodBodyBuilder builder = methodBuilder(contractDsl) + BlockBuilder blockBuilder = new BlockBuilder(" ") + when: + builder.appendTo(blockBuilder) + then: + String test = blockBuilder.toString() + SyntaxChecker.tryToCompileWithoutCompileStatic(methodBuilderName, test) + !test.contains("a=abc&b=123") + and: + stubMappingIsValidWireMockStub(contractDsl) + where: + methodBuilderName | methodBuilder + "MockMvcSpockMethodBuilder" | { Contract dsl -> new MockMvcSpockMethodRequestProcessingBodyBuilder(dsl, properties) } + "MockMvcJUnitMethodBuilder" | { Contract dsl -> new MockMvcJUnitMethodBodyBuilder(dsl, properties) } + "JaxRsClientSpockMethodRequestProcessingBodyBuilder" | { Contract dsl -> new JaxRsClientSpockMethodRequestProcessingBodyBuilder(dsl, properties) } + "JaxRsClientJUnitMethodBodyBuilder" | { Contract dsl -> new JaxRsClientJUnitMethodBodyBuilder(dsl, properties) } + } + }