diff --git a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/JUnitMethodBodyBuilder.groovy b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/JUnitMethodBodyBuilder.groovy index dc6651893f..4697518a4d 100644 --- a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/JUnitMethodBodyBuilder.groovy +++ b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/JUnitMethodBodyBuilder.groovy @@ -34,6 +34,7 @@ import org.springframework.cloud.contract.verifier.config.ContractVerifierConfig import org.springframework.cloud.contract.verifier.util.RegexpBuilders import static groovy.json.StringEscapeUtils.escapeJava +import static org.apache.commons.text.StringEscapeUtils.escapeJava import static org.springframework.cloud.contract.verifier.config.TestFramework.JUNIT import static org.springframework.cloud.contract.verifier.config.TestFramework.JUNIT5 import static org.springframework.cloud.contract.verifier.util.ContentUtils.getJavaMultipartFileParameterContent @@ -214,7 +215,7 @@ abstract class JUnitMethodBodyBuilder extends RequestProcessingMethodBodyBuilder } protected String createHeaderComparison(Pattern headerValue) { - return buildEscapedMatchesMethod(headerValue) + ";" + return createMatchesMethod(escapeJava(headerValue.pattern())) + ";" } protected String createBodyComparison(Pattern bodyValue) { @@ -229,7 +230,7 @@ abstract class JUnitMethodBodyBuilder extends RequestProcessingMethodBodyBuilder } protected String createCookieComparison(Pattern cookieValue) { - return buildEscapedMatchesMethod(cookieValue) + ";" + return createMatchesMethod(escapeJava(cookieValue.pattern())) + ";" } private String buildEscapedMatchesMethod(Pattern escapedValue) { diff --git a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/SpockMethodRequestProcessingBodyBuilder.groovy b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/SpockMethodRequestProcessingBodyBuilder.groovy index 683383173e..b8c4dbe54d 100644 --- a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/SpockMethodRequestProcessingBodyBuilder.groovy +++ b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/SpockMethodRequestProcessingBodyBuilder.groovy @@ -198,11 +198,11 @@ abstract class SpockMethodRequestProcessingBodyBuilder extends RequestProcessing } protected String convertHeaderComparison(Pattern headerValue) { - return patternComparison(headerValue) + return patternComparisonWithMatchesCheck(escapeJava(headerValue.pattern())) } protected String convertHeaderComparison(RegexProperty headerValue) { - return convertHeaderComparison(headerValue.pattern) + return patternComparisonWithMatchesCheck(escapeJava(headerValue.pattern.pattern())) } protected String convertCookieComparison(String cookieValue) { @@ -220,7 +220,7 @@ abstract class SpockMethodRequestProcessingBodyBuilder extends RequestProcessing } protected String convertCookieComparison(Pattern cookieValue) { - return patternComparison(cookieValue) + return patternComparisonWithMatchesCheck(convertUnicodeEscapesIfRequired(cookieValue.pattern())) } protected String patternComparison(Pattern pattern) { @@ -229,6 +229,10 @@ abstract class SpockMethodRequestProcessingBodyBuilder extends RequestProcessing protected String patternComparison(String pattern) { String converted = escapeJava(convertUnicodeEscapesIfRequired(pattern)) + return patternComparisonWithMatchesCheck(converted) + } + + private String patternComparisonWithMatchesCheck(String converted) { return "==~ java.util.regex.Pattern.compile('${converted}')" } } 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 257240a862..0e87c5131f 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 @@ -1284,4 +1284,40 @@ DocumentContext parsedJson = JsonPath.parse(json); "JaxRsClientJUnitMethodBodyBuilder" | { Contract dsl -> new JaxRsClientJUnitMethodBodyBuilder(dsl, properties, classDataForMethod) } } + @Issue("#892") + def "should not unnecessarily escape non json body [#methodBuilderName]"() { + given: + Contract contractDsl = Contract.make { + request { + method GET() + url $(consumer(regex("/api/v1/files/" + uuid())), + producer("/api/v1/files/b0683f29-741a-4178-b5c6-6e62202e3cf1")) + } + response { + status OK() + body($(consumer("some-content"), producer(regex(nonBlank())))) + headers { + header(contentLength(), $(consumer(2647691), producer(regex(positiveInt())))) + header(contentType(), $(consumer(applicationOctetStream()), producer(regex(nonBlank())))) + } + } + } + MethodBodyBuilder builder = methodBuilder(contractDsl) + BlockBuilder blockBuilder = new BlockBuilder(" ") + when: + builder.appendTo(blockBuilder) + then: + String test = blockBuilder.toString() + SyntaxChecker.tryToCompile(methodBuilderName, test) + asserter(test) + and: + stubMappingIsValidWireMockStub(contractDsl) + where: + methodBuilderName | methodBuilder | asserter + "MockMvcSpockMethodBuilder" | { Contract dsl -> new HttpSpockMethodRequestProcessingBodyBuilder(dsl, properties, classDataForMethod) } | { String testContent -> assert testContent.contains('''response.header('Content-Length') ==~ java.util.regex.Pattern.compile('([1-9]\\\\d*)')''') && testContent.contains('''response.header('Content-Type') ==~ java.util.regex.Pattern.compile('^\\\\s*\\\\S[\\\\S\\\\s]*')'''); return true } + "MockMvcJUnitMethodBuilder" | { Contract dsl -> new MockMvcJUnitMethodBodyBuilder(dsl, properties, classDataForMethod) } | { String testContent -> assert testContent.contains('''assertThat(response.header("Content-Type")).matches("^\\\\s*\\\\S[\\\\S\\\\s]*")''') && testContent.contains('''assertThat(response.header("Content-Length")).matches("([1-9]\\\\d*)")'''); return true } + "JaxRsClientSpockMethodRequestProcessingBodyBuilder" | { Contract dsl -> new JaxRsClientSpockMethodRequestProcessingBodyBuilder(dsl, properties, classDataForMethod) } | { String testContent -> assert testContent.contains('''response.getHeaderString('Content-Length') ==~ java.util.regex.Pattern.compile('([1-9]\\\\d*)')''') && testContent.contains('''response.getHeaderString('Content-Type') ==~ java.util.regex.Pattern.compile('^\\\\s*\\\\S[\\\\S\\\\s]*')'''); return true } + "JaxRsClientJUnitMethodBodyBuilder" | { Contract dsl -> new JaxRsClientJUnitMethodBodyBuilder(dsl, properties, classDataForMethod) } | { String testContent -> assert testContent.contains('''assertThat(response.getHeaderString("Content-Type")).matches("^\\\\s*\\\\S[\\\\S\\\\s]*")''') && testContent.contains('''assertThat(response.getHeaderString("Content-Length")).matches("([1-9]\\\\d*)")'''); return true } + } + } diff --git a/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/builder/SpringTestMethodBodyBuildersSpec.groovy b/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/builder/SpringTestMethodBodyBuildersSpec.groovy index b5316f2999..b13f3c5cf2 100644 --- a/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/builder/SpringTestMethodBodyBuildersSpec.groovy +++ b/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/builder/SpringTestMethodBodyBuildersSpec.groovy @@ -898,9 +898,9 @@ class SpringTestMethodBodyBuildersSpec extends Specification implements WireMock SyntaxChecker.tryToCompile(methodBuilderName, blockBuilder.toString()) where: methodBuilderName | methodBuilder | headerEvaluationString - HttpSpockMethodRequestProcessingBodyBuilder.simpleName | { Contract dsl -> new HttpSpockMethodRequestProcessingBodyBuilder(dsl, properties, generatedClassDataForMethod) } | '''response.header('Location') ==~ java.util.regex.Pattern.compile('^((http[s]?|ftp):/)/?([^:/s]+)(:[0-9]{1,5})?/partners/[0-9]+/users/[0-9]+')''' - MockMvcJUnitMethodBodyBuilder.simpleName | { Contract dsl -> new MockMvcJUnitMethodBodyBuilder(dsl, properties, generatedClassDataForMethod) } | 'assertThat(response.header("Location")).matches("^((http[s]?|ftp):/)/?([^:/s]+)(:[0-9]{1,5})?/partners/[0-9]+/users/[0-9]+");' - WebTestClientJUnitMethodBodyBuilder.simpleName | { Contract dsl -> new WebTestClientJUnitMethodBodyBuilder(dsl, properties, generatedClassDataForMethod) } | 'assertThat(response.header("Location")).matches("^((http[s]?|ftp):/)/?([^:/s]+)(:[0-9]{1,5})?/partners/[0-9]+/users/[0-9]+");' + HttpSpockMethodRequestProcessingBodyBuilder.simpleName | { Contract dsl -> new HttpSpockMethodRequestProcessingBodyBuilder(dsl, properties, generatedClassDataForMethod) } | '''response.header('Location') ==~ java.util.regex.Pattern.compile('^((http[s]?|ftp):/)/?([^:/\\\\s]+)(:[0-9]{1,5})?/partners/[0-9]+/users/[0-9]+')''' + MockMvcJUnitMethodBodyBuilder.simpleName | { Contract dsl -> new MockMvcJUnitMethodBodyBuilder(dsl, properties, generatedClassDataForMethod) } | 'assertThat(response.header("Location")).matches("^((http[s]?|ftp):/)/?([^:/\\\\s]+)(:[0-9]{1,5})?/partners/[0-9]+/users/[0-9]+");' + WebTestClientJUnitMethodBodyBuilder.simpleName | { Contract dsl -> new WebTestClientJUnitMethodBodyBuilder(dsl, properties, generatedClassDataForMethod) } | 'assertThat(response.header("Location")).matches("^((http[s]?|ftp):/)/?([^:/\\\\s]+)(:[0-9]{1,5})?/partners/[0-9]+/users/[0-9]+");' } def 'should work with more complex stuff and jsonpaths with #methodBuilderName'() {