Ensured that stub part of the body gets returned form WIreMock response, fixes gh-611
This commit is contained in:
@@ -33,7 +33,7 @@ import org.springframework.cloud.contract.verifier.util.ContentType
|
||||
import org.springframework.cloud.contract.verifier.util.ContentUtils
|
||||
import org.springframework.cloud.contract.verifier.util.MapConverter
|
||||
|
||||
import static ContentUtils.extractValue
|
||||
import static org.springframework.cloud.contract.verifier.util.ContentUtils.extractValue
|
||||
import static org.springframework.cloud.contract.verifier.util.MapConverter.transformValues
|
||||
/**
|
||||
* Common abstraction over WireMock Request / Response conversion implementations
|
||||
@@ -53,12 +53,12 @@ abstract class BaseWireMockStubStrategy {
|
||||
protected final Contract contract
|
||||
|
||||
protected BaseWireMockStubStrategy(Contract contract) {
|
||||
this.processor = processor()
|
||||
this.processor = templateProcessor()
|
||||
this.template = contractTemplate()
|
||||
this.contract = contract
|
||||
}
|
||||
|
||||
private TemplateProcessor processor() {
|
||||
private TemplateProcessor templateProcessor() {
|
||||
return new HandlebarsTemplateProcessor()
|
||||
}
|
||||
|
||||
@@ -145,7 +145,7 @@ abstract class BaseWireMockStubStrategy {
|
||||
* For the given {@link ContentType} returns the String version of the body
|
||||
*/
|
||||
String parseBody(GString value, ContentType contentType) {
|
||||
Object processedValue = extractValue(value, contentType, { Object o -> o instanceof DslProperty ? o.clientValue : o })
|
||||
Object processedValue = extractValue(value, contentType, { Object o -> o instanceof DslProperty ? o.clientValue : o })
|
||||
if (processedValue instanceof GString) {
|
||||
return parseBody(processedValue.toString(), contentType)
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ import org.springframework.cloud.contract.spec.Contract
|
||||
import org.springframework.cloud.contract.spec.internal.Request
|
||||
import org.springframework.cloud.contract.spec.internal.Response
|
||||
import org.springframework.cloud.contract.verifier.util.ContentType
|
||||
import org.springframework.cloud.contract.verifier.util.ContentUtils
|
||||
import org.springframework.cloud.contract.verifier.util.MapConverter
|
||||
import org.springframework.core.io.support.SpringFactoriesLoader
|
||||
|
||||
@@ -84,7 +85,7 @@ class WireMockResponseStubStrategy extends BaseWireMockStubStrategy {
|
||||
|
||||
private void appendBody(ResponseDefinitionBuilder builder) {
|
||||
if (response.body) {
|
||||
Object body = response.body.clientValue
|
||||
Object body = MapConverter.getStubSideValues(response.body)
|
||||
ContentType contentType = recognizeContentTypeFromHeader(response.headers)
|
||||
if (contentType == ContentType.UNKNOWN) {
|
||||
contentType = recognizeContentTypeFromContent(body)
|
||||
|
||||
@@ -21,6 +21,7 @@ import groovy.json.JsonOutput
|
||||
import groovy.json.JsonSlurper
|
||||
import groovy.transform.TypeChecked
|
||||
import groovy.util.logging.Slf4j
|
||||
import org.apache.commons.lang3.StringEscapeUtils
|
||||
import org.codehaus.groovy.runtime.GStringImpl
|
||||
import org.springframework.cloud.contract.spec.internal.DslProperty
|
||||
import org.springframework.cloud.contract.spec.internal.ExecutionProperty
|
||||
@@ -35,6 +36,7 @@ import java.util.regex.Pattern
|
||||
import static org.apache.commons.text.StringEscapeUtils.escapeJava
|
||||
import static org.apache.commons.text.StringEscapeUtils.escapeJson
|
||||
import static org.apache.commons.text.StringEscapeUtils.escapeXml11
|
||||
import static org.apache.commons.text.StringEscapeUtils.unescapeXml
|
||||
|
||||
/**
|
||||
* A utility class that can operate on a message body basing on the provided Content Type.
|
||||
@@ -208,7 +210,7 @@ class ContentUtils {
|
||||
}
|
||||
|
||||
private static String transformXMLStringValue(Object obj, Closure valueProvider) {
|
||||
return escapeXml11(obj.toString())
|
||||
return escapeXml11(unescapeXml(obj.toString()))
|
||||
}
|
||||
|
||||
private static String transformXMLStringValue(DslProperty dslProperty, Closure valueProvider) {
|
||||
|
||||
@@ -340,23 +340,13 @@ class DelegatingJsonVerifiable implements MethodBufferingJsonVerifiable {
|
||||
return true;
|
||||
if (o == null || getClass() != o.getClass())
|
||||
return false;
|
||||
|
||||
DelegatingJsonVerifiable that = (DelegatingJsonVerifiable) o;
|
||||
|
||||
if (this.delegate != null ? !this.delegate.equals(that.delegate) : that.delegate != null)
|
||||
return false;
|
||||
if (this.delegate == null) {
|
||||
return false;
|
||||
}
|
||||
if (this.delegate.jsonPath() == null && that.delegate.jsonPath() == null)
|
||||
return true;
|
||||
return this.delegate.jsonPath().equals(that.delegate.jsonPath());
|
||||
|
||||
return this.methodsBuffer.toString().equals(that.methodsBuffer.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = this.delegate != null ? this.delegate.jsonPath().hashCode() : 0;
|
||||
int result = this.methodsBuffer.toString().hashCode();
|
||||
return 31 * result;
|
||||
}
|
||||
|
||||
|
||||
@@ -274,7 +274,7 @@ class JsonToJsonPathsConverter {
|
||||
String systemPropValue = System.getProperty(SIZE_ASSERTION_SYSTEM_PROP)
|
||||
Boolean configPropValue = configProperties.assertJsonSize
|
||||
if ((systemPropValue != null && Boolean.parseBoolean(systemPropValue)) ||
|
||||
configPropValue) {
|
||||
configPropValue && listContainsOnlyPrimitives(value)) {
|
||||
addArraySizeCheck(key, value, closure)
|
||||
} else {
|
||||
if (log.isDebugEnabled()) {
|
||||
@@ -323,6 +323,10 @@ class JsonToJsonPathsConverter {
|
||||
return jsonPathVerifiable.matches((object as Pattern).pattern())
|
||||
}
|
||||
return jsonPathVerifiable.contains(object)
|
||||
} else if (element instanceof List) {
|
||||
if (listContainsOnlyPrimitives(element)) {
|
||||
return jsonPathVerifiable.array()
|
||||
}
|
||||
}
|
||||
return jsonPathVerifiable
|
||||
}
|
||||
@@ -367,6 +371,7 @@ class JsonToJsonPathsConverter {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isAnEntryWithLists(def value) {
|
||||
if (!(value instanceof Iterable)) {
|
||||
return false
|
||||
|
||||
@@ -1597,10 +1597,10 @@ World.'''"""
|
||||
builder.appendTo(blockBuilder)
|
||||
def test = blockBuilder.toString()
|
||||
then:
|
||||
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()')
|
||||
test.contains('assertThatJson(parsedJson).array().arrayField().isEqualTo("Boot").value()')
|
||||
test.contains('assertThatJson(parsedJson).array().array().arrayField().isEqualTo("Programming").value()')
|
||||
test.contains('assertThatJson(parsedJson).array().array().arrayField().isEqualTo("Java").value()')
|
||||
test.contains('assertThatJson(parsedJson).array().array().arrayField().isEqualTo("Spring").value()')
|
||||
test.contains('assertThatJson(parsedJson).array().array().arrayField().isEqualTo("Boot").value()')
|
||||
and:
|
||||
SyntaxChecker.tryToCompile(methodBuilderName, blockBuilder.toString())
|
||||
where:
|
||||
|
||||
@@ -20,6 +20,7 @@ import com.github.tomakehurst.wiremock.WireMockServer
|
||||
import com.github.tomakehurst.wiremock.core.WireMockConfiguration
|
||||
import com.github.tomakehurst.wiremock.extension.responsetemplating.ResponseTemplateTransformer
|
||||
import groovy.json.JsonBuilder
|
||||
import groovy.json.JsonOutput
|
||||
import groovy.json.JsonSlurper
|
||||
import org.springframework.boot.test.web.client.TestRestTemplate
|
||||
import org.springframework.cloud.contract.spec.Contract
|
||||
@@ -1842,6 +1843,76 @@ class WireMockGroovyDslSpec extends Specification implements WireMockStubVerifie
|
||||
server?.shutdown()
|
||||
}
|
||||
|
||||
@Issue('#266')
|
||||
def "should work with array of arrays"() {
|
||||
given:
|
||||
org.springframework.cloud.contract.spec.Contract groovyDsl = Contract.make {
|
||||
request {
|
||||
method 'POST'
|
||||
urlPath '/api/categories'
|
||||
body([["Programming", "Java"], ["Programming", "Java", "Spring", "Boot"]])
|
||||
headers {
|
||||
header('Content-Type': 'application/json;charset=UTF-8')
|
||||
}
|
||||
}
|
||||
response {
|
||||
status 200
|
||||
body([["Programming", "Java"], ["Programming", "Java", "Spring", "Boot"]])
|
||||
headers {
|
||||
header('Content-Type': 'application/json;charset=UTF-8')
|
||||
}
|
||||
}
|
||||
}
|
||||
when:
|
||||
def json = toWireMockClientJsonStub(groovyDsl)
|
||||
then:
|
||||
AssertionUtil.assertThatJsonsAreEqual(('''
|
||||
{
|
||||
"request" : {
|
||||
"urlPath" : "/api/categories",
|
||||
"method" : "POST",
|
||||
"headers" : {
|
||||
"Content-Type" : {
|
||||
"equalTo" : "application/json;charset=UTF-8"
|
||||
}
|
||||
},
|
||||
"bodyPatterns" : [ {
|
||||
"matchesJsonPath" : "$[*][?(@ == 'Spring')]"
|
||||
}, {
|
||||
"matchesJsonPath" : "$[*][?(@ == 'Boot')]"
|
||||
}, {
|
||||
"matchesJsonPath" : "$[*][?(@ == 'Programming')]"
|
||||
}, {
|
||||
"matchesJsonPath" : "$[*][?(@ == 'Java')]"
|
||||
} ]
|
||||
},
|
||||
"response" : {
|
||||
"status" : 200,
|
||||
"body" : "[\\"[\\\\\\"Programming\\\\\\",\\\\\\"Java\\\\\\"]\\",\\"[\\\\\\"Programming\\\\\\",\\\\\\"Java\\\\\\",\\\\\\"Spring\\\\\\",\\\\\\"Boot\\\\\\"]\\"]",
|
||||
"headers" : {
|
||||
"Content-Type" : "application/json;charset=UTF-8"
|
||||
},
|
||||
"transformers" : [ "response-template", "foo-transformer" ]
|
||||
}
|
||||
}
|
||||
'''), json)
|
||||
and:
|
||||
stubMappingIsValidWireMockStub(json)
|
||||
and:
|
||||
int port = SocketUtils.findAvailableTcpPort()
|
||||
WireMockServer server = new WireMockServer(config().port(port))
|
||||
server.start()
|
||||
server.addStubMapping(WireMockStubMapping.buildFrom(json))
|
||||
then:
|
||||
String entity = callApiCategories(port)
|
||||
and:
|
||||
AssertionUtil.assertThatJsonsAreEqual(('''
|
||||
["[\\"Programming\\",\\"Java\\"]","[\\"Programming\\",\\"Java\\",\\"Spring\\",\\"Boot\\"]"]
|
||||
'''), entity)
|
||||
cleanup:
|
||||
server?.shutdown()
|
||||
}
|
||||
|
||||
@Issue('#269')
|
||||
def "should create a stub for dot separated keys"() {
|
||||
given:
|
||||
@@ -2028,4 +2099,11 @@ class WireMockGroovyDslSpec extends Specification implements WireMockStubVerifie
|
||||
.header("Cookie", "foo=bar")
|
||||
.body("{\"foo\":\"bar\",\"baz\":5}"), String.class)
|
||||
}
|
||||
|
||||
String callApiCategories(int port) {
|
||||
return new TestRestTemplate().exchange(
|
||||
RequestEntity.post(URI.create("http://localhost:" + port + "/api/categories"))
|
||||
.header("Content-Type", "application/json;charset=UTF-8")
|
||||
.body(JsonOutput.toJson([["Programming", "Java"], ["Programming", "Java", "Spring", "Boot"]])), String.class).body
|
||||
}
|
||||
}
|
||||
@@ -673,20 +673,25 @@ class JsonToJsonPathsConverterSpec extends Specification {
|
||||
when:
|
||||
JsonPaths pathAndValues = new JsonToJsonPathsConverter().transformToJsonPathWithTestsSideValues(new JsonSlurper().parseText(json))
|
||||
then:
|
||||
DocumentContext context = JsonPath.parse(json)
|
||||
pathAndValues.each {
|
||||
assert context.read(it.jsonPath(), JSONArray)
|
||||
}
|
||||
and:
|
||||
pathAndValues.find {
|
||||
it.method()== """.array().field("['place']").field("['bounding_box']").array("['coordinates']").array().arrayField().isEqualTo(38.995548)""" &&
|
||||
it.method()== """.array().field("['place']").field("['bounding_box']").array("['coordinates']").array().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.method()== """.array().field("['place']").field("['bounding_box']").array("['coordinates']").array().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.method()== """.array().field("['place']").field("['bounding_box']").array("['coordinates']").array().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.method()== """.array().field("['place']").field("['bounding_box']").array("['coordinates']").array().array().arrayField().isEqualTo(38.791645)""" &&
|
||||
it.jsonPath() == """\$[*].['place'].['bounding_box'].['coordinates'][*][*][?(@ == 38.791645)]"""
|
||||
}
|
||||
and:
|
||||
@@ -700,8 +705,8 @@ class JsonToJsonPathsConverterSpec extends Specification {
|
||||
@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 = '''
|
||||
System.setProperty('spring.cloud.contract.verifier.assert.size', 'true')
|
||||
String json = '''
|
||||
[{
|
||||
"place":
|
||||
{
|
||||
@@ -717,38 +722,46 @@ class JsonToJsonPathsConverterSpec extends Specification {
|
||||
}]
|
||||
'''
|
||||
when:
|
||||
JsonPaths pathAndValues = new JsonToJsonPathsConverter().transformToJsonPathWithTestsSideValues(new JsonSlurper().parseText(json))
|
||||
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() == """\$"""
|
||||
}
|
||||
pathAndValues.find {
|
||||
it.method()== """.array().field("['place']").field("['bounding_box']").array("['coordinates']").array().hasSize(2)""" &&
|
||||
it.jsonPath() == """\$[*].['place'].['bounding_box'].['coordinates'][*][*]"""
|
||||
}
|
||||
pathAndValues.find {
|
||||
it.method()== """.array().field("['place']").field("['bounding_box']").array("['coordinates']").hasSize(1)""" &&
|
||||
it.jsonPath() == """\$[*].['place'].['bounding_box'].['coordinates'][*]"""
|
||||
}
|
||||
DocumentContext context = JsonPath.parse(json)
|
||||
pathAndValues.each {
|
||||
assert context.read(it.jsonPath(), JSONArray)
|
||||
}
|
||||
pathAndValues.find {
|
||||
it.method() == """.hasSize(1)""" &&
|
||||
it.jsonPath() == """\$"""
|
||||
}
|
||||
pathAndValues.find {
|
||||
it.method() == """.array().field("['place']").field("['bounding_box']").array("['coordinates']").array().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().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']").hasSize(1)""" &&
|
||||
it.jsonPath() == """\$[*].['place'].['bounding_box'].['coordinates'][*]"""
|
||||
}
|
||||
pathAndValues.find {
|
||||
it.method() == """.array().field("['place']").field("['bounding_box']").array("['coordinates']").array().hasSize(2)""" &&
|
||||
it.jsonPath() == """\$[*].['place'].['bounding_box'].['coordinates'][*][*]"""
|
||||
}
|
||||
pathAndValues.find {
|
||||
it.method() == """.array().field("['place']").field("['bounding_box']").array("['coordinates']").array().array().arrayField().isEqualTo(38.791645)""" &&
|
||||
it.jsonPath() == """\$[*].['place'].['bounding_box'].['coordinates'][*][*][?(@ == 38.791645)]"""
|
||||
}
|
||||
pathAndValues.find {
|
||||
it.method() == """.array().field("['place']").field("['bounding_box']").array("['coordinates']").array().array().hasSize(2)""" &&
|
||||
it.jsonPath() == """\$[*].['place'].['bounding_box'].['coordinates'][*][*]"""
|
||||
}
|
||||
pathAndValues.find {
|
||||
it.method() == """.array().field("['place']").field("['bounding_box']").array("['coordinates']").array().array().arrayField().isEqualTo(-76.909393)""" &&
|
||||
it.jsonPath() == """\$[*].['place'].['bounding_box'].['coordinates'][*][*][?(@ == -76.909393)]"""
|
||||
}
|
||||
and:
|
||||
pathAndValues.size() == 7
|
||||
pathAndValues.size() == 8
|
||||
and:
|
||||
pathAndValues.each {
|
||||
JsonAssertion.assertThat(json).matchesJsonPath(it.jsonPath())
|
||||
|
||||
Reference in New Issue
Block a user