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 b868ab161a..1bfe6bf92c 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 @@ -38,6 +38,7 @@ import java.util.regex.Pattern * JSON Paths together with methods needed to be called to build them. * * @author Marcin Grzejszczak + * @author Tim Ysewyn */ @Slf4j class JsonToJsonPathsConverter { @@ -232,8 +233,10 @@ class JsonToJsonPathsConverter { return convertWithKey(List, key, value as Map, closure) } else if (isAnEntryWithoutNestedStructures(value)) { return convertWithKey(List, key, value as Map, closure) - } else if (value instanceof Map) { + } else if (value instanceof Map && !value.isEmpty()) { return convertWithKey(Map, key, value as Map, closure) + } else if (value instanceof Map && value.isEmpty()) { + return runClosure(closure, key.isEmpty(), value) // JSON with a list of primitives ["a", "b", "c"] in root issue #266 } else if (key.isIteratingOverNamelessArray() && value instanceof List && listContainsOnlyPrimitives(value)) { addSizeVerificationForListWithPrimitives(key, closure, value) @@ -356,6 +359,9 @@ class JsonToJsonPathsConverter { return false } Map valueAsMap = ((Map) value) + if (valueAsMap.isEmpty()) { + return false + } return valueAsMap.entrySet().every { Map.Entry entry -> [String, Number, Boolean].any { it.isAssignableFrom(entry.value.getClass()) } } 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 cff968bd6a..227a0b5a8f 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 @@ -2562,4 +2562,71 @@ DocumentContext parsedJson = JsonPath.parse(json); SyntaxChecker.tryToCompile("MockMvcSpockMethodRequestProcessingBodyBuilder", blockBuilder.toString()) } + @Issue("#628") + def "should execute method in response header [#methodBuilderName]"() { + given: + Contract contractDsl = Contract.make { + request { + method 'GET' + urlPath '/whatever' + headers { + header 'My-UUID': value(test(execute('property("my-uuid")')), stub('76c53386-ad9b-11e6-92dc-0370ae47c3b2')) + } + } + response { + status 200 + headers { + header 'My-UUID': value(test(execute('property("my-uuid")')), stub('76c53386-ad9b-11e6-92dc-0370ae47c3b2')) + } + } + } + MethodBodyBuilder builder = methodBuilder(contractDsl) + BlockBuilder blockBuilder = new BlockBuilder(" ") + when: + builder.appendTo(blockBuilder) + String test = blockBuilder.toString() + then: + responseAssertion(test) + where: + methodBuilderName | methodBuilder | responseAssertion + "MockMvcSpockMethodBuilder" | { Contract dsl -> new MockMvcSpockMethodRequestProcessingBodyBuilder(dsl, properties) } | { String body -> body.contains("response.header('My-UUID') == property(\"my-uuid\")") } + "MockMvcJUnitMethodBuilder" | { Contract dsl -> new MockMvcJUnitMethodBodyBuilder(dsl, properties) } | { String body -> body.contains('assertThat(response.header("My-UUID")).isEqualTo(property("my-uuid"));') } + "JaxRsClientSpockMethodRequestProcessingBodyBuilder" | { Contract dsl -> new JaxRsClientSpockMethodRequestProcessingBodyBuilder(dsl, properties) } | { String body -> body.contains("response.getHeaderString('My-UUID') == property(\"my-uuid\")") } + "JaxRsClientJUnitMethodBodyBuilder" | { Contract dsl -> new JaxRsClientJUnitMethodBodyBuilder(dsl, properties) } | { String body -> body.contains('assertThat(response.getHeaderString("My-UUID")).isEqualTo(property("my-uuid"));') } + } + + @Issue('#554') + def "should create an assertion for an empty map or Object for [#methodBuilderName]"() { + given: + Contract contractDsl = Contract.make { + request { + method 'GET' + url '/api/v1/xxxx' + } + response { + status 200 + body([ + aMap: ["foo": "bar"], + anEmptyMap: [:] + ]) + } + } + MethodBodyBuilder builder = methodBuilder(contractDsl) + BlockBuilder blockBuilder = new BlockBuilder(" ") + and: + builder.appendTo(blockBuilder) + String test = blockBuilder.toString() + when: + SyntaxChecker.tryToRun(methodBuilderName, test.join("\n")) + then: + test.contains('''assertThatJson(parsedJson).field("['aMap']").field("['foo']").isEqualTo("bar")''') + test.contains('''assertThatJson(parsedJson).field("['anEmptyMap']").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) } + } + } 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 06cc215abc..b4661572fb 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 @@ -842,6 +842,48 @@ class JsonToJsonPathsConverterSpec extends Specification { e.message.contains("not found") } + def "should generate assertion for empty map"() { + given: + Map json = [ + aMap: ["foo": "bar"], + anEmptyMap: [:] + ] + when: + JsonPaths pathAndValues = new JsonToJsonPathsConverter().transformToJsonPathWithTestsSideValues(json) + then: + pathAndValues.find { + it.method() == """.field("['aMap']").field("['foo']").isEqualTo("bar")""" && + it.jsonPath() == """\$.['aMap'][?(@.['foo'] == 'bar')]""" + } + pathAndValues.find { + it.method()== """.field("['anEmptyMap']").isEmpty()""" && + it.jsonPath() == """\$.['anEmptyMap']""" + } + and: + pathAndValues.size() == 2 + } + + def "should generate assertion for empty object"() { + given: + String json = """{ + "aMap": {"foo": "bar"}, + "anEmptyMap": {} + }""" + when: + JsonPaths pathAndValues = new JsonToJsonPathsConverter().transformToJsonPathWithTestsSideValues(new JsonSlurper().parseText(json)) + then: + pathAndValues.find { + it.method()== """.field("['aMap']").field("['foo']").isEqualTo("bar")""" && + it.jsonPath() == """\$.['aMap'][?(@.['foo'] == 'bar')]""" + } + pathAndValues.find { + it.method() == """.field("['anEmptyMap']").isEmpty()""" && + it.jsonPath() == """\$.['anEmptyMap']""" + } + and: + pathAndValues.size() == 2 + } + private BodyMatcher matcher(final MatchingType matchingType, final String jsonPath, final Object value) { return new BodyMatcher() { @Override