diff --git a/pom.xml b/pom.xml index 8b9841716a..f0e517233f 100644 --- a/pom.xml +++ b/pom.xml @@ -190,6 +190,32 @@ + + + org.apache.maven.plugins + maven-clean-plugin + 3.0.0 + + + + target + + + ${env.HOME}/.m2/repository/com/example/ + + **/* + + + + ${env.HOME}/.m2/repository/org/springframework/cloud/contract/verifier/stubs/ + + **/* + + + + + + diff --git a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/messaging/camel/StubRunnerCamelPredicate.java b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/messaging/camel/StubRunnerCamelPredicate.java index 985ee3e66a..d0174bbba6 100644 --- a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/messaging/camel/StubRunnerCamelPredicate.java +++ b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/messaging/camel/StubRunnerCamelPredicate.java @@ -55,7 +55,8 @@ class StubRunnerCamelPredicate implements Predicate { } Object inputMessage = exchange.getIn().getBody(); JsonPaths jsonPaths = JsonToJsonPathsConverter - .transformToJsonPathWithStubsSideValues(groovyDsl.getInput().getMessageBody()); + .transformToJsonPathWithStubsSideValuesAndNoArraySizeCheck( + groovyDsl.getInput().getMessageBody()); DocumentContext parsedJson; try { parsedJson = JsonPath diff --git a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/messaging/integration/StubRunnerIntegrationMessageSelector.java b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/messaging/integration/StubRunnerIntegrationMessageSelector.java index d69147cd24..12a567e4c2 100644 --- a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/messaging/integration/StubRunnerIntegrationMessageSelector.java +++ b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/messaging/integration/StubRunnerIntegrationMessageSelector.java @@ -55,7 +55,7 @@ class StubRunnerIntegrationMessageSelector implements MessageSelector { } Object inputMessage = message.getPayload(); JsonPaths jsonPaths = JsonToJsonPathsConverter - .transformToJsonPathWithStubsSideValues( + .transformToJsonPathWithStubsSideValuesAndNoArraySizeCheck( groovyDsl.getInput().getMessageBody()); DocumentContext parsedJson; try { diff --git a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/messaging/stream/StubRunnerStreamMessageSelector.java b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/messaging/stream/StubRunnerStreamMessageSelector.java index 9292707667..a448536aa4 100644 --- a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/messaging/stream/StubRunnerStreamMessageSelector.java +++ b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/messaging/stream/StubRunnerStreamMessageSelector.java @@ -55,7 +55,7 @@ class StubRunnerStreamMessageSelector implements MessageSelector { } Object inputMessage = message.getPayload(); JsonPaths jsonPaths = JsonToJsonPathsConverter - .transformToJsonPathWithStubsSideValues( + .transformToJsonPathWithStubsSideValuesAndNoArraySizeCheck( groovyDsl.getInput().getMessageBody()); DocumentContext parsedJson; try { diff --git a/spring-cloud-contract-tools/spring-cloud-contract-converters/src/main/groovy/org/springframework/cloud/contract/verifier/wiremock/DslToWireMockClientConverter.groovy b/spring-cloud-contract-tools/spring-cloud-contract-converters/src/main/groovy/org/springframework/cloud/contract/verifier/wiremock/DslToWireMockClientConverter.groovy index 3fe90afb25..cae1b34b86 100644 --- a/spring-cloud-contract-tools/spring-cloud-contract-converters/src/main/groovy/org/springframework/cloud/contract/verifier/wiremock/DslToWireMockClientConverter.groovy +++ b/spring-cloud-contract-tools/spring-cloud-contract-converters/src/main/groovy/org/springframework/cloud/contract/verifier/wiremock/DslToWireMockClientConverter.groovy @@ -21,7 +21,6 @@ import org.springframework.cloud.contract.verifier.dsl.wiremock.WireMockStubStra import org.springframework.cloud.contract.verifier.file.ContractMetadata import java.nio.charset.StandardCharsets - /** * Converts DSLs to WireMock stubs * @@ -33,6 +32,7 @@ class DslToWireMockClientConverter extends DslToWireMockConverter { @Override String convertContent(String rootName, ContractMetadata contract) { String dslContent = contract.path.getText(StandardCharsets.UTF_8.toString()) - return new WireMockStubStrategy(rootName, contract, createGroovyDSLFromStringContent(dslContent)).toWireMockClientStub() + return new WireMockStubStrategy(rootName, contract, + createGroovyDSLFromStringContent(dslContent)).toWireMockClientStub() } } diff --git a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/main/groovy/org/springframework/cloud/contract/verifier/plugin/GenerateWireMockClientStubsFromDslTask.groovy b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/main/groovy/org/springframework/cloud/contract/verifier/plugin/GenerateWireMockClientStubsFromDslTask.groovy index e87a0422cd..a3693e28c8 100644 --- a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/main/groovy/org/springframework/cloud/contract/verifier/plugin/GenerateWireMockClientStubsFromDslTask.groovy +++ b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/main/groovy/org/springframework/cloud/contract/verifier/plugin/GenerateWireMockClientStubsFromDslTask.groovy @@ -47,7 +47,8 @@ class GenerateWireMockClientStubsFromDslTask extends ConventionTask { ContractVerifierConfigProperties props = getConfigProperties() File outMappingsDir = props.stubsOutputDir != null ? new File(props.stubsOutputDir, DEFAULT_MAPPINGS_FOLDER) : new File(project.buildDir, "stubs/$DEFAULT_MAPPINGS_FOLDER") - RecursiveFilesConverter converter = new RecursiveFilesConverter(new DslToWireMockClientConverter(), + RecursiveFilesConverter converter = new RecursiveFilesConverter( + new DslToWireMockClientConverter(), getConfigProperties(), outMappingsDir) converter.processFiles() } diff --git a/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/main/java/org/springframework/cloud/contract/maven/verifier/GenerateTestsMojo.java b/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/main/java/org/springframework/cloud/contract/maven/verifier/GenerateTestsMojo.java index fd6c43ea98..3e90e34e5c 100644 --- a/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/main/java/org/springframework/cloud/contract/maven/verifier/GenerateTestsMojo.java +++ b/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/main/java/org/springframework/cloud/contract/maven/verifier/GenerateTestsMojo.java @@ -80,6 +80,14 @@ public class GenerateTestsMojo extends AbstractMojo { @Parameter private List excludedFiles; + /** + * Incubating feature. You can check the size of JSON arrays. If not turned on + * explicitly will be disabled. + */ + @Parameter(property = "spring.cloud.contract.verifier.assert.size", + defaultValue = "false") + private boolean assertJsonSize; + /** * Patterns for which Spring Cloud Contract Verifier should generate @Ignored tests */ @@ -119,6 +127,7 @@ public class GenerateTestsMojo extends AbstractMojo { config.setStaticImports(staticImports); config.setIgnoredFiles(ignoredFiles); config.setExcludedFiles(excludedFiles); + config.setAssertJsonSize(assertJsonSize); project.addTestCompileSourceRoot(generatedTestSourcesDir.getAbsolutePath()); if (getLog().isInfoEnabled()) { getLog().info( @@ -155,4 +164,11 @@ public class GenerateTestsMojo extends AbstractMojo { this.ignoredFiles = ignoredFiles; } + public boolean isAssertJsonSize() { + return assertJsonSize; + } + + public void setAssertJsonSize(boolean assertJsonSize) { + this.assertJsonSize = assertJsonSize; + } } diff --git a/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/java/org/springframework/cloud/contract/maven/verifier/PluginUnitTest.java b/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/java/org/springframework/cloud/contract/maven/verifier/PluginUnitTest.java index 4c4b6804a3..60e7959978 100644 --- a/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/java/org/springframework/cloud/contract/maven/verifier/PluginUnitTest.java +++ b/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/java/org/springframework/cloud/contract/maven/verifier/PluginUnitTest.java @@ -18,6 +18,7 @@ package org.springframework.cloud.contract.maven.verifier; import java.io.File; +import org.apache.commons.io.FileUtils; import org.junit.Rule; import org.junit.Test; @@ -27,6 +28,7 @@ import io.takari.maven.testing.TestResources; import static io.takari.maven.testing.TestMavenRuntime.newParameter; import static io.takari.maven.testing.TestResources.assertFilesNotPresent; import static io.takari.maven.testing.TestResources.assertFilesPresent; +import static org.assertj.core.api.BDDAssertions.then; public class PluginUnitTest { @@ -58,6 +60,7 @@ public class PluginUnitTest { assertFilesPresent(basedir, "target/stubs/contracts/Sample.groovy"); assertFilesPresent(basedir, "target/stubs/contracts/Messaging.groovy"); } + @Test public void shouldGenerateWireMockStubsInSelectedLocation() throws Exception { File basedir = resources.getBasedir("basic"); @@ -89,6 +92,26 @@ public class PluginUnitTest { "target/generated-test-sources/contracts/org/springframework/cloud/contract/verifier/tests/ContractVerifierTest.java"); } + @Test + public void shouldGenerateContractTestsWithoutArraySize() throws Exception { + File basedir = resources.getBasedir("basic"); + maven.executeMojo(basedir, "generateTests"); + assertFilesPresent(basedir, + "target/generated-test-sources/contracts/org/springframework/cloud/contract/verifier/tests/ContractVerifierTest.java"); + File test = new File(basedir, "target/generated-test-sources/contracts/org/springframework/cloud/contract/verifier/tests/ContractVerifierTest.java"); + then(FileUtils.readFileToString(test)).doesNotContain("hasSize(4)"); + } + + @Test + public void shouldGenerateContractTestsWithArraySize() throws Exception { + File basedir = resources.getBasedir("basic"); + maven.executeMojo(basedir, "generateTests", newParameter("assertJsonSize", "true")); + assertFilesPresent(basedir, + "target/generated-test-sources/contracts/org/springframework/cloud/contract/verifier/tests/ContractVerifierTest.java"); + File test = new File(basedir, "target/generated-test-sources/contracts/org/springframework/cloud/contract/verifier/tests/ContractVerifierTest.java"); + then(FileUtils.readFileToString(test)).contains("hasSize(4)"); + } + @Test public void shouldGenerateStubs() throws Exception { File basedir = resources.getBasedir("generatedStubs"); diff --git a/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/projects/basic/src/test/resources/contracts/WithList.groovy b/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/projects/basic/src/test/resources/contracts/WithList.groovy new file mode 100644 index 0000000000..f542a19e9a --- /dev/null +++ b/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/projects/basic/src/test/resources/contracts/WithList.groovy @@ -0,0 +1,34 @@ +/** + * + * Copyright 2013-2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +org.springframework.cloud.contract.spec.Contract.make { + request { + method 'POST' + url('/users') { + + } + headers { + header 'Content-Type': 'application/json' + } + } + response { + status 200 + headers { + header 'Location': '/users/john' + } + body '''{ "list" : [ "login", "john", "name", "John The Contract" ] }''' + } +} \ No newline at end of file diff --git a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/config/ContractVerifierConfigProperties.groovy b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/config/ContractVerifierConfigProperties.groovy index 366c9c4c96..6e34af897e 100644 --- a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/config/ContractVerifierConfigProperties.groovy +++ b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/config/ContractVerifierConfigProperties.groovy @@ -100,6 +100,6 @@ class ContractVerifierConfigProperties { * Incubating feature. You can check the size of JSON arrays. If not turned on * explicitly will be disabled. */ - Boolean assertJsonSize + Boolean assertJsonSize = false } diff --git a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/dsl/wiremock/WireMockRequestStubStrategy.groovy b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/dsl/wiremock/WireMockRequestStubStrategy.groovy index c156ec3aaa..2725e8c158 100755 --- a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/dsl/wiremock/WireMockRequestStubStrategy.groovy +++ b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/dsl/wiremock/WireMockRequestStubStrategy.groovy @@ -75,7 +75,8 @@ class WireMockRequestStubStrategy extends BaseWireMockStubStrategy { } ContentType contentType = tryToGetContentType(request.body.clientValue, request.headers) if (contentType == ContentType.JSON) { - JsonPaths values = JsonToJsonPathsConverter.transformToJsonPathWithStubsSideValues(getMatchingStrategyFromBody(request.body)?.clientValue) + JsonPaths values = JsonToJsonPathsConverter.transformToJsonPathWithStubsSideValuesAndNoArraySizeCheck( + getMatchingStrategyFromBody(request.body)?.clientValue) if (values.empty) { requestPattern.withRequestBody(WireMock.equalToJson(JsonOutput.toJson(getMatchingStrategy(request.body.clientValue).clientValue), false, false)) } else { diff --git a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/util/JsonToJsonPathsConverter.groovy b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/util/JsonToJsonPathsConverter.groovy index 7825035622..35ecbdc3e8 100644 --- a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/util/JsonToJsonPathsConverter.groovy +++ b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/util/JsonToJsonPathsConverter.groovy @@ -53,14 +53,20 @@ class JsonToJsonPathsConverter { JsonToJsonPathsConverter() { this.configProperties = new ContractVerifierConfigProperties() + log.debug("Creating JsonToJsonPaths converter with default properties") } - public static JsonPaths transformToJsonPathWithTestsSideValues(def json) { - return new JsonToJsonPathsConverter().transformToJsonPathWithValues(json, SERVER_SIDE) + public JsonPaths transformToJsonPathWithTestsSideValues(def json) { + return transformToJsonPathWithValues(json, SERVER_SIDE) } - public static JsonPaths transformToJsonPathWithStubsSideValues(def json) { - return new JsonToJsonPathsConverter().transformToJsonPathWithValues(json, CLIENT_SIDE) + public JsonPaths transformToJsonPathWithStubsSideValues(def json) { + return transformToJsonPathWithValues(json, CLIENT_SIDE) + } + + public static JsonPaths transformToJsonPathWithStubsSideValuesAndNoArraySizeCheck(def json) { + return new JsonToJsonPathsConverter() + .transformToJsonPathWithValues(json, CLIENT_SIDE) } private JsonPaths transformToJsonPathWithValues(def json, boolean clientSide) { @@ -134,14 +140,19 @@ class JsonToJsonPathsConverter { // Size verification: https://github.com/Codearte/accurest/issues/279 private void addSizeVerificationForListWithPrimitives(MethodBufferingJsonVerifiable key, Closure closure, List value) { - Boolean systemPropValue = Boolean.parseBoolean(System.getProperty(SIZE_ASSERTION_SYSTEM_PROP, "false")) + String systemPropValue = System.getProperty(SIZE_ASSERTION_SYSTEM_PROP) Boolean configPropValue = configProperties.assertJsonSize - boolean configPropSet = configPropValue != null - if (configPropSet && !configPropValue) { - return - } else if (!configPropSet && systemPropValue) { + if ((systemPropValue != null && Boolean.parseBoolean(systemPropValue)) || + configPropValue) { + addArraySizeCheck(key, value, closure) + } else { + log.debug("Turning off the incubating feature of JSON array check. " + + "System property [$systemPropValue]. Config property [$configPropValue]") return } + } + + private void addArraySizeCheck(MethodBufferingJsonVerifiable key, List value, Closure closure) { log.debug("WARNING: Turning on the incubating feature of JSON array check") if (isRootElement(key) || key.assertsConcreteValue()) { if (value.size() > 0) { 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 849c06591d..46569c0a0a 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 @@ -18,11 +18,12 @@ package org.springframework.cloud.contract.verifier.builder import org.springframework.cloud.contract.verifier.config.ContractVerifierConfigProperties import org.springframework.cloud.contract.verifier.dsl.WireMockStubVerifier -import org.springframework.cloud.contract.verifier.file.ContractMetadata import org.springframework.cloud.contract.verifier.dsl.wiremock.WireMockStubStrategy +import org.springframework.cloud.contract.verifier.file.ContractMetadata import spock.lang.Issue import spock.lang.Shared import spock.lang.Specification +import spock.util.environment.RestoreSystemProperties class JaxRsClientMethodBuilderSpec extends Specification implements WireMockStubVerifier { @@ -114,6 +115,43 @@ class JaxRsClientMethodBuilderSpec extends Specification implements WireMockStub BlockBuilder blockBuilder = new BlockBuilder(" ") when: builder.appendTo(blockBuilder) + then: + blockBuilder.toString().contains("""assertThatJson(parsedJson).field("property1").isEqualTo("a")""") + blockBuilder.toString().contains("""assertThatJson(parsedJson).array("property2").contains("a").isEqualTo("sth")""") + blockBuilder.toString().contains("""assertThatJson(parsedJson).array("property2").contains("b").isEqualTo("sthElse")""") + and: + stubMappingIsValidWireMockStub(new WireMockStubStrategy("Test", new ContractMetadata(null, false, 0, null), contractDsl).toWireMockClientStub()) + where: + methodBuilderName | methodBuilder + "JaxRsClientSpockMethodRequestProcessingBodyBuilder" | { org.springframework.cloud.contract.spec.Contract dsl -> new JaxRsClientSpockMethodRequestProcessingBodyBuilder(dsl, properties) } + "JaxRsClientJUnitMethodBodyBuilder" | { org.springframework.cloud.contract.spec.Contract dsl -> new JaxRsClientJUnitMethodBodyBuilder(dsl, properties) } + } + + @Issue("#79") + @RestoreSystemProperties + def "should generate assertions for simple response body constructed from map with a list with #methodBuilderName with array size check"() { + given: + System.setProperty('spring.cloud.contract.verifier.assert.size', 'true') + org.springframework.cloud.contract.spec.Contract contractDsl = org.springframework.cloud.contract.spec.Contract.make { + request { + method "GET" + url "test" + } + response { + status 200 + body( + property1: 'a', + property2: [ + [a: 'sth'], + [b: 'sthElse'] + ] + ) + } + } + MethodBodyBuilder builder = methodBuilder(contractDsl) + BlockBuilder blockBuilder = new BlockBuilder(" ") + when: + builder.appendTo(blockBuilder) then: blockBuilder.toString().contains("""assertThatJson(parsedJson).field("property1").isEqualTo("a")""") blockBuilder.toString().contains("""assertThatJson(parsedJson).array("property2").contains("a").isEqualTo("sth")""") diff --git a/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/builder/MockMvcMethodBodyBuilderSpec.groovy b/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/builder/MockMvcMethodBodyBuilderSpec.groovy index 58a44e8193..a46d9c3a76 100644 --- a/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/builder/MockMvcMethodBodyBuilderSpec.groovy +++ b/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/builder/MockMvcMethodBodyBuilderSpec.groovy @@ -22,6 +22,7 @@ import org.springframework.cloud.contract.verifier.dsl.WireMockStubVerifier import spock.lang.Issue import spock.lang.Shared import spock.lang.Specification +import spock.util.environment.RestoreSystemProperties import java.util.regex.Pattern /** @@ -186,6 +187,43 @@ class MockMvcMethodBodyBuilderSpec extends Specification implements WireMockStub then: blockBuilder.toString().contains("""assertThatJson(parsedJson).field("property1").isEqualTo("a")""") blockBuilder.toString().contains("""assertThatJson(parsedJson).array("property2").contains("a").isEqualTo("sth")""") + blockBuilder.toString().contains("""assertThatJson(parsedJson).array("property2").contains("b").isEqualTo("sthElse")""") + and: + stubMappingIsValidWireMockStub(contractDsl) + where: + methodBuilderName | methodBuilder + "MockMvcSpockMethodBuilder" | { Contract dsl -> new MockMvcSpockMethodRequestProcessingBodyBuilder(dsl, properties) } + "MockMvcJUnitMethodBuilder" | { Contract dsl -> new MockMvcJUnitMethodBodyBuilder(dsl, properties) } + } + + @Issue("#79") + @RestoreSystemProperties + def "should generate assertions for simple response body constructed from map with a list with #methodBuilderName with array size check"() { + given: + System.setProperty('spring.cloud.contract.verifier.assert.size', 'true') + Contract contractDsl = Contract.make { + request { + method "GET" + url "test" + } + response { + status 200 + body( + property1: 'a', + property2: [ + [a: 'sth'], + [b: 'sthElse'] + ] + ) + } + } + MethodBodyBuilder builder = methodBuilder(contractDsl) + BlockBuilder blockBuilder = new BlockBuilder(" ") + when: + builder.appendTo(blockBuilder) + then: + blockBuilder.toString().contains("""assertThatJson(parsedJson).field("property1").isEqualTo("a")""") + blockBuilder.toString().contains("""assertThatJson(parsedJson).array("property2").contains("a").isEqualTo("sth")""") blockBuilder.toString().contains("""assertThatJson(parsedJson).array("property2").hasSize(2)""") blockBuilder.toString().contains("""assertThatJson(parsedJson).array("property2").contains("b").isEqualTo("sthElse")""") and: @@ -1271,6 +1309,41 @@ World.'''""" when: builder.then(blockBuilder) def test = blockBuilder.toString() + then: + test.contains('assertThatJson(parsedJson).arrayField().contains("Java8").value()') + test.contains('assertThatJson(parsedJson).arrayField().contains("Spring").value()') + test.contains('assertThatJson(parsedJson).arrayField().contains("Java").value()') + test.contains('assertThatJson(parsedJson).arrayField().contains("Stream").value()') + test.contains('assertThatJson(parsedJson).arrayField().contains("SpringBoot").value()') + where: + methodBuilderName | methodBuilder + "MockMvcSpockMethodBuilder" | { Contract dsl -> new MockMvcSpockMethodRequestProcessingBodyBuilder(dsl, properties) } + "MockMvcJUnitMethodBuilder" | { Contract dsl -> new MockMvcJUnitMethodBodyBuilder(dsl, properties) } + } + + @Issue('266') + @RestoreSystemProperties + def "should generate proper test code with top level array using #methodBuilderName with array size check"() { + given: + System.setProperty('spring.cloud.contract.verifier.assert.size', 'true') + Contract contractDsl = Contract.make { + request { + method 'GET' + urlPath '/api/tags' + } + response { + status 200 + body(["Java", "Java8", "Spring", "SpringBoot", "Stream"]) + headers { + header('Content-Type': 'application/json;charset=UTF-8') + } + } + } + MethodBodyBuilder builder = methodBuilder(contractDsl) + BlockBuilder blockBuilder = new BlockBuilder(" ") + when: + builder.then(blockBuilder) + def test = blockBuilder.toString() then: test.contains('assertThatJson(parsedJson).hasSize(5)') test.contains('assertThatJson(parsedJson).arrayField().contains("Java8").value()') @@ -1306,7 +1379,6 @@ World.'''""" builder.then(blockBuilder) def test = blockBuilder.toString() then: - test.contains('assertThatJson(parsedJson).hasSize(2)') test.contains('assertThatJson(parsedJson).array().arrayField().isEqualTo("Programming").value()') test.contains('assertThatJson(parsedJson).array().arrayField().isEqualTo("Java").value()') test.contains('assertThatJson(parsedJson).array().arrayField().isEqualTo("Spring").value()') diff --git a/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/util/JsonToJsonPathsConverterSpec.groovy b/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/util/JsonToJsonPathsConverterSpec.groovy index aefe865f00..fb2e4152b3 100644 --- a/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/util/JsonToJsonPathsConverterSpec.groovy +++ b/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/util/JsonToJsonPathsConverterSpec.groovy @@ -25,6 +25,7 @@ import groovy.json.JsonOutput import groovy.json.JsonSlurper import net.minidev.json.JSONArray import spock.lang.Specification +import spock.util.environment.RestoreSystemProperties import java.util.regex.Pattern @@ -369,6 +370,36 @@ class JsonToJsonPathsConverterSpec extends Specification { it.method()== """.array("property2").contains("a").isEqualTo("sth")""" && it.jsonPath() == """\$.property2[*][?(@.a == 'sth')]""" } + pathAndValues.find { + it.method()== """.array("property2").contains("b").isEqualTo("sthElse")""" && + it.jsonPath() == """\$.property2[*][?(@.b == 'sthElse')]""" + } + and: + pathAndValues.size() == 3 + } + + @RestoreSystemProperties + def "should generate assertions for simple response body constructed from map with a list with array size check"() { + given: + System.setProperty('spring.cloud.contract.verifier.assert.size', 'true') + Map json = [ + property1: 'a', + property2: [ + [a: 'sth'], + [b: 'sthElse'] + ] + ] + when: + JsonPaths pathAndValues = new JsonToJsonPathsConverter().transformToJsonPathWithTestsSideValues(json) + then: + pathAndValues.find { + it.method()== """.field("property1").isEqualTo("a")""" && + it.jsonPath() == """\$[?(@.property1 == 'a')]""" + } + pathAndValues.find { + it.method()== """.array("property2").contains("a").isEqualTo("sth")""" && + it.jsonPath() == """\$.property2[*][?(@.a == 'sth')]""" + } pathAndValues.find { it.method()== """.array("property2").hasSize(2)""" && it.jsonPath() == """\$.property2[*]""" @@ -410,6 +441,32 @@ class JsonToJsonPathsConverterSpec extends Specification { { "property1": "a" }, + { + "property2": "b" + }]""" + when: + JsonPaths pathAndValues = new JsonToJsonPathsConverter().transformToJsonPathWithTestsSideValues(new JsonSlurper().parseText(json)) + then: + pathAndValues.find { + it.method()== """.array().contains("property1").isEqualTo("a")""" && + it.jsonPath() == """\$[*][?(@.property1 == 'a')]""" + } + pathAndValues.find { + it.method()== """.array().contains("property2").isEqualTo("b")""" && + it.jsonPath() == """\$[*][?(@.property2 == 'b')]""" + } + and: + pathAndValues.size() == 2 + } + + @RestoreSystemProperties + def "should generate assertions for array in response body with array size check"() { + given: + System.setProperty('spring.cloud.contract.verifier.assert.size', 'true') + String json = """[ + { + "property1": "a" + }, { "property2": "b" }]""" @@ -439,6 +496,31 @@ class JsonToJsonPathsConverterSpec extends Specification { { "property2": "test1"}, { "property3": "test2"} ] +}""" + when: + JsonPaths pathAndValues = new JsonToJsonPathsConverter().transformToJsonPathWithTestsSideValues(new JsonSlurper().parseText(json)) + then: + pathAndValues.find { + it.method()== """.array("property1").contains("property2").isEqualTo("test1")""" && + it.jsonPath() == """\$.property1[*][?(@.property2 == 'test1')]""" + } + pathAndValues.find { + it.method()== """.array("property1").contains("property3").isEqualTo("test2")""" && + it.jsonPath() == """\$.property1[*][?(@.property3 == 'test2')]""" + } + and: + pathAndValues.size() == 2 + } + + @RestoreSystemProperties + def "should generate assertions for array inside response body element with array size check"() { + given: + System.setProperty('spring.cloud.contract.verifier.assert.size', 'true') + String json = """{ + "property1": [ + { "property2": "test1"}, + { "property3": "test2"} + ] }""" when: JsonPaths pathAndValues = new JsonToJsonPathsConverter().transformToJsonPathWithTestsSideValues(new JsonSlurper().parseText(json)) @@ -517,7 +599,6 @@ class JsonToJsonPathsConverterSpec extends Specification { pathAndValues.size() == 1 } - def "should work with more complex stuff and jsonpaths"() { given: Map json = [ @@ -537,6 +618,31 @@ class JsonToJsonPathsConverterSpec extends Specification { it.method()== """.array("errors").contains("message").isEqualTo("incorrect_format")""" && it.jsonPath() == """\$.errors[*][?(@.message == 'incorrect_format')]""" } + and: + pathAndValues.size() == 2 + } + + @RestoreSystemProperties + def "should work with more complex stuff and jsonpaths with array size check"() { + given: + System.setProperty('spring.cloud.contract.verifier.assert.size', 'true') + Map json = [ + errors: [ + [property: "bank_account_number", + message: "incorrect_format"] + ] + ] + when: + JsonPaths pathAndValues = new JsonToJsonPathsConverter().transformToJsonPathWithTestsSideValues(json) + then: + pathAndValues.find { + it.method()== """.array("errors").contains("property").isEqualTo("bank_account_number")""" && + it.jsonPath() == """\$.errors[*][?(@.property == 'bank_account_number')]""" + } + pathAndValues.find { + it.method()== """.array("errors").contains("message").isEqualTo("incorrect_format")""" && + it.jsonPath() == """\$.errors[*][?(@.message == 'incorrect_format')]""" + } pathAndValues.find { it.method()== """.array("errors").hasSize(1)""" && it.jsonPath() == """\$.errors[*]""" @@ -581,6 +687,52 @@ class JsonToJsonPathsConverterSpec extends Specification { it.method()== """.array().field("place").field("bounding_box").array("coordinates").array().arrayField().isEqualTo(38.791645)""" && it.jsonPath() == """\$[*].place.bounding_box.coordinates[*][*][?(@ == 38.791645)]""" } + and: + pathAndValues.size() == 4 + and: + pathAndValues.each { + JsonAssertion.assertThat(json).matchesJsonPath(it.jsonPath()) + } + } + + @RestoreSystemProperties + def "should manage to parse a double array with array size check"() { + given: + System.setProperty('spring.cloud.contract.verifier.assert.size', 'true') + String json = ''' + [{ + "place": + { + "bounding_box": + { + "coordinates": + [[ + [-77.119759,38.995548], + [-76.909393,38.791645] + ]] + } + } + }] + ''' + when: + JsonPaths pathAndValues = new JsonToJsonPathsConverter().transformToJsonPathWithTestsSideValues(new JsonSlurper().parseText(json)) + then: + pathAndValues.find { + it.method()== """.array().field("place").field("bounding_box").array("coordinates").array().arrayField().isEqualTo(38.995548)""" && + it.jsonPath() == """\$[*].place.bounding_box.coordinates[*][*][?(@ == 38.995548)]""" + } + pathAndValues.find { + it.method()== """.array().field("place").field("bounding_box").array("coordinates").array().arrayField().isEqualTo(-77.119759)""" && + it.jsonPath() == """\$[*].place.bounding_box.coordinates[*][*][?(@ == -77.119759)]""" + } + pathAndValues.find { + it.method()== """.array().field("place").field("bounding_box").array("coordinates").array().arrayField().isEqualTo(-76.909393)""" && + it.jsonPath() == """\$[*].place.bounding_box.coordinates[*][*][?(@ == -76.909393)]""" + } + pathAndValues.find { + it.method()== """.array().field("place").field("bounding_box").array("coordinates").array().arrayField().isEqualTo(38.791645)""" && + it.jsonPath() == """\$[*].place.bounding_box.coordinates[*][*][?(@ == 38.791645)]""" + } pathAndValues.find { it.method()== """.hasSize(1)""" && it.jsonPath() == """\$"""