Abstracted WireMock. Introduced the HttpServerStub abstraction
This commit is contained in:
@@ -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<File> stubFiles);
|
||||
|
||||
/**
|
||||
* Returns {@code true} if the file is a valid stub mapping
|
||||
*/
|
||||
boolean isAccepted(File file);
|
||||
}
|
||||
|
||||
@@ -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<File> stubFiles) {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override public boolean isAccepted(File file) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<WiremockMappingDescriptor> projectDescriptors;
|
||||
final List<File> stubs;
|
||||
final Collection<Contract> contracts;
|
||||
private final List<ContractConverter> contractConverters;
|
||||
private final List<HttpServerStub> httpServerStubs;
|
||||
|
||||
public StubRepository(File repository) {
|
||||
StubRepository(File repository, List<HttpServerStub> 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<HttpServerStub>());
|
||||
}
|
||||
|
||||
public File getPath() {
|
||||
return this.path;
|
||||
}
|
||||
|
||||
public List<WiremockMappingDescriptor> getProjectDescriptors() {
|
||||
return this.projectDescriptors;
|
||||
public List<File> getStubs() {
|
||||
return this.stubs;
|
||||
}
|
||||
|
||||
public Collection<Contract> getContracts() {
|
||||
@@ -68,7 +79,7 @@ class StubRepository {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of {@link Contract}
|
||||
* Returns a list of contracts
|
||||
*/
|
||||
private Collection<Contract> contracts() {
|
||||
List<Contract> 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<WiremockMappingDescriptor> projectDescriptors() {
|
||||
List<WiremockMappingDescriptor> mappingDescriptors = new ArrayList<>();
|
||||
mappingDescriptors.addAll(contextDescriptors());
|
||||
return mappingDescriptors;
|
||||
private List<File> stubs() {
|
||||
List<File> stubs = new ArrayList<>();
|
||||
stubs.addAll(collectedStubs());
|
||||
return stubs;
|
||||
}
|
||||
|
||||
private List<WiremockMappingDescriptor> contextDescriptors() {
|
||||
return this.path.exists() ? collectMappingDescriptors(this.path)
|
||||
: Collections.<WiremockMappingDescriptor>emptyList();
|
||||
private List<File> collectedStubs() {
|
||||
return this.path.exists() ? collectMappings(this.path)
|
||||
: Collections.<File>emptyList();
|
||||
}
|
||||
|
||||
private List<WiremockMappingDescriptor> collectMappingDescriptors(
|
||||
private List<File> collectMappings(
|
||||
File descriptorsDirectory) {
|
||||
final List<WiremockMappingDescriptor> mappingDescriptors = new ArrayList<>();
|
||||
final List<File> mappingDescriptors = new ArrayList<>();
|
||||
try {
|
||||
Files.walkFileTree(Paths.get(descriptorsDirectory.toURI()),
|
||||
new SimpleFileVisitor<Path>() {
|
||||
@@ -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<Contract> contractDescriptors() {
|
||||
return (this.path.exists() ? collectContractDescriptors(this.path)
|
||||
: Collections.<Contract>emptySet());
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private Collection<Contract> collectContractDescriptors(File descriptorsDirectory) {
|
||||
final List<Contract> mappingDescriptors = new ArrayList<>();
|
||||
final List<Contract> contractDescriptors = new ArrayList<>();
|
||||
try {
|
||||
Files.walkFileTree(Paths.get(descriptorsDirectory.toURI()),
|
||||
new SimpleFileVisitor<Path>() {
|
||||
@@ -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) {
|
||||
|
||||
@@ -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<HttpServerStub> 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
|
||||
|
||||
@@ -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<HttpServerStub> serverStubs;
|
||||
|
||||
public StubRunnerExecutor(AvailablePortScanner portScanner, MessageVerifier<?> contractVerifierMessaging) {
|
||||
StubRunnerExecutor(AvailablePortScanner portScanner, MessageVerifier<?> contractVerifierMessaging, List<HttpServerStub> serverStubs) {
|
||||
this.portScanner = portScanner;
|
||||
this.contractVerifierMessaging = contractVerifierMessaging;
|
||||
this.serverStubs = serverStubs;
|
||||
}
|
||||
|
||||
protected StubRunnerExecutor(AvailablePortScanner portScanner) {
|
||||
this(portScanner, new NoOpStubMessages());
|
||||
StubRunnerExecutor(AvailablePortScanner portScanner, List<HttpServerStub> serverStubs) {
|
||||
this(portScanner, new NoOpStubMessages(), serverStubs);
|
||||
}
|
||||
|
||||
StubRunnerExecutor(AvailablePortScanner portScanner) {
|
||||
this(portScanner, new NoOpStubMessages(), new ArrayList<HttpServerStub>());
|
||||
}
|
||||
|
||||
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<WiremockMappingDescriptor> mappings = repository.getProjectDescriptors();
|
||||
final List<File> mappings = repository.getStubs();
|
||||
final Collection<Contract> 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<StubServer>() {
|
||||
@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<Contract> 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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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<WiremockMappingDescriptor> mappings;
|
||||
final Collection<File> mappings;
|
||||
final Collection<Contract> contracts;
|
||||
|
||||
StubServer(StubConfiguration stubConfiguration, Collection<WiremockMappingDescriptor> mappings,
|
||||
StubServer(StubConfiguration stubConfiguration, Collection<File> mappings,
|
||||
Collection<Contract> 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<WiremockMappingDescriptor> 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)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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<File> 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<File> 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<File> 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)));
|
||||
}
|
||||
}
|
||||
@@ -27,7 +27,7 @@ class StubRepositorySpec extends Specification {
|
||||
StubRepository repository = new StubRepository(REPOSITORY_LOCATION)
|
||||
int expectedDescriptorsSize = 8
|
||||
when:
|
||||
List<WiremockMappingDescriptor> descriptors = repository.getProjectDescriptors()
|
||||
List<File> 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<WiremockMappingDescriptor> descriptors = repository.getProjectDescriptors()
|
||||
List<File> descriptors = repository.getStubs()
|
||||
then:
|
||||
descriptors.empty
|
||||
}
|
||||
|
||||
@@ -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')
|
||||
|
||||
@@ -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<WiremockMappingDescriptor> mappingDescriptors = new StubRepository(repository).getProjectDescriptors()
|
||||
List<File> 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<WiremockMappingDescriptor> mappingDescriptors = new StubRepository(repository).getProjectDescriptors()
|
||||
List<File> 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:
|
||||
|
||||
@@ -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'
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user