diff --git a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/HttpServerStub.java b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/HttpServerStub.java index 9249607fc5..dcaa2cda97 100644 --- a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/HttpServerStub.java +++ b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/HttpServerStub.java @@ -1,14 +1,50 @@ package org.springframework.cloud.contract.stubrunner; +import java.io.File; +import java.util.Collection; + /** * Describes an HTTP Server Stub * * @author Marcin Grzejszczak - * @since 1.0.0 + * @since 1.1.0 */ -interface HttpServerStub { +public interface HttpServerStub { + /** + * Port on which the server is running + */ int port(); + + /** + * Returns {@code true} if the server is running + */ boolean isRunning(); - void start(); - void stop(); + + /** + * Starts the server on a random port. Should return itself + * to allow chaining. + */ + HttpServerStub start(); + + /** + * Starts the server on a given port. Should return itself + * to allow chaining. + */ + HttpServerStub start(int port); + + /** + * Stops the server. Should return itself to allow chaining. + */ + HttpServerStub stop(); + + /** + * Registers the stub files in the HTTP server stub. Should return itself + * to allow chaining. + */ + HttpServerStub registerMappings(Collection stubFiles); + + /** + * Returns {@code true} if the file is a valid stub mapping + */ + boolean isAccepted(File file); } diff --git a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/NoOpHttpServerStub.java b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/NoOpHttpServerStub.java index 47dea53816..fa08ffdfc7 100644 --- a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/NoOpHttpServerStub.java +++ b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/NoOpHttpServerStub.java @@ -1,5 +1,8 @@ package org.springframework.cloud.contract.stubrunner; +import java.io.File; +import java.util.Collection; + /** * @author Marcin Grzejszczak */ @@ -15,12 +18,26 @@ class NoOpHttpServerStub implements HttpServerStub { } @Override - public void start() { - + public HttpServerStub start() { + return this; } @Override - public void stop() { + public HttpServerStub start(int port) { + return this; + } + @Override + public HttpServerStub stop() { + return this; + } + + @Override + public HttpServerStub registerMappings(Collection stubFiles) { + return this; + } + + @Override public boolean isAccepted(File file) { + return true; } } diff --git a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubRepository.java b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubRepository.java index 7f0d234c1c..e0b43f0351 100644 --- a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubRepository.java +++ b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubRepository.java @@ -32,35 +32,46 @@ import java.util.List; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.cloud.contract.spec.Contract; +import org.springframework.cloud.contract.spec.ContractConverter; +import org.springframework.cloud.contract.stubrunner.provider.wiremock.WireMockHttpServerStub; import org.springframework.cloud.contract.verifier.util.ContractVerifierDslConverter; +import org.springframework.core.io.support.SpringFactoriesLoader; /** - * Wraps the folder with WireMock mappings. + * Wraps the folder with stub mappings. */ class StubRepository { private static final Logger log = LoggerFactory.getLogger(StubRepository.class); private final File path; - final List projectDescriptors; + final List stubs; final Collection contracts; + private final List contractConverters; + private final List httpServerStubs; - public StubRepository(File repository) { + StubRepository(File repository, List httpServerStubs) { if (!repository.isDirectory()) { throw new IllegalArgumentException( "Missing descriptor repository under path [" + repository + "]"); } + this.contractConverters = SpringFactoriesLoader.loadFactories(ContractConverter.class, null); + this.httpServerStubs = httpServerStubs; this.path = repository; - this.projectDescriptors = projectDescriptors(); + this.stubs = stubs(); this.contracts = contracts(); } + StubRepository(File repository) { + this(repository, new ArrayList()); + } + public File getPath() { return this.path; } - public List getProjectDescriptors() { - return this.projectDescriptors; + public List getStubs() { + return this.stubs; } public Collection getContracts() { @@ -68,7 +79,7 @@ class StubRepository { } /** - * Returns a list of {@link Contract} + * Returns a list of contracts */ private Collection contracts() { List contracts = new ArrayList<>(); @@ -77,23 +88,22 @@ class StubRepository { } /** - * Returns the list of WireMock JSON files wrapped in - * {@link WiremockMappingDescriptor} + * Returns the list of stubs */ - private List projectDescriptors() { - List mappingDescriptors = new ArrayList<>(); - mappingDescriptors.addAll(contextDescriptors()); - return mappingDescriptors; + private List stubs() { + List stubs = new ArrayList<>(); + stubs.addAll(collectedStubs()); + return stubs; } - private List contextDescriptors() { - return this.path.exists() ? collectMappingDescriptors(this.path) - : Collections.emptyList(); + private List collectedStubs() { + return this.path.exists() ? collectMappings(this.path) + : Collections.emptyList(); } - private List collectMappingDescriptors( + private List collectMappings( File descriptorsDirectory) { - final List mappingDescriptors = new ArrayList<>(); + final List mappingDescriptors = new ArrayList<>(); try { Files.walkFileTree(Paths.get(descriptorsDirectory.toURI()), new SimpleFileVisitor() { @@ -101,9 +111,8 @@ class StubRepository { public FileVisitResult visitFile(Path path, BasicFileAttributes attrs) throws IOException { File file = path.toFile(); - if (isMappingDescriptor(file)) { - mappingDescriptors - .add(new WiremockMappingDescriptor(file)); + if (httpServerStubAccepts(file)) { + mappingDescriptors.add(file); } return super.visitFile(path, attrs); } @@ -115,13 +124,33 @@ class StubRepository { return mappingDescriptors; } + private ContractConverter contractConverter(File file) { + for (ContractConverter converter : this.contractConverters) { + if (converter.isAccepted(file)) { + return converter; + } + } + return null; + } + + private boolean httpServerStubAccepts(File file) { + for (HttpServerStub httpServerStub : this.httpServerStubs) { + if (httpServerStub.isAccepted(file)) { + return true; + } + } + // the default implementation + return new WireMockHttpServerStub().isAccepted(file); + } + private Collection contractDescriptors() { return (this.path.exists() ? collectContractDescriptors(this.path) : Collections.emptySet()); } + @SuppressWarnings("unchecked") private Collection collectContractDescriptors(File descriptorsDirectory) { - final List mappingDescriptors = new ArrayList<>(); + final List contractDescriptors = new ArrayList<>(); try { Files.walkFileTree(Paths.get(descriptorsDirectory.toURI()), new SimpleFileVisitor() { @@ -129,9 +158,12 @@ class StubRepository { public FileVisitResult visitFile(Path path, BasicFileAttributes attrs) throws IOException { File file = path.toFile(); + ContractConverter converter = contractConverter(file); if (isContractDescriptor(file)) { - mappingDescriptors + contractDescriptors .addAll(ContractVerifierDslConverter.convertAsCollection(file)); + } else if (converter != null) { + contractDescriptors.addAll(converter.convertFrom(file)); } return super.visitFile(path, attrs); } @@ -140,11 +172,7 @@ class StubRepository { catch (IOException e) { log.warn("Exception occurred while trying to parse file", e); } - return mappingDescriptors; - } - - private static boolean isMappingDescriptor(File file) { - return file.isFile() && file.getName().endsWith(".json"); + return contractDescriptors; } private static boolean isContractDescriptor(File file) { diff --git a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubRunner.java b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubRunner.java index c2c2f7cf0c..9e4e44471f 100644 --- a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubRunner.java +++ b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubRunner.java @@ -20,11 +20,13 @@ import java.io.File; import java.io.IOException; import java.net.URL; import java.util.Collection; +import java.util.List; import java.util.Map; import org.springframework.cloud.contract.spec.Contract; import org.springframework.cloud.contract.verifier.messaging.MessageVerifier; import org.springframework.cloud.contract.verifier.messaging.noop.NoOpStubMessages; +import org.springframework.core.io.support.SpringFactoriesLoader; /** * Represents a single instance of ready-to-run stubs. Can run the stubs and then will @@ -55,10 +57,11 @@ public class StubRunner implements StubRunning { MessageVerifier contractVerifierMessaging) { this.stubsConfiguration = stubsConfiguration; this.stubRunnerOptions = stubRunnerOptions; - this.stubRepository = new StubRepository(new File(repositoryPath)); + List serverStubs = SpringFactoriesLoader.loadFactories(HttpServerStub.class, null); + this.stubRepository = new StubRepository(new File(repositoryPath), serverStubs); AvailablePortScanner portScanner = new AvailablePortScanner( stubRunnerOptions.getMinPortValue(), stubRunnerOptions.getMaxPortValue()); - this.localStubRunner = new StubRunnerExecutor(portScanner, contractVerifierMessaging); + this.localStubRunner = new StubRunnerExecutor(portScanner, contractVerifierMessaging, serverStubs); } @Override diff --git a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubRunnerExecutor.java b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubRunnerExecutor.java index 7ca041bfe1..325d710800 100644 --- a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubRunnerExecutor.java +++ b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubRunnerExecutor.java @@ -16,6 +16,7 @@ package org.springframework.cloud.contract.stubrunner; +import java.io.File; import java.net.URL; import java.util.ArrayList; import java.util.Collection; @@ -32,6 +33,7 @@ import org.springframework.cloud.contract.spec.internal.DslProperty; import org.springframework.cloud.contract.spec.internal.Headers; import org.springframework.cloud.contract.spec.internal.OutputMessage; import org.springframework.cloud.contract.stubrunner.AvailablePortScanner.PortCallback; +import org.springframework.cloud.contract.stubrunner.provider.wiremock.WireMockHttpServerStub; import org.springframework.cloud.contract.verifier.messaging.MessageVerifier; import org.springframework.cloud.contract.verifier.messaging.noop.NoOpStubMessages; import org.springframework.cloud.contract.verifier.util.BodyExtractor; @@ -47,14 +49,20 @@ class StubRunnerExecutor implements StubFinder { private final AvailablePortScanner portScanner; private final MessageVerifier contractVerifierMessaging; private StubServer stubServer; + private final List serverStubs; - public StubRunnerExecutor(AvailablePortScanner portScanner, MessageVerifier contractVerifierMessaging) { + StubRunnerExecutor(AvailablePortScanner portScanner, MessageVerifier contractVerifierMessaging, List serverStubs) { this.portScanner = portScanner; this.contractVerifierMessaging = contractVerifierMessaging; + this.serverStubs = serverStubs; } - protected StubRunnerExecutor(AvailablePortScanner portScanner) { - this(portScanner, new NoOpStubMessages()); + StubRunnerExecutor(AvailablePortScanner portScanner, List serverStubs) { + this(portScanner, new NoOpStubMessages(), serverStubs); + } + + StubRunnerExecutor(AvailablePortScanner portScanner) { + this(portScanner, new NoOpStubMessages(), new ArrayList()); } public RunningStubs runStubs(StubRunnerOptions stubRunnerOptions, StubRepository repository, @@ -224,33 +232,32 @@ class StubRunnerExecutor implements StubFinder { private void startStubServers(final StubRunnerOptions stubRunnerOptions, final StubConfiguration stubConfiguration, StubRepository repository) { - final List mappings = repository.getProjectDescriptors(); + final List mappings = repository.getStubs(); final Collection contracts = repository.contracts; Integer port = stubRunnerOptions.port(stubConfiguration); if (!contracts.isEmpty() && !hasRequest(contracts)) { if (log.isDebugEnabled()) { log.debug("There are no HTTP related contracts. Won't start any servers"); } - this.stubServer = new StubServer(stubConfiguration, mappings, contracts, new NoOpHttpServerStub()); + this.stubServer = new StubServer(stubConfiguration, mappings, contracts, new NoOpHttpServerStub()).start(); return; } if (contracts.isEmpty()) { log.warn("There are no contracts in the published JAR. This is an unusual situation " - + "that's why will start the server - maybe you know what you're doing..."); + + "but we will start the server anyways - maybe you know what you're doing..."); } if (port != null && port >= 0) { - this.stubServer = new StubServer(stubConfiguration, mappings, contracts, new WireMockHttpServerStub(port)); + this.stubServer = new StubServer(stubConfiguration, mappings, contracts, httpServerStub()).start(port); } else { this.stubServer = this.portScanner.tryToExecuteWithFreePort(new PortCallback() { @Override public StubServer call(int availablePort) { return new StubServer(stubConfiguration, mappings, contracts, - new WireMockHttpServerStub(availablePort)); + httpServerStub()).start(availablePort); } }); } - this.stubServer = this.stubServer.start(); } private boolean hasRequest(Collection contracts) { @@ -262,4 +269,13 @@ class StubRunnerExecutor implements StubFinder { return false; } + private HttpServerStub httpServerStub() { + // the default impl is the WireMock one + if (this.serverStubs.isEmpty()) { + return new WireMockHttpServerStub(); + } + // first one wins + return this.serverStubs.get(0); + } + } diff --git a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubServer.java b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubServer.java index 6fb15069da..7ab5bbb103 100644 --- a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubServer.java +++ b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubServer.java @@ -16,6 +16,7 @@ package org.springframework.cloud.contract.stubrunner; +import java.io.File; import java.net.MalformedURLException; import java.net.URL; import java.util.Collection; @@ -24,18 +25,16 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.cloud.contract.spec.Contract; -import com.github.tomakehurst.wiremock.client.WireMock; - class StubServer { private static final Logger log = LoggerFactory.getLogger(StubServer.class); private final HttpServerStub httpServerStub; final StubConfiguration stubConfiguration; - final Collection mappings; + final Collection mappings; final Collection contracts; - StubServer(StubConfiguration stubConfiguration, Collection mappings, + StubServer(StubConfiguration stubConfiguration, Collection mappings, Collection contracts, HttpServerStub httpServerStub) { this.stubConfiguration = stubConfiguration; this.mappings = mappings; @@ -45,9 +44,18 @@ class StubServer { public StubServer start() { this.httpServerStub.start(); + return stubServer(); + } + + public StubServer start(int port) { + this.httpServerStub.start(port); + return stubServer(); + } + + private StubServer stubServer() { log.info("Started stub server for project [" + this.stubConfiguration.toColonSeparatedDependencyNotation() + "] on port " + this.httpServerStub.port()); - registerStubMappings(); + this.httpServerStub.registerMappings(this.mappings); return this; } @@ -83,37 +91,5 @@ class StubServer { return this.contracts; } - private void registerStubMappings() { - WireMock wireMock = new WireMock("localhost", this.httpServerStub.port(), ""); - registerDefaultHealthChecks(wireMock); - registerStubs(this.mappings, wireMock); - } - private void registerDefaultHealthChecks(WireMock wireMock) { - registerHealthCheck(wireMock, "/ping"); - registerHealthCheck(wireMock, "/health"); - } - - private void registerStubs(Collection sortedMappings, WireMock wireMock) { - for (WiremockMappingDescriptor mappingDescriptor : sortedMappings) { - try { - wireMock.register(mappingDescriptor.getMapping()); - if (log.isDebugEnabled()) { - log.debug("Registered stub mappings from [" + mappingDescriptor.descriptor + "]"); - } - } - catch (Exception e) { - log.warn("Failed to register the stub mapping [" + mappingDescriptor + "]", e); - } - } - } - - private void registerHealthCheck(WireMock wireMock, String url) { - registerHealthCheck(wireMock, url, "OK"); - } - - private void registerHealthCheck(WireMock wireMock, String url, String body) { - wireMock.register( - WireMock.get(WireMock.urlEqualTo(url)).willReturn(WireMock.aResponse().withBody(body).withStatus(200))); - } } diff --git a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/WireMockHttpServerStub.java b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/WireMockHttpServerStub.java deleted file mode 100644 index 54765ed3ae..0000000000 --- a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/WireMockHttpServerStub.java +++ /dev/null @@ -1,46 +0,0 @@ -package org.springframework.cloud.contract.stubrunner; - -import org.springframework.cloud.contract.wiremock.WireMockSpring; -import org.springframework.util.ClassUtils; - -import com.github.tomakehurst.wiremock.WireMockServer; -import com.github.tomakehurst.wiremock.core.WireMockConfiguration; - -/** - * @author Marcin Grzejszczak - */ -class WireMockHttpServerStub implements HttpServerStub { - - private final WireMockServer wireMockServer; - - WireMockHttpServerStub(int port) { - this.wireMockServer = new WireMockServer(config().port(port)); - } - - private WireMockConfiguration config() { - if (ClassUtils.isPresent("org.springframework.cloud.contract.wiremock.WireMockSpring", null)) { - return WireMockSpring.options(); - } - return new WireMockConfiguration(); - } - - @Override - public int port() { - return this.wireMockServer.port(); - } - - @Override - public boolean isRunning() { - return this.wireMockServer.isRunning(); - } - - @Override - public void start() { - this.wireMockServer.start(); - } - - @Override - public void stop() { - this.wireMockServer.stop(); - } -} diff --git a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/WiremockMappingDescriptor.java b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/WiremockMappingDescriptor.java deleted file mode 100644 index 131bf1716b..0000000000 --- a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/WiremockMappingDescriptor.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright 2013-2016 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 - * - * http://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. - */ - -package org.springframework.cloud.contract.stubrunner; - -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.nio.charset.Charset; - -import org.springframework.util.StreamUtils; - -import com.github.tomakehurst.wiremock.stubbing.StubMapping; - -/** - * Represents a single JSON file that was found in the folder with potential WireMock - * stubs - */ -class WiremockMappingDescriptor { - - final File descriptor; - - public WiremockMappingDescriptor(File mappingDescriptor) { - this.descriptor = mappingDescriptor; - } - - public StubMapping getMapping() { - try { - return StubMapping.buildFrom(StreamUtils.copyToString( - new FileInputStream(this.descriptor), Charset.forName("UTF-8"))); - } - catch (IOException e) { - throw new IllegalStateException("Cannot read file", e); - } - } - - @Override - public String toString() { - return "WiremockMappingDescriptor [descriptor=" + this.descriptor + "]"; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((this.descriptor == null) ? 0 : this.descriptor.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - WiremockMappingDescriptor other = (WiremockMappingDescriptor) obj; - if (this.descriptor == null) { - if (other.descriptor != null) - return false; - } - else if (!this.descriptor.equals(other.descriptor)) - return false; - return true; - } -} diff --git a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/provider/wiremock/WireMockHttpServerStub.java b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/provider/wiremock/WireMockHttpServerStub.java new file mode 100644 index 0000000000..60fe2e8d21 --- /dev/null +++ b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/provider/wiremock/WireMockHttpServerStub.java @@ -0,0 +1,135 @@ +package org.springframework.cloud.contract.stubrunner.provider.wiremock; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.nio.charset.Charset; +import java.util.Collection; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.cloud.contract.stubrunner.HttpServerStub; +import org.springframework.cloud.contract.wiremock.WireMockSpring; +import org.springframework.util.ClassUtils; +import org.springframework.util.SocketUtils; +import org.springframework.util.StreamUtils; + +import com.github.tomakehurst.wiremock.WireMockServer; +import com.github.tomakehurst.wiremock.client.WireMock; +import com.github.tomakehurst.wiremock.core.WireMockConfiguration; +import com.github.tomakehurst.wiremock.stubbing.StubMapping; + +/** + * Abstraction over WireMock as a HTTP Server Stub + * + * @author Marcin Grzejszczak + * @since 1.1.0 + */ +public class WireMockHttpServerStub implements HttpServerStub { + + private static final Logger log = LoggerFactory.getLogger(WireMockHttpServerStub.class); + private static final int INVALID_PORT = -1; + + private WireMockServer wireMockServer; + + private WireMockConfiguration config() { + if (ClassUtils.isPresent("org.springframework.cloud.contract.wiremock.WireMockSpring", null)) { + return WireMockSpring.options(); + } + return new WireMockConfiguration(); + } + + @Override + public int port() { + return isRunning() ? this.wireMockServer.port() : INVALID_PORT; + } + + @Override + public boolean isRunning() { + return this.wireMockServer != null && this.wireMockServer.isRunning(); + } + + @Override + public HttpServerStub start() { + if (isRunning()) { + log.info("The server is already running at port [" + port() + "]"); + return this; + } + return start(SocketUtils.findAvailableTcpPort()); + } + + @Override + public HttpServerStub start(int port) { + this.wireMockServer = new WireMockServer(config().port(port)); + this.wireMockServer.start(); + return this; + } + + @Override + public HttpServerStub stop() { + if (!isRunning()) { + log.warn("Trying to stop a non started server!"); + return this; + } + this.wireMockServer.stop(); + return this; + } + + @Override + public HttpServerStub registerMappings(Collection stubFiles) { + if (!isRunning()) { + throw new IllegalStateException("Server not started!"); + } + registerStubMappings(stubFiles); + return this; + } + + @Override + public boolean isAccepted(File file) { + return file.getName().endsWith(".json"); + } + + StubMapping getMapping(File file) { + try { + return StubMapping.buildFrom(StreamUtils.copyToString( + new FileInputStream(file), Charset.forName("UTF-8"))); + } + catch (IOException e) { + throw new IllegalStateException("Cannot read file", e); + } + } + + private void registerStubMappings(Collection stubFiles) { + WireMock wireMock = new WireMock("localhost", port(), ""); + registerDefaultHealthChecks(wireMock); + registerStubs(stubFiles, wireMock); + } + + private void registerDefaultHealthChecks(WireMock wireMock) { + registerHealthCheck(wireMock, "/ping"); + registerHealthCheck(wireMock, "/health"); + } + + private void registerStubs(Collection sortedMappings, WireMock wireMock) { + for (File mappingDescriptor : sortedMappings) { + try { + wireMock.register(getMapping(mappingDescriptor)); + if (log.isDebugEnabled()) { + log.debug("Registered stub mappings from [" + mappingDescriptor + "]"); + } + } + catch (Exception e) { + log.warn("Failed to register the stub mapping [" + mappingDescriptor + "]", e); + } + } + } + + private void registerHealthCheck(WireMock wireMock, String url) { + registerHealthCheck(wireMock, url, "OK"); + } + + private void registerHealthCheck(WireMock wireMock, String url, String body) { + wireMock.register( + WireMock.get(WireMock.urlEqualTo(url)).willReturn(WireMock.aResponse().withBody(body).withStatus(200))); + } +} diff --git a/spring-cloud-contract-stub-runner/src/test/groovy/org/springframework/cloud/contract/stubrunner/StubRepositorySpec.groovy b/spring-cloud-contract-stub-runner/src/test/groovy/org/springframework/cloud/contract/stubrunner/StubRepositorySpec.groovy index 722739a0fc..7422c8949b 100644 --- a/spring-cloud-contract-stub-runner/src/test/groovy/org/springframework/cloud/contract/stubrunner/StubRepositorySpec.groovy +++ b/spring-cloud-contract-stub-runner/src/test/groovy/org/springframework/cloud/contract/stubrunner/StubRepositorySpec.groovy @@ -27,7 +27,7 @@ class StubRepositorySpec extends Specification { StubRepository repository = new StubRepository(REPOSITORY_LOCATION) int expectedDescriptorsSize = 8 when: - List descriptors = repository.getProjectDescriptors() + List descriptors = repository.getStubs() then: descriptors.size() == expectedDescriptorsSize } @@ -36,7 +36,7 @@ class StubRepositorySpec extends Specification { given: StubRepository repository = new StubRepository(new File('src/test/resources/emptyrepo')) when: - List descriptors = repository.getProjectDescriptors() + List descriptors = repository.getStubs() then: descriptors.empty } diff --git a/spring-cloud-contract-stub-runner/src/test/groovy/org/springframework/cloud/contract/stubrunner/StubRunnerExecutorSpec.groovy b/spring-cloud-contract-stub-runner/src/test/groovy/org/springframework/cloud/contract/stubrunner/StubRunnerExecutorSpec.groovy index 0f8ef3c7f9..e727217faa 100644 --- a/spring-cloud-contract-stub-runner/src/test/groovy/org/springframework/cloud/contract/stubrunner/StubRunnerExecutorSpec.groovy +++ b/spring-cloud-contract-stub-runner/src/test/groovy/org/springframework/cloud/contract/stubrunner/StubRunnerExecutorSpec.groovy @@ -85,7 +85,7 @@ class StubRunnerExecutorSpec extends Specification { def 'should ensure that triggered contracts have properly parsed message body when a message is sent'() { given: - StubRunnerExecutor executor = new StubRunnerExecutor(portScanner, new AssertingStubMessages()) + StubRunnerExecutor executor = new StubRunnerExecutor(portScanner, new AssertingStubMessages(), []) executor.runStubs(stubRunnerOptions, repository, stub) when: executor.trigger('send_order') diff --git a/spring-cloud-contract-stub-runner/src/test/groovy/org/springframework/cloud/contract/stubrunner/StubServerSpec.groovy b/spring-cloud-contract-stub-runner/src/test/groovy/org/springframework/cloud/contract/stubrunner/StubServerSpec.groovy index bee5ab6820..54b89407cb 100644 --- a/spring-cloud-contract-stub-runner/src/test/groovy/org/springframework/cloud/contract/stubrunner/StubServerSpec.groovy +++ b/spring-cloud-contract-stub-runner/src/test/groovy/org/springframework/cloud/contract/stubrunner/StubServerSpec.groovy @@ -16,6 +16,7 @@ package org.springframework.cloud.contract.stubrunner +import org.springframework.cloud.contract.stubrunner.provider.wiremock.WireMockHttpServerStub import spock.lang.Specification class StubServerSpec extends Specification { @@ -27,9 +28,9 @@ class StubServerSpec extends Specification { def 'should register stub mappings upon server start'() { given: - List mappingDescriptors = new StubRepository(repository).getProjectDescriptors() + List mappingDescriptors = new StubRepository(repository).getStubs() StubServer pingStubServer = new StubServer(stubConfiguration, mappingDescriptors, [], - new WireMockHttpServerStub(STUB_SERVER_PORT)) + new WireMockHttpServerStub()).start(STUB_SERVER_PORT) when: pingStubServer.start() then: @@ -39,9 +40,9 @@ class StubServerSpec extends Specification { def 'should provide stub server URL'() { given: - List mappingDescriptors = new StubRepository(repository).getProjectDescriptors() + List mappingDescriptors = new StubRepository(repository).getStubs() StubServer pingStubServer = new StubServer(stubConfiguration, mappingDescriptors, [], - new WireMockHttpServerStub(STUB_SERVER_PORT)) + new WireMockHttpServerStub()).start(STUB_SERVER_PORT) when: pingStubServer.start() then: diff --git a/spring-cloud-contract-stub-runner/src/test/groovy/org/springframework/cloud/contract/stubrunner/MappingDescriptorSpec.groovy b/spring-cloud-contract-stub-runner/src/test/groovy/org/springframework/cloud/contract/stubrunner/provider/wiremock/WireMockHttpServerStubSpec.groovy similarity index 59% rename from spring-cloud-contract-stub-runner/src/test/groovy/org/springframework/cloud/contract/stubrunner/MappingDescriptorSpec.groovy rename to spring-cloud-contract-stub-runner/src/test/groovy/org/springframework/cloud/contract/stubrunner/provider/wiremock/WireMockHttpServerStubSpec.groovy index 2fddae9d96..644c0308fc 100644 --- a/spring-cloud-contract-stub-runner/src/test/groovy/org/springframework/cloud/contract/stubrunner/MappingDescriptorSpec.groovy +++ b/spring-cloud-contract-stub-runner/src/test/groovy/org/springframework/cloud/contract/stubrunner/provider/wiremock/WireMockHttpServerStubSpec.groovy @@ -14,26 +14,30 @@ * limitations under the License. */ -package org.springframework.cloud.contract.stubrunner +package org.springframework.cloud.contract.stubrunner.provider.wiremock import com.github.tomakehurst.wiremock.http.RequestMethod +import com.github.tomakehurst.wiremock.stubbing.StubMapping import spock.lang.Specification -class MappingDescriptorSpec extends Specification { +class WireMockHttpServerStubSpec extends Specification { public static final File MAPPING_DESCRIPTOR = new File('src/test/resources/repository/mappings/spring/cloud/ping/ping.json') def 'should describe stub mapping'() { given: - WiremockMappingDescriptor mappingDescriptor = new WiremockMappingDescriptor(MAPPING_DESCRIPTOR) + WireMockHttpServerStub mappingDescriptor = new WireMockHttpServerStub().start() as WireMockHttpServerStub - expect: - with(mappingDescriptor.mapping) { - request.method == RequestMethod.GET - request.url == '/ping' - response.status == 200 - response.body == 'pong' - response.headers.contentTypeHeader.mimeTypePart() == 'text/plain' + when: + StubMapping mapping = mappingDescriptor.getMapping(MAPPING_DESCRIPTOR) + + then: + with(mapping) { + assert request.method == RequestMethod.GET + assert request.url == '/ping' + assert response.status == 200 + assert response.body == 'pong' + assert response.headers.contentTypeHeader.mimeTypePart() == 'text/plain' } } }