From 6d44c54c8010b8bbb68d819359bafe3146eba878 Mon Sep 17 00:00:00 2001 From: Dominik Meister Date: Fri, 30 Oct 2020 09:32:25 +0100 Subject: [PATCH] Don't delete additional files before generating stubs on the fly (#1544) This behaviour did break contracts which include request or response bodies from separate files (via the `body(file(...))` directives). --- .../stubrunner/StubRunnerFactory.java | 44 +------------------ .../contract/stubrunner/StubRunnerSpec.groovy | 35 +++++++++++++-- .../wiremock/TestWireMockExtensions.groovy | 8 ++-- .../WireMockHttpServerStubSpec.groovy | 6 ++- .../contracts/goodbye_from_file.groovy | 28 ++++++++++++ .../contracts/goodbye_response.txt | 1 + .../generateStubs/contracts/hello.groovy | 28 ++++++++++++ .../{mappings => contracts}/hello.json | 0 8 files changed, 100 insertions(+), 50 deletions(-) create mode 100644 spring-cloud-contract-stub-runner/src/test/resources/generateStubs/contracts/goodbye_from_file.groovy create mode 100644 spring-cloud-contract-stub-runner/src/test/resources/generateStubs/contracts/goodbye_response.txt create mode 100644 spring-cloud-contract-stub-runner/src/test/resources/generateStubs/contracts/hello.groovy rename spring-cloud-contract-stub-runner/src/test/resources/generateStubs/{mappings => contracts}/hello.json (100%) diff --git a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubRunnerFactory.java b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubRunnerFactory.java index f0e1cf9f81..32ec7c0244 100644 --- a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubRunnerFactory.java +++ b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubRunnerFactory.java @@ -18,12 +18,8 @@ package org.springframework.cloud.contract.stubrunner; import java.io.File; import java.io.IOException; -import java.nio.file.FileVisitResult; -import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; -import java.nio.file.SimpleFileVisitor; -import java.nio.file.attribute.BasicFileAttributes; import java.util.ArrayList; import java.util.Collection; import java.util.Map; @@ -32,8 +28,6 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.cloud.contract.verifier.converter.RecursiveFilesConverter; -import org.springframework.cloud.contract.verifier.converter.StubGenerator; -import org.springframework.cloud.contract.verifier.converter.StubGeneratorProvider; import org.springframework.cloud.contract.verifier.messaging.MessageVerifier; import org.springframework.core.io.Resource; @@ -94,12 +88,12 @@ class StubRunnerFactory { } private void generateMappingsAtRuntime(Path path) { - removeCurrentMappings(path); generateNewMappings(path); } private Path resolvePath(File unpackedLocation) { - Resource resource = ResourceResolver.resource(unpackedLocation.toURI().toString()); + Resource resource = ResourceResolver + .resource(unpackedLocation.toURI().toString()); Path path = unpackedLocation.toPath(); if (resource != null) { try { @@ -112,40 +106,6 @@ class StubRunnerFactory { return path; } - private void removeCurrentMappings(Path path) { - try { - Files.walkFileTree(path, new SimpleFileVisitor() { - - private final Log log = LogFactory.getLog(StubRunnerFactory.class); - - private final StubGeneratorProvider provider = new StubGeneratorProvider(); - - @Override - public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) { - Collection stubGenerators = this.provider - .converterForName(file.toString()); - if (!stubGenerators.isEmpty()) { - if (log.isDebugEnabled()) { - log.debug("Deleting file [" + file.toString() - + "] since at least one stub generator would run it"); - } - try { - Files.delete(file); - } - catch (IOException ex) { - log.warn("Failed to delete file [" + file.toString() + "]", - ex); - } - } - return FileVisitResult.CONTINUE; - } - }); - } - catch (IOException ex) { - log.warn("Exception occurred while trying to delete mappings", ex); - } - } - private void generateNewMappings(Path path) { File unpackedLocation = path.toFile(); RecursiveFilesConverter converter = new RecursiveFilesConverter( diff --git a/spring-cloud-contract-stub-runner/src/test/groovy/org/springframework/cloud/contract/stubrunner/StubRunnerSpec.groovy b/spring-cloud-contract-stub-runner/src/test/groovy/org/springframework/cloud/contract/stubrunner/StubRunnerSpec.groovy index d583f78907..b2fa019598 100644 --- a/spring-cloud-contract-stub-runner/src/test/groovy/org/springframework/cloud/contract/stubrunner/StubRunnerSpec.groovy +++ b/spring-cloud-contract-stub-runner/src/test/groovy/org/springframework/cloud/contract/stubrunner/StubRunnerSpec.groovy @@ -61,18 +61,45 @@ class StubRunnerSpec extends Specification { def 'should generate stubs at runtime'() { given: Arguments args = argumentsWithGenerateStubs() - StubDownloader downloader = new FileStubDownloader().build(args.stubRunnerOptions); + StubDownloader downloader = new FileStubDownloader().build(args.stubRunnerOptions) StubRunner runner = new StubRunnerFactory(args.stubRunnerOptions, downloader, new NoOpStubMessages()).createStubsFromServiceConfiguration().first() when: runner.runStubs() then: URL url = runner.findStubUrl("groupId2", "artifactId2") - new URL(url.toString() + "/goodbye").text + "Goodbye World!" == new URL(url.toString() + "/goodbye").text + cleanup: + runner.close() + } + + def 'should override existing mappings when generating stubs at runtime'() { + given: + Arguments args = argumentsWithGenerateStubs() + StubDownloader downloader = new FileStubDownloader().build(args.stubRunnerOptions) + StubRunner runner = new StubRunnerFactory(args.stubRunnerOptions, + downloader, new NoOpStubMessages()).createStubsFromServiceConfiguration().first() when: - new URL(url.toString() + "/hello").text + runner.runStubs() then: - thrown(FileNotFoundException) + URL url = runner.findStubUrl("groupId2", "artifactId2") + // don't return the response defined in hello.json, but the one defined in the contract + "Hello New World!" == new URL(url.toString() + "/hello").text + cleanup: + runner.close() + } + + def 'should handle contracts with body contents loaded from external file when generating stubs at runtime'() { + given: + Arguments args = argumentsWithGenerateStubs() + StubDownloader downloader = new FileStubDownloader().build(args.stubRunnerOptions) + StubRunner runner = new StubRunnerFactory(args.stubRunnerOptions, + downloader, new NoOpStubMessages()).createStubsFromServiceConfiguration().first() + when: + runner.runStubs() + then: + URL url = runner.findStubUrl("groupId2", "artifactId2") + "Goodbye from file!" == new URL(url.toString() + "/goodbye_from_file").text cleanup: runner.close() } diff --git a/spring-cloud-contract-stub-runner/src/test/groovy/org/springframework/cloud/contract/stubrunner/provider/wiremock/TestWireMockExtensions.groovy b/spring-cloud-contract-stub-runner/src/test/groovy/org/springframework/cloud/contract/stubrunner/provider/wiremock/TestWireMockExtensions.groovy index 91b1a5293e..bb69a4b1fc 100644 --- a/spring-cloud-contract-stub-runner/src/test/groovy/org/springframework/cloud/contract/stubrunner/provider/wiremock/TestWireMockExtensions.groovy +++ b/spring-cloud-contract-stub-runner/src/test/groovy/org/springframework/cloud/contract/stubrunner/provider/wiremock/TestWireMockExtensions.groovy @@ -21,8 +21,10 @@ import com.github.tomakehurst.wiremock.extension.Extension import com.github.tomakehurst.wiremock.extension.Parameters import com.github.tomakehurst.wiremock.extension.ResponseTransformer import com.github.tomakehurst.wiremock.http.ChunkedDribbleDelay +import com.github.tomakehurst.wiremock.http.HttpHeader import com.github.tomakehurst.wiremock.http.Request import com.github.tomakehurst.wiremock.http.Response +import wiremock.org.apache.http.HttpHeaders import org.springframework.cloud.contract.verifier.dsl.wiremock.DefaultResponseTransformer import org.springframework.cloud.contract.verifier.dsl.wiremock.WireMockExtensions @@ -52,13 +54,13 @@ class CustomExtension extends ResponseTransformer { } /** - * Transformer returns the "surprise!" body regardless of what you - * the stub mapping returns + * Transformer adds a X-My-Header with value "surprise!" to the response */ @Override Response transform(Request request, Response response, FileSource files, Parameters parameters) { + def headers = response.headers + new HttpHeader("X-My-Header", "surprise!") return new Response(response.status, response.statusMessage, - "surprise!", response.headers, response.wasConfigured(), response.fault, + response.body, headers, response.wasConfigured(), response.fault, response.initialDelay, new ChunkedDribbleDelay(0, 0), response.fromProxy) } diff --git a/spring-cloud-contract-stub-runner/src/test/groovy/org/springframework/cloud/contract/stubrunner/provider/wiremock/WireMockHttpServerStubSpec.groovy b/spring-cloud-contract-stub-runner/src/test/groovy/org/springframework/cloud/contract/stubrunner/provider/wiremock/WireMockHttpServerStubSpec.groovy index e73b68ca8d..3a75c3cc32 100644 --- a/spring-cloud-contract-stub-runner/src/test/groovy/org/springframework/cloud/contract/stubrunner/provider/wiremock/WireMockHttpServerStubSpec.groovy +++ b/spring-cloud-contract-stub-runner/src/test/groovy/org/springframework/cloud/contract/stubrunner/provider/wiremock/WireMockHttpServerStubSpec.groovy @@ -23,6 +23,8 @@ import spock.lang.Specification import org.springframework.boot.test.system.OutputCaptureRule import org.springframework.boot.test.web.client.TestRestTemplate +import org.springframework.http.HttpEntity +import org.springframework.http.HttpMethod import org.springframework.web.client.RestTemplate class WireMockHttpServerStubSpec extends Specification { @@ -50,7 +52,9 @@ class WireMockHttpServerStubSpec extends Specification { then: noExceptionThrown() expect: - "surprise!" == new RestTemplate().getForObject("http://localhost:" + mappingDescriptor.port() + "/ping", String.class) + URI uri = new URI("http://localhost:" + mappingDescriptor.port() + "/ping") + "surprise!" == new RestTemplate().exchange(uri, HttpMethod.GET, (HttpEntity)null, String.class) + .getHeaders().getFirst("X-My-Header") cleanup: mappingDescriptor?.stop() } diff --git a/spring-cloud-contract-stub-runner/src/test/resources/generateStubs/contracts/goodbye_from_file.groovy b/spring-cloud-contract-stub-runner/src/test/resources/generateStubs/contracts/goodbye_from_file.groovy new file mode 100644 index 0000000000..67c5325eed --- /dev/null +++ b/spring-cloud-contract-stub-runner/src/test/resources/generateStubs/contracts/goodbye_from_file.groovy @@ -0,0 +1,28 @@ +/* + * Copyright 2013-2020 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import org.springframework.cloud.contract.spec.Contract + +Contract.make { + request { + url("/goodbye_from_file") + method(GET()) + } + response { + status(OK()) + body(file("goodbye_response.txt")) + } +} diff --git a/spring-cloud-contract-stub-runner/src/test/resources/generateStubs/contracts/goodbye_response.txt b/spring-cloud-contract-stub-runner/src/test/resources/generateStubs/contracts/goodbye_response.txt new file mode 100644 index 0000000000..02e35ff340 --- /dev/null +++ b/spring-cloud-contract-stub-runner/src/test/resources/generateStubs/contracts/goodbye_response.txt @@ -0,0 +1 @@ +Goodbye from file! \ No newline at end of file diff --git a/spring-cloud-contract-stub-runner/src/test/resources/generateStubs/contracts/hello.groovy b/spring-cloud-contract-stub-runner/src/test/resources/generateStubs/contracts/hello.groovy new file mode 100644 index 0000000000..cb971a69ee --- /dev/null +++ b/spring-cloud-contract-stub-runner/src/test/resources/generateStubs/contracts/hello.groovy @@ -0,0 +1,28 @@ +/* + * Copyright 2013-2020 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import org.springframework.cloud.contract.spec.Contract + +Contract.make { + request { + url("/hello") + method(GET()) + } + response { + status(OK()) + body("Hello New World!") + } +} diff --git a/spring-cloud-contract-stub-runner/src/test/resources/generateStubs/mappings/hello.json b/spring-cloud-contract-stub-runner/src/test/resources/generateStubs/contracts/hello.json similarity index 100% rename from spring-cloud-contract-stub-runner/src/test/resources/generateStubs/mappings/hello.json rename to spring-cloud-contract-stub-runner/src/test/resources/generateStubs/contracts/hello.json