From 6ab02d8ce893d5a65feb4d115d79f140f40a0bc3 Mon Sep 17 00:00:00 2001 From: Alexey Nesterov Date: Thu, 19 Jul 2018 08:14:53 +0100 Subject: [PATCH] Fix using regex patterns when matching raw body value (#692) When matching non-json or xml body with regex the expression gets escaped. Because of that all standard patterns (number(), positiveInt()) will not work either. E.g. response { status OK() body(value(stub("1"), test(number()))) } Will generate invalid matcher: assertThat(responseBody).matches("-?(d*.d+|d+)"); Valid version is: assertThat(responseBody).matches("-?(\\d*.\\d+|\\d+)"); --- .../builder/JUnitMethodBodyBuilder.groovy | 14 ++++++--- .../RestAssuredJUnitMethodBodyBuilder.groovy | 2 +- ...kMethodRequestProcessingBodyBuilder.groovy | 8 ++++- .../verifier/util/RegexpBuilders.groovy | 2 +- .../builder/MethodBodyBuilderSpec.groovy | 30 +++++++++++++++++++ 5 files changed, 49 insertions(+), 7 deletions(-) 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 0d1dbd64fb..b8ff6f9e10 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 @@ -26,12 +26,14 @@ import org.springframework.cloud.contract.spec.internal.Header import org.springframework.cloud.contract.spec.internal.NamedProperty import org.springframework.cloud.contract.spec.internal.Request import org.springframework.cloud.contract.verifier.config.ContractVerifierConfigProperties +import org.springframework.cloud.contract.verifier.util.RegexpBuilders import java.util.regex.Pattern import static groovy.json.StringEscapeUtils.escapeJava import static org.springframework.cloud.contract.verifier.config.TestFramework.JUNIT import static org.springframework.cloud.contract.verifier.util.ContentUtils.getJavaMultipartFileParameterContent + /** * Root class for JUnit method building * @@ -78,7 +80,7 @@ abstract class JUnitMethodBodyBuilder extends RequestProcessingMethodBodyBuilder @Override protected String getResponseBodyPropertyComparisonString(String property, Pattern value) { - return "assertThat(responseBody${property}).${buildEscapedMatchesMethod(value)}" + return "assertThat(responseBody${property}).${createBodyComparison(value)}" } @Override @@ -183,6 +185,11 @@ abstract class JUnitMethodBodyBuilder extends RequestProcessingMethodBodyBuilder return buildEscapedMatchesMethod(headerValue) + ";" } + protected String createBodyComparison(Pattern bodyValue) { + String patternAsString = bodyValue.pattern() + return createMatchesMethod(RegexpBuilders.buildGStringRegexpForTestSide(patternAsString)) + ";" + } + protected String createCookieComparison(Object cookieValue) { String escapedCookie = convertUnicodeEscapesIfRequired("$cookieValue") return "isEqualTo(\"$escapedCookie\");" @@ -197,8 +204,7 @@ abstract class JUnitMethodBodyBuilder extends RequestProcessingMethodBodyBuilder return createMatchesMethod(escapedHeader) } - protected String createMatchesMethod(String escapedHeader) { - return "matches(\"$escapedHeader\")" + protected String createMatchesMethod(String pattern) { + return "matches(\"$pattern\")" } - } diff --git a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/RestAssuredJUnitMethodBodyBuilder.groovy b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/RestAssuredJUnitMethodBodyBuilder.groovy index fdfabf0c72..249f260867 100644 --- a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/RestAssuredJUnitMethodBodyBuilder.groovy +++ b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/RestAssuredJUnitMethodBodyBuilder.groovy @@ -72,7 +72,7 @@ class RestAssuredJUnitMethodBodyBuilder extends JUnitMethodBodyBuilder { @Override protected String getResponseBodyPropertyComparisonString(String property, Pattern value) { - return """assertThat(responseBody).${createHeaderComparison(value)}""" + return """assertThat(responseBody).${createBodyComparison(value)}""" } @Override 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 0e4f0022e6..3cef2d4487 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 @@ -27,6 +27,7 @@ import org.springframework.cloud.contract.spec.internal.Request import org.springframework.cloud.contract.spec.internal.ExecutionProperty import org.springframework.cloud.contract.verifier.config.ContractVerifierConfigProperties import org.springframework.cloud.contract.verifier.util.ContentUtils +import org.springframework.cloud.contract.verifier.util.RegexpBuilders import java.util.regex.Pattern @@ -60,7 +61,7 @@ abstract class SpockMethodRequestProcessingBodyBuilder extends RequestProcessing @Override protected String getResponseBodyPropertyComparisonString(String property, Pattern value) { - return "responseBody$property ${patternComparison(value)}" + return "responseBody$property ${createBodyComparison(value)}" } @Override @@ -173,6 +174,11 @@ abstract class SpockMethodRequestProcessingBodyBuilder extends RequestProcessing return "== '$cookieValue'" } + protected String createBodyComparison(Pattern bodyValue) { + String patternAsString = bodyValue.pattern() + return patternComparison(RegexpBuilders.buildGStringRegexpForTestSide(patternAsString)) + ";" + } + protected String convertCookieComparison(Pattern cookieValue) { return patternComparison(cookieValue) } diff --git a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/util/RegexpBuilders.groovy b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/util/RegexpBuilders.groovy index f1593eb2f4..74f5f915ce 100644 --- a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/util/RegexpBuilders.groovy +++ b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/util/RegexpBuilders.groovy @@ -28,7 +28,7 @@ import static ContentType.* import static ContentUtils.extractValue /** - * Useful utility methods to work with regular expresisons + * Useful utility methods to work with regular expressions * * Do not change to {@code @CompileStatic} since it's using double dispatch. * 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 437773ee97..7d68656ffc 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 @@ -716,4 +716,34 @@ DocumentContext parsedJson = JsonPath.parse(json); "JaxRsClientJUnitMethodBodyBuilder" | { Contract dsl -> new JaxRsClientJUnitMethodBodyBuilder(dsl, properties) } } + def "should not escape a regex pattern when matching raw body value [#methodBuilderName]"() { + def pattern = "\\d+\\w?" + def escapedPattern = "\\\\d+\\\\w?" + + given: + Contract contractDsl = Contract.make { + request { + method 'GET' + url '/api/arbitrary-url' + } + response { + status OK() + body(value(stub("1"), test(regex(pattern)))) + } + } + MethodBodyBuilder builder = methodBuilder(contractDsl) + BlockBuilder blockBuilder = new BlockBuilder(" ") + when: + builder.appendTo(blockBuilder) + then: + String test = blockBuilder.toString() + test.contains(escapedPattern) + 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) } + } + }