diff --git a/spring-cloud-contract-verifier/src/main/java/org/springframework/cloud/contract/verifier/builder/GroovyBodyParser.java b/spring-cloud-contract-verifier/src/main/java/org/springframework/cloud/contract/verifier/builder/GroovyBodyParser.java index 8547042824..5333825dcd 100644 --- a/spring-cloud-contract-verifier/src/main/java/org/springframework/cloud/contract/verifier/builder/GroovyBodyParser.java +++ b/spring-cloud-contract-verifier/src/main/java/org/springframework/cloud/contract/verifier/builder/GroovyBodyParser.java @@ -78,7 +78,15 @@ interface GroovyBodyParser extends BodyParser { @Override default String quotedLongText(Object text) { - return "'''" + groovyEscapedString(text) + "'''"; + String escapedString = groovyEscapedString(text); + if (escapedString.startsWith("'")) { + escapedString = "\\'" + escapedString.substring(1); + } + if (escapedString.endsWith("'")) { + escapedString = escapedString.substring(0, escapedString.length() - 1) + + "\\'"; + } + return "'''" + escapedString + "'''"; } default String groovyEscapedString(Object text) { diff --git a/spring-cloud-contract-verifier/src/main/java/org/springframework/cloud/contract/verifier/builder/JaxRsUrlPathWhen.java b/spring-cloud-contract-verifier/src/main/java/org/springframework/cloud/contract/verifier/builder/JaxRsUrlPathWhen.java index 238ebba105..0be77f743d 100644 --- a/spring-cloud-contract-verifier/src/main/java/org/springframework/cloud/contract/verifier/builder/JaxRsUrlPathWhen.java +++ b/spring-cloud-contract-verifier/src/main/java/org/springframework/cloud/contract/verifier/builder/JaxRsUrlPathWhen.java @@ -33,9 +33,13 @@ class JaxRsUrlPathWhen implements When, JaxRsAcceptor, QueryParamsResolver { private final GeneratedClassMetaData generatedClassMetaData; - JaxRsUrlPathWhen(BlockBuilder blockBuilder, GeneratedClassMetaData metaData) { + private final BodyParser bodyParser; + + JaxRsUrlPathWhen(BlockBuilder blockBuilder, GeneratedClassMetaData metaData, + BodyParser bodyParser) { this.blockBuilder = blockBuilder; this.generatedClassMetaData = metaData; + this.bodyParser = bodyParser; } @Override @@ -72,7 +76,8 @@ class JaxRsUrlPathWhen implements When, JaxRsAcceptor, QueryParamsResolver { .iterator(); while (iterator.hasNext()) { QueryParameter param = iterator.next(); - String text = ".queryParam(\"" + param.getName() + "\", \"" + resolveParamValue(param) + "\")"; + String text = ".queryParam(\"" + param.getName() + "\", " + + this.bodyParser.quotedShortText(resolveParamValue(param)) + ")"; if (iterator.hasNext()) { this.blockBuilder.addLine(text); } diff --git a/spring-cloud-contract-verifier/src/main/java/org/springframework/cloud/contract/verifier/builder/JaxRsWhen.java b/spring-cloud-contract-verifier/src/main/java/org/springframework/cloud/contract/verifier/builder/JaxRsWhen.java index 42a6c678ae..af327ba738 100644 --- a/spring-cloud-contract-verifier/src/main/java/org/springframework/cloud/contract/verifier/builder/JaxRsWhen.java +++ b/spring-cloud-contract-verifier/src/main/java/org/springframework/cloud/contract/verifier/builder/JaxRsWhen.java @@ -36,7 +36,9 @@ class JaxRsWhen implements When, BodyMethodVisitor, JaxRsAcceptor { this.blockBuilder = blockBuilder; this.generatedClassMetaData = generatedClassMetaData; this.bodyParser = bodyParser; - this.whens.addAll(Arrays.asList(new JaxRsUrlPathWhen(this.blockBuilder, this.generatedClassMetaData), + this.whens.addAll(Arrays.asList( + new JaxRsUrlPathWhen(this.blockBuilder, this.generatedClassMetaData, + bodyParser), new JaxRsRequestWhen(this.blockBuilder, this.generatedClassMetaData), new JaxRsRequestHeadersWhen(this.blockBuilder, bodyParser), new JaxRsRequestCookiesWhen(this.blockBuilder, bodyParser), diff --git a/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/builder/JaxRsClientMethodBuilderSpec.groovy b/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/builder/JaxRsClientMethodBuilderSpec.groovy index de55bc0349..c496ef30c2 100644 --- a/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/builder/JaxRsClientMethodBuilderSpec.groovy +++ b/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/builder/JaxRsClientMethodBuilderSpec.groovy @@ -632,7 +632,7 @@ class JaxRsClientMethodBuilderSpec extends Specification implements WireMockStub } | ['entity("\\"\\"", "text/plain")', 'header("Timer", "123")'] } - def "should generate a call with an url path and query parameters with #methodBuilderName"() { + def "should generate a call with an url path and query parameters with jaxrs"() { given: Contract contractDsl = Contract.make { request { @@ -646,6 +646,8 @@ class JaxRsClientMethodBuilderSpec extends Specification implements WireMockStub parameter 'search': $(consumer(notMatching(~/^\/[0-9]{2}$/)), producer("55")) parameter 'age': $(consumer(notMatching("^\\w*\$")), producer("99")) parameter 'name': $(consumer(matching("Denis.*")), producer("Denis.Stepanov")) + parameter 'nameWithDoubleQuote': '"quote"' + parameter 'nameWithQuote': "'quote'" parameter 'email': "bob@email.com" parameter 'hello': $(consumer(matching("Denis.*")), producer(absent())) parameter 'hello': absent() @@ -666,14 +668,16 @@ class JaxRsClientMethodBuilderSpec extends Specification implements WireMockStub when: String test = singleTestGenerator(contractDsl) then: - test.contains(modifyStringIfRequired.call('''queryParam("limit", "10"''')) - test.contains(modifyStringIfRequired.call('''queryParam("offset", "20"''')) - test.contains(modifyStringIfRequired.call('''queryParam("filter", "email"''')) - test.contains(modifyStringIfRequired.call('''queryParam("sort", "name"''')) - test.contains(modifyStringIfRequired.call('''queryParam("search", "55"''')) - test.contains(modifyStringIfRequired.call('''queryParam("age", "99"''')) - test.contains(modifyStringIfRequired.call('''queryParam("name", "Denis.Stepanov"''')) - test.contains(modifyStringIfRequired.call('''queryParam("email", "bob@email.com"''')) + test.contains('''queryParam("limit", "10"''') + test.contains('''queryParam("offset", "20"''') + test.contains('''queryParam("filter", "email"''') + test.contains('''queryParam("sort", "name"''') + test.contains('''queryParam("search", "55"''') + test.contains('''queryParam("age", "99"''') + test.contains('''queryParam("name", "Denis.Stepanov"''') + test.contains('''queryParam("nameWithDoubleQuote", "\\"quote\\""''') + test.contains('''queryParam("nameWithQuote", "'quote'"''') + test.contains('''queryParam("email", "bob@email.com"''') test.contains("""assertThatJson(parsedJson).field("['property1']").isEqualTo("a")""") test.contains("""assertThatJson(parsedJson).field("['property2']").isEqualTo("b")""") and: @@ -681,13 +685,69 @@ class JaxRsClientMethodBuilderSpec extends Specification implements WireMockStub and: SyntaxChecker.tryToCompile(methodBuilderName, test) where: - methodBuilderName | methodBuilder | modifyStringIfRequired - "jaxrs-spock" | { - properties.testFramework = TestFramework.SPOCK; properties.testMode = TestMode.JAXRSCLIENT - } | { String paramString -> paramString } + methodBuilderName | methodBuilder "jaxrs" | { properties.testFramework = TestFramework.JUNIT; properties.testMode = TestMode.JAXRSCLIENT - } | { String paramString -> paramString.replace("'", "\"") } + } + } + + def "should generate a call with an url path and query parameters with jaxrs-spock"() { + given: + Contract contractDsl = Contract.make { + request { + method 'GET' + urlPath('/users') { + queryParameters { + parameter 'limit': $(consumer(equalTo("20")), producer(equalTo("10"))) + parameter 'offset': $(consumer(containing("20")), producer(equalTo("20"))) + parameter 'filter': "email" + parameter 'sort': equalTo("name") + parameter 'search': $(consumer(notMatching(~/^\/[0-9]{2}$/)), producer("55")) + parameter 'age': $(consumer(notMatching("^\\w*\$")), producer("99")) + parameter 'name': $(consumer(matching("Denis.*")), producer("Denis.Stepanov")) + parameter 'nameWithDoubleQuote': '"quote"' + parameter 'nameWithQuote': "'quote'" + parameter 'email': "bob@email.com" + parameter 'hello': $(consumer(matching("Denis.*")), producer(absent())) + parameter 'hello': absent() + } + } + } + response { + status OK() + body """ + { + "property1": "a", + "property2": "b" + } + """ + } + } + methodBuilder() + when: + String test = singleTestGenerator(contractDsl) + then: + test.contains('''queryParam("limit", '10\'''') + test.contains('''queryParam("offset", '20\'''') + test.contains('''queryParam("filter", 'email\'''') + test.contains('''queryParam("sort", 'name\'''') + test.contains('''queryParam("search", '55\'''') + test.contains('''queryParam("age", '99\'''') + test.contains('''queryParam("name", 'Denis.Stepanov\'''') + test.contains("""queryParam("nameWithDoubleQuote", '''"quote"'''""") + test.contains("""queryParam("nameWithQuote", '''\\'quote\\''''""") + test.contains('''queryParam("email", 'bob@email.com\'''') + test.contains("""assertThatJson(parsedJson).field("['property1']").isEqualTo("a")""") + test.contains("""assertThatJson(parsedJson).field("['property2']").isEqualTo("b")""") + and: + stubMappingIsValidWireMockStub(contractDsl) + and: + SyntaxChecker.tryToCompile(methodBuilderName, test) + where: + methodBuilderName | methodBuilder + "jaxrs-spock" | { + properties.testFramework = TestFramework.SPOCK; properties.testMode = TestMode.JAXRSCLIENT + } } @Issue('#169') @@ -725,14 +785,14 @@ class JaxRsClientMethodBuilderSpec extends Specification implements WireMockStub when: String test = singleTestGenerator(contractDsl) then: - test.contains(modifyStringIfRequired.call('''queryParam("limit", "10"''')) - test.contains(modifyStringIfRequired.call('''queryParam("offset", "20"''')) - test.contains(modifyStringIfRequired.call('''queryParam("filter", "email"''')) - test.contains(modifyStringIfRequired.call('''queryParam("sort", "name"''')) - test.contains(modifyStringIfRequired.call('''queryParam("search", "55"''')) - test.contains(modifyStringIfRequired.call('''queryParam("age", "99"''')) - test.contains(modifyStringIfRequired.call('''queryParam("name", "Denis.Stepanov"''')) - test.contains(modifyStringIfRequired.call('''queryParam("email", "bob@email.com"''')) + test.contains(modifyStringIfRequired.call('''queryParam("limit", '10\'''')) + test.contains(modifyStringIfRequired.call('''queryParam("offset", '20\'''')) + test.contains(modifyStringIfRequired.call('''queryParam("filter", 'email\'''')) + test.contains(modifyStringIfRequired.call('''queryParam("sort", 'name\'''')) + test.contains(modifyStringIfRequired.call('''queryParam("search", '55\'''')) + test.contains(modifyStringIfRequired.call('''queryParam("age", '99\'''')) + test.contains(modifyStringIfRequired.call('''queryParam("name", 'Denis.Stepanov\'''')) + test.contains(modifyStringIfRequired.call('''queryParam("email", 'bob@email.com\'''')) test.contains("""assertThatJson(parsedJson).field("['property1']").isEqualTo("a")""") test.contains("""assertThatJson(parsedJson).field("['property2']").isEqualTo("b")""") and: 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 9be94f1cdd..2aa3737748 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 @@ -1814,6 +1814,49 @@ response: } } + @Issue("#1441") + def "should work with query parameters that need to be escaped [#methodBuilderName]"() { + given: + Contract contractDsl = Contract.make { + request { + method 'POST' + urlPath("/rest/something") { + queryParameters { + parameter 'quote': equalTo("\"") + } + } + } + response { + status OK() + } + } + methodBuilder() + when: + String test = singleTestGenerator(contractDsl) + then: + SyntaxChecker.tryToCompileWithoutCompileStatic(methodBuilderName, test) + !test.contains('''.queryParam("quote",""")''') + and: + stubMappingIsValidWireMockStub(contractDsl) + where: + methodBuilderName | methodBuilder + "spock" | { + properties.testFramework = TestFramework.SPOCK + } + "mockmvc" | { + properties.testMode = TestMode.MOCKMVC + } + "jaxrs-spock" | { + properties.testFramework = TestFramework.SPOCK; properties.testMode = TestMode.JAXRSCLIENT + } + "jaxrs" | { + properties.testFramework = TestFramework.JUNIT; properties.testMode = TestMode.JAXRSCLIENT + } + "testNG" | { + properties.testFramework = TestFramework.TESTNG + } + } + @Issue("#1262") def "should work with the timeout flag for groovy [#methodBuilderName]"() { given: