diff --git a/spring-cloud-contract-verifier/src/main/java/org/springframework/cloud/contract/verifier/dsl/wiremock/WireMockResponseStubStrategy.java b/spring-cloud-contract-verifier/src/main/java/org/springframework/cloud/contract/verifier/dsl/wiremock/WireMockResponseStubStrategy.java index 1e0b16c50c..5952e185d2 100755 --- a/spring-cloud-contract-verifier/src/main/java/org/springframework/cloud/contract/verifier/dsl/wiremock/WireMockResponseStubStrategy.java +++ b/spring-cloud-contract-verifier/src/main/java/org/springframework/cloud/contract/verifier/dsl/wiremock/WireMockResponseStubStrategy.java @@ -25,6 +25,7 @@ import com.github.tomakehurst.wiremock.extension.Extension; import com.github.tomakehurst.wiremock.http.HttpHeader; import com.github.tomakehurst.wiremock.http.HttpHeaders; import com.github.tomakehurst.wiremock.http.ResponseDefinition; +import groovy.lang.Closure; import groovy.lang.GString; import org.springframework.cloud.contract.spec.Contract; @@ -52,10 +53,13 @@ class WireMockResponseStubStrategy extends BaseWireMockStubStrategy { private final ContentType contentType; + private final SingleContractMetadata contractMetadata; + WireMockResponseStubStrategy(Contract groovyDsl, SingleContractMetadata singleContractMetadata) { super(groovyDsl); this.response = groovyDsl.getResponse(); this.contentType = contentType(singleContractMetadata); + this.contractMetadata = singleContractMetadata; } protected ContentType contentType(SingleContractMetadata singleContractMetadata) { @@ -96,7 +100,7 @@ class WireMockResponseStubStrategy extends BaseWireMockStubStrategy { private void appendBody(ResponseDefinitionBuilder builder) { if (response.getBody() != null) { - Object body = MapConverter.getStubSideValues(response.getBody()); + Object body = MapConverter.getStubSideValues(response.getBody(), parsingClosureForContentType()); if (body instanceof byte[]) { builder.withBody((byte[]) body); } @@ -118,6 +122,10 @@ class WireMockResponseStubStrategy extends BaseWireMockStubStrategy { } } + Closure parsingClosureForContentType() { + return contractMetadata.getDefinedOutputTestContentType().contains("/stream") ? Closure.IDENTITY : MapConverter.JSON_PARSING_CLOSURE; + } + private void appendResponseDelayTime(ResponseDefinitionBuilder builder) { // TODO: Add a missing test for this if (response.getDelay() != null) { diff --git a/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/dsl/wiremock/WireMockGroovyDslSpec.groovy b/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/dsl/wiremock/WireMockGroovyDslSpec.groovy index 95d08fa9e2..f2c7fc4f65 100755 --- a/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/dsl/wiremock/WireMockGroovyDslSpec.groovy +++ b/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/dsl/wiremock/WireMockGroovyDslSpec.groovy @@ -2903,6 +2903,66 @@ class WireMockGroovyDslSpec extends Specification implements WireMockStubVerifie } + @Issue("#1535") + def "should work with streams"() { + given: + Contract contractDsl = Contract.make { + description "should return all entities" + request{ + method GET() + url("/api/v1/entities") + } + response { + headers { + header 'Content-Type': 'application/stream+json' + } + body( + ''' + { + "id" : "1", + "name" : "Entity1", + "nested_data" : { + "key1" : "value1" + } + }, + { + "id" : "2", + "name" : "Entity2", + "nested_data" : { + "key1" : "value1" + } + } + ''' + ) + status 200 + } + } + + when: + String wireMockStub = new WireMockStubStrategy("Test", + new ContractMetadata(null, false, 0, null, contractDsl), contractDsl) + .toWireMockClientStub() + then: + wireMockStub.contains('Entity1') + wireMockStub.contains('Entity2') + stubMappingIsValidWireMockStub(wireMockStub) + and: + int port = SocketUtils.findAvailableTcpPort() + WireMockServer server = new WireMockServer(config().port(port)) + server.start() + and: + stubMappingIsValidWireMockStub(wireMockStub) + server.addStubMapping(WireMockStubMapping.buildFrom(wireMockStub)) + when: + ResponseEntity entity = callForStream(port) + then: + entity.statusCodeValue == 200 + entity.body.contains("Entity1") + entity.body.contains("Entity2") + cleanup: + server?.shutdown() + } + @Issue("#1125") def "should not generate assertions for [*] when all manual entries were passed"() { given: @@ -3029,6 +3089,12 @@ class WireMockGroovyDslSpec extends Specification implements WireMockStubVerifie .body("{\"foo\":null,\"name\":\"\"}"), String.class) } + ResponseEntity callForStream(int port) { + return new TestRestTemplate().exchange( + RequestEntity.get(URI.create("http://localhost:" + port + "/api/v1/entities")) + .header("Content-Type", "application/stream+json").build(), String.class) + } + ResponseEntity callBytes(int port, File request) { return new TestRestTemplate().exchange( RequestEntity.put(URI.create("http://localhost:" + port + "/1")) diff --git a/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/dsl/wiremock/WireMockResponseStubStrategySpec.groovy b/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/dsl/wiremock/WireMockResponseStubStrategySpec.groovy index 5f77f538c6..fac202330e 100644 --- a/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/dsl/wiremock/WireMockResponseStubStrategySpec.groovy +++ b/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/dsl/wiremock/WireMockResponseStubStrategySpec.groovy @@ -23,6 +23,7 @@ import spock.lang.Specification import org.springframework.cloud.contract.spec.Contract import org.springframework.cloud.contract.verifier.file.SingleContractMetadata import org.springframework.cloud.contract.verifier.util.ContentType +import org.springframework.cloud.contract.verifier.util.MapConverter class WireMockResponseStubStrategySpec extends Specification { @@ -247,6 +248,11 @@ class WireMockResponseStubStrategySpec extends Specification { protected ContentType contentType(SingleContractMetadata singleContractMetadata) { return ContentType.JSON } + + @Override + Closure parsingClosureForContentType() { + return MapConverter.JSON_PARSING_CLOSURE + } } response.buildClientResponseContent() then: