diff --git a/spring-cloud-contract-dependencies/pom.xml b/spring-cloud-contract-dependencies/pom.xml index 942ee8136c..4b86eb8f2e 100644 --- a/spring-cloud-contract-dependencies/pom.xml +++ b/spring-cloud-contract-dependencies/pom.xml @@ -15,7 +15,7 @@ Spring Cloud Contract Dependencies 2.1.7 - 0.4.7 + 0.4.8 1.0.2.v20150114 diff --git a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/main/groovy/org/springframework/cloud/contract/verifier/plugin/SpringCloudContractVerifierGradlePlugin.groovy b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/main/groovy/org/springframework/cloud/contract/verifier/plugin/SpringCloudContractVerifierGradlePlugin.groovy index 7cdd4242cf..007d1543ec 100644 --- a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/main/groovy/org/springframework/cloud/contract/verifier/plugin/SpringCloudContractVerifierGradlePlugin.groovy +++ b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/main/groovy/org/springframework/cloud/contract/verifier/plugin/SpringCloudContractVerifierGradlePlugin.groovy @@ -91,7 +91,7 @@ class SpringCloudContractVerifierGradlePlugin implements Plugin { //TODO: Deprecate this since starting with 1.1.x private void addProjectDependencies(Project project) { project.dependencies.add("testCompile", "com.github.tomakehurst:wiremock:2.1.7") - project.dependencies.add("testCompile", "com.toomuchcoding.jsonassert:jsonassert:0.4.7") + project.dependencies.add("testCompile", "com.toomuchcoding.jsonassert:jsonassert:0.4.8") project.dependencies.add("testCompile", "org.assertj:assertj-core:2.3.0") } diff --git a/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/projects/complex-configuration/pom.xml b/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/projects/complex-configuration/pom.xml index 601db4ead6..2ef9310d56 100644 --- a/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/projects/complex-configuration/pom.xml +++ b/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/projects/complex-configuration/pom.xml @@ -63,7 +63,7 @@ com.toomuchcoding.jsonassert jsonassert - 0.4.7 + 0.4.8 test diff --git a/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/projects/plugin-extension/pom.xml b/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/projects/plugin-extension/pom.xml index e57964dd63..e03b81bc7a 100644 --- a/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/projects/plugin-extension/pom.xml +++ b/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/projects/plugin-extension/pom.xml @@ -63,7 +63,7 @@ com.toomuchcoding.jsonassert jsonassert - 0.4.7 + 0.4.8 test diff --git a/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/projects/spring-boot-groovy/pom.xml b/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/projects/spring-boot-groovy/pom.xml index 6d0570cf4b..a0dc42aa9d 100644 --- a/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/projects/spring-boot-groovy/pom.xml +++ b/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/projects/spring-boot-groovy/pom.xml @@ -64,7 +64,7 @@ com.toomuchcoding.jsonassert jsonassert - 0.4.7 + 0.4.8 test diff --git a/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/projects/spring-boot-java/pom.xml b/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/projects/spring-boot-java/pom.xml index e57964dd63..e03b81bc7a 100644 --- a/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/projects/spring-boot-java/pom.xml +++ b/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/projects/spring-boot-java/pom.xml @@ -63,7 +63,7 @@ com.toomuchcoding.jsonassert jsonassert - 0.4.7 + 0.4.8 test 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 fe5be66de4..af206c4b71 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 @@ -78,7 +78,7 @@ class WireMockRequestStubStrategy extends BaseWireMockStubStrategy { def originalBody = getMatchingStrategyFromBody(request.body)?.clientValue def body = JsonToJsonPathsConverter.removeMatchingJsonPaths(originalBody, request.matchers) JsonPaths values = JsonToJsonPathsConverter.transformToJsonPathWithStubsSideValuesAndNoArraySizeCheck(body) - if (values.empty && !request.matchers?.hasMatchers()) { + if ((values.empty && !request.matchers?.hasMatchers()) || onlySizeAssertionsArePresent(values)) { requestPattern.withRequestBody(WireMock.equalToJson(JsonOutput.toJson(getMatchingStrategy(request.body.clientValue).clientValue), false, false)) } else { values.findAll{ !it.assertsSize() }.each { @@ -100,12 +100,15 @@ class WireMockRequestStubStrategy extends BaseWireMockStubStrategy { requestPattern.withRequestBody(convertToValuePattern(getMatchingStrategy(request.body.clientValue))) } } - + + private boolean onlySizeAssertionsArePresent(JsonPaths values) { + return !values.empty && !request.matchers?.hasMatchers() && values.every { it.assertsSize() } + } + private void appendMultipart(RequestPatternBuilder requestPattern) { if (!request.multipart) { return } - if (request.multipart.clientValue instanceof Map) { List multipartPatterns = (request.multipart.clientValue as Map).collect { (it.value instanceof NamedProperty diff --git a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/util/DelegatingJsonVerifiable.java b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/util/DelegatingJsonVerifiable.java index 0c5114e939..beba817b81 100644 --- a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/util/DelegatingJsonVerifiable.java +++ b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/util/DelegatingJsonVerifiable.java @@ -16,13 +16,13 @@ package org.springframework.cloud.contract.verifier.util; -import static org.apache.commons.lang3.StringEscapeUtils.escapeJava; - import java.util.LinkedList; import java.util.regex.Pattern; import com.toomuchcoding.jsonassert.JsonVerifiable; +import static org.apache.commons.lang3.StringEscapeUtils.escapeJava; + /** * Implementation of the {@link MethodBufferingJsonVerifiable} that contains a list * of String method commands that need to be executed to assert JSONs. @@ -187,6 +187,12 @@ class DelegatingJsonVerifiable implements MethodBufferingJsonVerifiable { return readyToCheck; } + @Override public MethodBufferingJsonVerifiable isEmpty() { + DelegatingJsonVerifiable readyToCheck = new FinishedDelegatingJsonVerifiable(this.delegate.isEmpty(), this.methodsBuffer); + readyToCheck.methodsBuffer.offer(".isEmpty()"); + return readyToCheck; + } + @Override public MethodBufferingJsonVerifiable matches(String value) { DelegatingJsonVerifiable readyToCheck = new FinishedDelegatingJsonVerifiable(this.delegate.matches(value), this.methodsBuffer); @@ -228,7 +234,7 @@ class DelegatingJsonVerifiable implements MethodBufferingJsonVerifiable { @Override public boolean assertsSize() { for (String s : this.methodsBuffer) { - if (s.contains(".hasSize(")) { + if (s.contains(".hasSize(") || s.contains(".isEmpty()")) { return true; } } 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 303f18051e..c60638859e 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 @@ -180,7 +180,7 @@ class JsonToJsonPathsConverter { traverseRecursively(Object, valueToAsserter(key.arrayField(), ContentUtils.returnParsedObject(it)), ContentUtils.returnParsedObject(it), closure) } - } else if (value instanceof List) { + } else if (value instanceof List && !value.empty) { MethodBufferingJsonVerifiable jsonPathVerifiable = createAsserterFromList(key, value) addSizeVerificationForListWithPrimitives(key, closure, value) value.each { def element -> @@ -188,7 +188,9 @@ class JsonToJsonPathsConverter { ContentUtils.returnParsedObject(element), closure) } return value - } else if (key.isIteratingOverArray()) { + } else if (value instanceof List && value.empty) { + return runClosure(closure, key, value) + } else if (key.isIteratingOverArray()) { traverseRecursively(Object, key.arrayField().contains(ContentUtils.returnParsedObject(value)), ContentUtils.returnParsedObject(value), closure) } @@ -288,6 +290,9 @@ class JsonToJsonPathsConverter { } private boolean listContainsOnlyPrimitives(List list) { + if (list.empty) { + return false + } return list.every { def element -> [String, Number, Boolean].any { it.isAssignableFrom(element.getClass()) @@ -308,15 +313,22 @@ class JsonToJsonPathsConverter { Object entrykey, value -> def convertedValue = ContentUtils.returnParsedObject(value) [entrykey, traverseRecursively(parentType, - convertedValue instanceof List ? listContainsOnlyPrimitives(convertedValue) ? - parentKey.arrayField(entrykey) : - parentKey.array(entrykey) : + convertedValue instanceof List ? list(convertedValue, entrykey, parentKey) : convertedValue instanceof Map ? parentKey.field(new ShouldTraverse(entrykey)) : valueToAsserter(parentKey.field(entrykey), convertedValue) , convertedValue, closureToExecute)] } } + protected MethodBufferingJsonVerifiable list(List convertedValue, Object entrykey, MethodBufferingJsonVerifiable parentKey) { + if (convertedValue.empty) { + return parentKey.array(entrykey).isEmpty() + } + return listContainsOnlyPrimitives(convertedValue) ? + parentKey.arrayField(entrykey) : + parentKey.array(entrykey) + } + private void traverseRecursivelyForKey(def json, MethodBufferingJsonVerifiable rootKey, Closure closure) { traverseRecursively(Map, rootKey, json, closure) } diff --git a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/util/MethodBufferingJsonVerifiable.java b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/util/MethodBufferingJsonVerifiable.java index 74ff5179b5..49e0c82982 100644 --- a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/util/MethodBufferingJsonVerifiable.java +++ b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/util/MethodBufferingJsonVerifiable.java @@ -59,6 +59,9 @@ public interface MethodBufferingJsonVerifiable @Override MethodBufferingJsonVerifiable isNull(); + @Override + MethodBufferingJsonVerifiable isEmpty(); + @Override MethodBufferingJsonVerifiable matches(String value); 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 d2f390092f..74e80123e3 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 @@ -2123,4 +2123,39 @@ World.'''""" "JaxRsClientSpockMethodRequestProcessingBodyBuilder" | { Contract dsl -> new JaxRsClientSpockMethodRequestProcessingBodyBuilder(dsl, properties) } | { Throwable t, OutputCapture capture -> t.message.contains("Cannot find matching method Script1#executedMethod") } "JaxRsClientJUnitMethodBodyBuilder" | { Contract dsl -> new JaxRsClientJUnitMethodBodyBuilder(dsl, properties) } | { Throwable t, OutputCapture capture -> t.message.contains("Truncated class file") && capture.toString().contains("path(executedMethod())") } } + + @Issue('#203') + def "should create an assertion for an empty list for [#methodBuilderName]"() { + given: + Contract contractDsl = Contract.make { + request { + method 'GET' + url '/api/v1/xxxx' + } + response { + status 200 + body([ + status: '200', + list: [], + foo: ["bar", "baz"] + ]) + } + } + MethodBodyBuilder builder = methodBuilder(contractDsl) + BlockBuilder blockBuilder = new BlockBuilder(" ") + and: + builder.appendTo(blockBuilder) + String test = blockBuilder.toString() + when: + SyntaxChecker.tryToCompile(methodBuilderName, test) + then: + test.contains('assertThatJson(parsedJson).array("list").isEmpty()') + !test.contains('assertThatJson(parsedJson).array("foo").isEmpty()') + 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) } + } }