Fixed the way we clone bodies in response

without this change we are not doing a deep copy of a collection thus we're accidentaly removing the original value
with this change we ensure that a deep copy is made

fixes #229
This commit is contained in:
Marcin Grzejszczak
2017-02-22 13:25:00 +01:00
parent 60e35be082
commit 19120f55fc
5 changed files with 65 additions and 5 deletions

View File

@@ -24,7 +24,7 @@ import groovy.transform.CompileStatic
* @since 1.0.0
*/
@CompileStatic
class ExecutionProperty {
class ExecutionProperty implements Serializable {
private static final String PLACEHOLDER_VALUE = '$it'

View File

@@ -24,7 +24,7 @@ import groovy.transform.CompileStatic
* @since 1.0.0
*/
@CompileStatic
class OptionalProperty {
class OptionalProperty implements Serializable {
final Object value
OptionalProperty(Object value) {

View File

@@ -22,12 +22,22 @@ import groovy.json.JsonOutput
import groovy.transform.PackageScope
import groovy.transform.TypeChecked
import org.apache.commons.lang3.StringEscapeUtils
import org.springframework.cloud.contract.spec.internal.*
import org.springframework.cloud.contract.spec.internal.BodyMatcher
import org.springframework.cloud.contract.spec.internal.BodyMatchers
import org.springframework.cloud.contract.spec.internal.DslProperty
import org.springframework.cloud.contract.spec.internal.ExecutionProperty
import org.springframework.cloud.contract.spec.internal.Header
import org.springframework.cloud.contract.spec.internal.MatchingStrategy
import org.springframework.cloud.contract.spec.internal.MatchingType
import org.springframework.cloud.contract.spec.internal.NamedProperty
import org.springframework.cloud.contract.spec.internal.OptionalProperty
import org.springframework.cloud.contract.spec.internal.QueryParameter
import org.springframework.cloud.contract.verifier.config.ContractVerifierConfigProperties
import org.springframework.cloud.contract.verifier.util.ContentType
import org.springframework.cloud.contract.verifier.util.JsonPaths
import org.springframework.cloud.contract.verifier.util.JsonToJsonPathsConverter
import org.springframework.cloud.contract.verifier.util.MapConverter
import org.springframework.util.SerializationUtils
import java.util.regex.Pattern
@@ -386,7 +396,12 @@ abstract class MethodBodyBuilder {
bb.endBlock().endBlock()
}
// Doing a clone doesn't work for nested lists...
private Object cloneBody(Object object) {
if (object instanceof List) {
byte[] serializedObject = SerializationUtils.serialize(object)
return SerializationUtils.deserialize(serializedObject)
}
try {
return object.clone()
} catch (CloneNotSupportedException e) {
@@ -405,7 +420,7 @@ abstract class MethodBodyBuilder {
try {
return JsonPath.parse(body).read(path)
} catch (PathNotFoundException e) {
throw new IllegalStateException("Entry for the provided JSON path [${path}] doesn't exist in the body [${JsonOutput.toJson(body)}]", e)
throw new IllegalStateException("Entry for the provided JSON path <${path}> doesn't exist in the body <${JsonOutput.toJson(body)}>", e)
}
}

View File

@@ -25,6 +25,7 @@ import groovy.json.JsonSlurper
import groovy.util.logging.Slf4j
import org.springframework.cloud.contract.spec.internal.*
import org.springframework.cloud.contract.verifier.config.ContractVerifierConfigProperties
import org.springframework.util.SerializationUtils
import java.util.regex.Pattern
@@ -86,7 +87,12 @@ class JsonToJsonPathsConverter {
return jsonCopy
}
// Doing a clone doesn't work for nested lists...
private static Object cloneBody(Object object) {
if (object instanceof List) {
byte[] serializedObject = SerializationUtils.serialize(object)
return SerializationUtils.deserialize(serializedObject)
}
try {
return object.clone()
} catch (CloneNotSupportedException e) {

View File

@@ -353,7 +353,46 @@ class MockMvcMethodBodyBuilderWithMatchersSpec extends Specification implements
builder.appendTo(blockBuilder)
then:
IllegalStateException e = thrown(IllegalStateException)
e.message.contains("Entry for the provided JSON path [\$.nonExistingPhoneNumbers[*].number] doesn't exist in the body")
e.message.contains("Entry for the provided JSON path <\$.nonExistingPhoneNumbers[*].number> doesn't exist in the body")
where:
methodBuilderName | methodBuilder | rootElement
"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) } | '$'
}
@Issue("#229")
def "should work for matchers and body with json array[#methodBuilderName]"() {
given:
Contract contractDsl = Contract.make {
request {
method 'GET'
url '/api/v1/xxxx'
body(12000)
}
response {
status 200
body ([[
[ access_token: '123']
]])
headers {
contentType(applicationJson())
}
testMatchers {
jsonPath('''$[0][0].access_token''', byEquality())
}
}
}
MethodBodyBuilder builder = methodBuilder(contractDsl)
BlockBuilder blockBuilder = new BlockBuilder(" ")
and:
builder.appendTo(blockBuilder)
String test = blockBuilder.toString()
when:
SyntaxChecker.tryToCompile(methodBuilderName, test)
then:
test.contains('assertThat(parsedJson.read("' + rootElement + '[0][0].access_token", String.class)).isEqualTo("123")')
where:
methodBuilderName | methodBuilder | rootElement
"MockMvcSpockMethodBuilder" | { Contract dsl -> new MockMvcSpockMethodRequestProcessingBodyBuilder(dsl, properties) } | '\\$'