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).
This commit is contained in:
@@ -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<Path>() {
|
||||
|
||||
private final Log log = LogFactory.getLog(StubRunnerFactory.class);
|
||||
|
||||
private final StubGeneratorProvider provider = new StubGeneratorProvider();
|
||||
|
||||
@Override
|
||||
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
|
||||
Collection<StubGenerator> 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(
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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"))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
Goodbye from file!
|
||||
@@ -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!")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user