Merge branch '1.0.x'
This commit is contained in:
@@ -332,7 +332,6 @@ for every registered WireMock server. Example for Stub Runner ids
|
||||
|
||||
Which you can reference in your code.
|
||||
|
||||
|
||||
=== Stub Runner Spring Cloud
|
||||
|
||||
Stub Runner can integrate with Spring Cloud.
|
||||
@@ -475,3 +474,97 @@ That way your deployed application can send requests to started WireMock servers
|
||||
discovery. Most likely points 1-3 could be set by default in `application.yml` cause they are not
|
||||
likely to change. That way you can provide only the list of stubs to download whenever you start
|
||||
the Stub Runner Boot.
|
||||
|
||||
=== Stubs Per Consumer
|
||||
|
||||
There are cases in which 2 consumers of the same endpoint want to have 2 different responses.
|
||||
|
||||
TIP: This approach also allows you to immediately know which consumer is using which part of your API.
|
||||
You can remove part of a response that your API produces and you can see which of your autogenerated tests
|
||||
fails. If none fails then you can safely delete that part of the response cause nobody is using it.
|
||||
|
||||
Let's look at the following example for contract defined for the producer called `producer`.
|
||||
There are 2 consumers: `foo-consumer` and `bar-consumer`.
|
||||
|
||||
*Consumer `foo-service`*
|
||||
|
||||
[source,groovy]
|
||||
----
|
||||
request {
|
||||
url '/foo'
|
||||
method GET()
|
||||
}
|
||||
response {
|
||||
status 200
|
||||
body(
|
||||
foo: "foo"
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
*Consumer `bar-service`*
|
||||
|
||||
[source,groovy]
|
||||
----
|
||||
request {
|
||||
url '/foo'
|
||||
method GET()
|
||||
}
|
||||
response {
|
||||
status 200
|
||||
body(
|
||||
bar: "bar"
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
You can't produce for the same request 2 different responses. That's why you can properly package the
|
||||
contracts and then profit from the `stubsPerConsumer` feature.
|
||||
|
||||
On the producer side the consumers can have a folder that contains contracts related only to them.
|
||||
By setting the `stubrunner.stubs-per-consumer` flag to `true` we no longer register all stubs but only those that
|
||||
correspond to the consumer application's name. In other words we'll scan the path of every stub and
|
||||
if it contains the subfolder with name of the consumer in the path only then will it get registered.
|
||||
|
||||
On the `foo` producer side the contracts would look like this
|
||||
|
||||
[source,bash]
|
||||
----
|
||||
.
|
||||
└── contracts
|
||||
├── bar-consumer
|
||||
│ ├── bookReturnedForBar.groovy
|
||||
│ └── shouldCallBar.groovy
|
||||
└── foo-consumer
|
||||
├── bookReturnedForFoo.groovy
|
||||
└── shouldCallFoo.groovy
|
||||
----
|
||||
|
||||
Being the `bar-consumer` consumer you can either set the `spring.application.name` or the `stubrunner.consumer-name` to `bar-consumer`
|
||||
Or set the test as follows:
|
||||
|
||||
[source,groovy]
|
||||
----
|
||||
include::src/test/groovy/org/springframework/cloud/contract/stubrunner/spring/cloud/StubRunnerStubsPerConsumerSpec.groovy[tags=test]
|
||||
...
|
||||
}
|
||||
----
|
||||
|
||||
Then only the stubs registered under a path that contains the `bar-consumer` in its name (i.e. those from the
|
||||
`src/test/resources/contracts/bar-consumer/some/contracts/...` folder) will be allowed to be referenced.
|
||||
|
||||
Or set the consumer name explicitly
|
||||
|
||||
[source,groovy]
|
||||
----
|
||||
include::src/test/groovy/org/springframework/cloud/contract/stubrunner/spring/cloud/StubRunnerStubsPerConsumerWithConsumerNameSpec.groovy[tags=test]
|
||||
...
|
||||
}
|
||||
----
|
||||
|
||||
Then only the stubs registered under a path that contains the `foo-consumer` in its name (i.e. those from the
|
||||
`src/test/resources/contracts/foo-consumer/some/contracts/...` folder) will be allowed to be referenced.
|
||||
|
||||
You can check out https://github.com/spring-cloud/spring-cloud-contract/issues/224[issue 224] for more
|
||||
information about the reasons behind this change.
|
||||
|
||||
|
||||
@@ -49,8 +49,10 @@ class StubRepository {
|
||||
final Collection<Contract> contracts;
|
||||
private final List<ContractConverter> contractConverters;
|
||||
private final List<HttpServerStub> httpServerStubs;
|
||||
private final StubRunnerOptions options;
|
||||
|
||||
StubRepository(File repository, List<HttpServerStub> httpServerStubs) {
|
||||
StubRepository(File repository, List<HttpServerStub> httpServerStubs,
|
||||
StubRunnerOptions options) {
|
||||
if (!repository.isDirectory()) {
|
||||
throw new IllegalArgumentException(
|
||||
"Missing descriptor repository under path [" + repository + "]");
|
||||
@@ -62,11 +64,12 @@ class StubRepository {
|
||||
this.httpServerStubs = httpServerStubs;
|
||||
this.path = repository;
|
||||
this.stubs = stubs();
|
||||
this.options = options;
|
||||
this.contracts = contracts();
|
||||
}
|
||||
|
||||
StubRepository(File repository) {
|
||||
this(repository, new ArrayList<HttpServerStub>());
|
||||
this(repository, new ArrayList<HttpServerStub>(), new StubRunnerOptionsBuilder().build());
|
||||
}
|
||||
|
||||
public File getPath() {
|
||||
@@ -114,7 +117,7 @@ class StubRepository {
|
||||
public FileVisitResult visitFile(Path path,
|
||||
BasicFileAttributes attrs) throws IOException {
|
||||
File file = path.toFile();
|
||||
if (httpServerStubAccepts(file)) {
|
||||
if (httpServerStubAccepts(file) && isStubPerConsumerPathMatching(file)) {
|
||||
mappingDescriptors.add(file);
|
||||
}
|
||||
return super.visitFile(path, attrs);
|
||||
@@ -162,10 +165,10 @@ class StubRepository {
|
||||
BasicFileAttributes attrs) throws IOException {
|
||||
File file = path.toFile();
|
||||
ContractConverter converter = contractConverter(file);
|
||||
if (isContractDescriptor(file)) {
|
||||
if (isContractDescriptor(file) && isStubPerConsumerPathMatching(file)) {
|
||||
contractDescriptors
|
||||
.addAll(ContractVerifierDslConverter.convertAsCollection(file));
|
||||
} else if (converter != null) {
|
||||
} else if (converter != null && isStubPerConsumerPathMatching(file)) {
|
||||
contractDescriptors.addAll(converter.convertFrom(file));
|
||||
}
|
||||
return super.visitFile(path, attrs);
|
||||
@@ -178,6 +181,14 @@ class StubRepository {
|
||||
return contractDescriptors;
|
||||
}
|
||||
|
||||
private boolean isStubPerConsumerPathMatching(File file) {
|
||||
if (!this.options.isStubsPerConsumer()) {
|
||||
return true;
|
||||
}
|
||||
String consumerName = this.options.getConsumerName();
|
||||
return file.getAbsolutePath().contains(File.separator + consumerName + File.separator);
|
||||
}
|
||||
|
||||
private static boolean isContractDescriptor(File file) {
|
||||
// TODO: Consider script injections implications...
|
||||
return file.isFile() && file.getName().endsWith(".groovy");
|
||||
|
||||
@@ -58,7 +58,7 @@ public class StubRunner implements StubRunning {
|
||||
this.stubsConfiguration = stubsConfiguration;
|
||||
this.stubRunnerOptions = stubRunnerOptions;
|
||||
List<HttpServerStub> serverStubs = SpringFactoriesLoader.loadFactories(HttpServerStub.class, null);
|
||||
this.stubRepository = new StubRepository(new File(repositoryPath), serverStubs);
|
||||
this.stubRepository = new StubRepository(new File(repositoryPath), serverStubs, this.stubRunnerOptions);
|
||||
AvailablePortScanner portScanner = new AvailablePortScanner(
|
||||
stubRunnerOptions.getMinPortValue(), stubRunnerOptions.getMaxPortValue());
|
||||
this.localStubRunner = new StubRunnerExecutor(portScanner, contractVerifierMessaging, serverStubs);
|
||||
|
||||
@@ -75,11 +75,22 @@ public class StubRunnerOptions {
|
||||
*/
|
||||
private final StubRunnerProxyOptions stubRunnerProxyOptions;
|
||||
|
||||
/**
|
||||
* Should only stubs applicable for the given consumer get registered
|
||||
*/
|
||||
private boolean stubsPerConsumer = false;
|
||||
|
||||
/**
|
||||
* Name of the consumer. If not set should default to {@code spring.application.name}
|
||||
*/
|
||||
private String consumerName;
|
||||
|
||||
StubRunnerOptions(Integer minPortValue, Integer maxPortValue,
|
||||
String stubRepositoryRoot, boolean workOffline, String stubsClassifier,
|
||||
Collection<StubConfiguration> dependencies,
|
||||
Map<StubConfiguration, Integer> stubIdsToPortMapping,
|
||||
String username, String password, final StubRunnerProxyOptions stubRunnerProxyOptions) {
|
||||
String username, String password, final StubRunnerProxyOptions stubRunnerProxyOptions,
|
||||
boolean stubsPerConsumer, String consumerName) {
|
||||
this.minPortValue = minPortValue;
|
||||
this.maxPortValue = maxPortValue;
|
||||
this.stubRepositoryRoot = stubRepositoryRoot;
|
||||
@@ -90,6 +101,8 @@ public class StubRunnerOptions {
|
||||
this.username = username;
|
||||
this.password = password;
|
||||
this.stubRunnerProxyOptions = stubRunnerProxyOptions;
|
||||
this.stubsPerConsumer = stubsPerConsumer;
|
||||
this.consumerName = consumerName;
|
||||
}
|
||||
|
||||
public Integer port(StubConfiguration stubConfiguration) {
|
||||
@@ -145,6 +158,22 @@ public class StubRunnerOptions {
|
||||
return this.stubRunnerProxyOptions;
|
||||
}
|
||||
|
||||
public boolean isStubsPerConsumer() {
|
||||
return this.stubsPerConsumer;
|
||||
}
|
||||
|
||||
public void setStubsPerConsumer(boolean stubsPerConsumer) {
|
||||
this.stubsPerConsumer = stubsPerConsumer;
|
||||
}
|
||||
|
||||
public String getConsumerName() {
|
||||
return this.consumerName;
|
||||
}
|
||||
|
||||
public void setConsumerName(String consumerName) {
|
||||
this.consumerName = consumerName;
|
||||
}
|
||||
|
||||
public static class StubRunnerProxyOptions {
|
||||
|
||||
private final String proxyHost;
|
||||
@@ -175,7 +204,9 @@ public class StubRunnerOptions {
|
||||
+ ", workOffline=" + this.workOffline + ", stubsClassifier='" + this.stubsClassifier
|
||||
+ '\'' + ", dependencies=" + this.dependencies + ", stubIdsToPortMapping="
|
||||
+ this.stubIdsToPortMapping + ", username='" + this.username + '\'' + ", password='"
|
||||
+ this.password + '\'' + ", stubRunnerProxyOptions=" + this.stubRunnerProxyOptions
|
||||
+ this.password + '\'' + ", stubRunnerProxyOptions='" + this.stubRunnerProxyOptions + "', stubsPerConsumer='"
|
||||
+ this.stubsPerConsumer
|
||||
+ '\'' + ", stubsPerConsumer='" + this.stubsPerConsumer + '\''
|
||||
+ '}';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,6 +41,8 @@ public class StubRunnerOptionsBuilder {
|
||||
private String username;
|
||||
private String password;
|
||||
private StubRunnerOptions.StubRunnerProxyOptions stubRunnerProxyOptions;
|
||||
private boolean stubPerConsumer = false;
|
||||
private String consumerName;
|
||||
|
||||
public StubRunnerOptionsBuilder() {
|
||||
}
|
||||
@@ -118,7 +120,7 @@ public class StubRunnerOptionsBuilder {
|
||||
public StubRunnerOptions build() {
|
||||
return new StubRunnerOptions(this.minPortValue, this.maxPortValue, this.stubRepositoryRoot,
|
||||
this.workOffline, this.stubsClassifier, buildDependencies(), this.stubIdsToPortMapping,
|
||||
this.username, this.password, this.stubRunnerProxyOptions);
|
||||
this.username, this.password, this.stubRunnerProxyOptions, this.stubPerConsumer, this.consumerName);
|
||||
}
|
||||
|
||||
private Collection<StubConfiguration> buildDependencies() {
|
||||
@@ -193,4 +195,14 @@ public class StubRunnerOptionsBuilder {
|
||||
this.stubRunnerProxyOptions = new StubRunnerOptions.StubRunnerProxyOptions(proxyHost, proxyPort);
|
||||
return this;
|
||||
}
|
||||
|
||||
public StubRunnerOptionsBuilder withStubPerConsumer(boolean stubPerConsumer) {
|
||||
this.stubPerConsumer = stubPerConsumer;
|
||||
return this;
|
||||
}
|
||||
|
||||
public StubRunnerOptionsBuilder withConsumerName(String consumerName) {
|
||||
this.consumerName = consumerName;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,7 +77,9 @@ public class StubRunnerRule implements TestRule, StubFinder {
|
||||
.withStubsClassifier(System.getProperty("stubrunner.classifier", "stubs"))
|
||||
.withStubs(System.getProperty("stubrunner.ids", ""))
|
||||
.withUsername(System.getProperty("stubrunner.username"))
|
||||
.withPassword(System.getProperty("stubrunner.password"));
|
||||
.withPassword(System.getProperty("stubrunner.password"))
|
||||
.withStubPerConsumer(Boolean.parseBoolean(System.getProperty("stubrunner.stubsPerConsumer", "false")))
|
||||
.withConsumerName(System.getProperty("stubrunner.consumer-name"));
|
||||
String proxyHost = System.getProperty("stubrunner.proxy.host");
|
||||
if (proxyHost != null) {
|
||||
builder.withProxy(proxyHost, Integer.parseInt(System.getProperty("stubrunner.proxy.port")));
|
||||
@@ -201,6 +203,22 @@ public class StubRunnerRule implements TestRule, StubFinder {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows stub per consumer
|
||||
*/
|
||||
public StubRunnerRule withStubPerConsumer(boolean stubPerConsumer) {
|
||||
this.stubRunnerOptionsBuilder.withStubPerConsumer(stubPerConsumer);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows setting consumer name
|
||||
*/
|
||||
public StubRunnerRule withConsumerName(String consumerName) {
|
||||
this.stubRunnerOptionsBuilder.withConsumerName(consumerName);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public URL findStubUrl(String groupId, String artifactId) {
|
||||
return this.stubFinder.findStubUrl(groupId, artifactId);
|
||||
|
||||
@@ -69,4 +69,33 @@ public @interface AutoConfigureStubRunner {
|
||||
* The classifier to use by default in ivy co-ordinates for a stub.
|
||||
*/
|
||||
String classifier() default "stubs";
|
||||
|
||||
/**
|
||||
* On the producer side the consumers can have a folder that contains contracts related only to them. By setting the flag to {@code true}
|
||||
* we no longer register all stubs but only those that correspond to the consumer application's name. In other words
|
||||
* we'll scan the path of every stub and if it contains the name of the consumer in the path only then will it get registered.
|
||||
*
|
||||
* Let's look at this example. Let's assume
|
||||
* that we have a producer called {@code foo} and two consumers {@code baz} and {@code bar}. On the {@code foo} producer side the
|
||||
* contracts would look like this
|
||||
* {@code src/test/resources/contracts/baz-service/some/contracts/...} and
|
||||
* {@code src/test/resources/contracts/bar-service/some/contracts/...}.
|
||||
*
|
||||
* Then when the consumer with {@code spring.application.name} or the {@link AutoConfigureStubRunner#consumerName()}
|
||||
* annotation parameter set to {@code baz-service} will define the test setup as follows
|
||||
* {@code @AutoConfigureStubRunner(ids = "com.example:foo:+:stubs:8095", stubsPerConsumer=true)} then only the stubs registered
|
||||
* under {@code src/test/resources/contracts/baz-service/some/contracts/...} will get registered and those under
|
||||
* {@code src/test/resources/contracts/bar-service/some/contracts/...} will get ignored.
|
||||
*
|
||||
* @see <a href="https://github.com/spring-cloud/spring-cloud-contract/issues/224">issue 224</a>
|
||||
*
|
||||
*/
|
||||
boolean stubsPerConsumer() default false;
|
||||
|
||||
/**
|
||||
* You can override the default {@code spring.application.name} of this field by setting a value to this parameter.
|
||||
*
|
||||
* @see <a href="https://github.com/spring-cloud/spring-cloud-contract/issues/224">issue 224</a>
|
||||
*/
|
||||
String consumerName() default "";
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ import org.springframework.cloud.contract.stubrunner.StubConfiguration;
|
||||
import org.springframework.cloud.contract.stubrunner.StubDownloaderBuilderProvider;
|
||||
import org.springframework.cloud.contract.stubrunner.StubRunnerOptions;
|
||||
import org.springframework.cloud.contract.stubrunner.StubRunnerOptionsBuilder;
|
||||
import org.springframework.cloud.contract.stubrunner.util.StringUtils;
|
||||
import org.springframework.cloud.contract.verifier.messaging.MessageVerifier;
|
||||
import org.springframework.cloud.contract.verifier.messaging.noop.NoOpStubMessages;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
@@ -89,7 +90,16 @@ public class StubRunnerConfiguration {
|
||||
.withStubsClassifier(this.props.getClassifier())
|
||||
.withStubs(this.props.getIds())
|
||||
.withUsername(this.props.getUsername())
|
||||
.withPassword(this.props.getPassword());
|
||||
.withPassword(this.props.getPassword())
|
||||
.withStubPerConsumer(this.props.isStubsPerConsumer())
|
||||
.withConsumerName(consumerName());
|
||||
}
|
||||
|
||||
private String consumerName() {
|
||||
if (StringUtils.hasText(this.props.getConsumerName())) {
|
||||
return this.props.getConsumerName();
|
||||
}
|
||||
return this.environment.getProperty("spring.application.name");
|
||||
}
|
||||
|
||||
private String uriStringOrEmpty(Resource stubRepositoryRoot) throws IOException {
|
||||
|
||||
@@ -85,6 +85,16 @@ public class StubRunnerProperties {
|
||||
*/
|
||||
private String proxyHost;
|
||||
|
||||
/**
|
||||
* Should only stubs for this particular consumer get registered in HTTP server stub.
|
||||
*/
|
||||
private boolean stubsPerConsumer;
|
||||
|
||||
/**
|
||||
* You can override the default {@code spring.application.name} of this field by setting a value to this parameter.
|
||||
*/
|
||||
private String consumerName;
|
||||
|
||||
public int getMinPort() {
|
||||
return this.minPort;
|
||||
}
|
||||
@@ -173,10 +183,27 @@ public class StubRunnerProperties {
|
||||
this.contextPath = contextPath;
|
||||
}
|
||||
|
||||
public boolean isStubsPerConsumer() {
|
||||
return this.stubsPerConsumer;
|
||||
}
|
||||
|
||||
public void setStubsPerConsumer(boolean stubsPerConsumer) {
|
||||
this.stubsPerConsumer = stubsPerConsumer;
|
||||
}
|
||||
|
||||
public String getConsumerName() {
|
||||
return this.consumerName;
|
||||
}
|
||||
|
||||
public void setConsumerName(String consumerName) {
|
||||
this.consumerName = consumerName;
|
||||
}
|
||||
|
||||
@Override public String toString() {
|
||||
return "StubRunnerProperties{" + "minPort=" + this.minPort + ", maxPort=" + this.maxPort
|
||||
+ ", workOffline=" + this.workOffline + ", repositoryRoot=" + this.repositoryRoot
|
||||
+ ", ids=" + Arrays.toString(this.ids) + ", classifier='" + this.classifier + '\''
|
||||
+ ", setStubsPerConsumer='" + this.stubsPerConsumer + "', consumerName='" + this.consumerName + '\''
|
||||
+ '}';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ class StubRepositorySpec extends Specification {
|
||||
|
||||
def 'should retrieve all descriptors for given project'() {
|
||||
given:
|
||||
StubRepository repository = new StubRepository(REPOSITORY_LOCATION)
|
||||
StubRepository repository = new StubRepository(REPOSITORY_LOCATION, new StubRunnerOptionsBuilder().build())
|
||||
int expectedDescriptorsSize = 8
|
||||
when:
|
||||
List<File> descriptors = repository.getStubs()
|
||||
@@ -34,7 +34,7 @@ class StubRepositorySpec extends Specification {
|
||||
|
||||
def 'should return empty list if files are missing'() {
|
||||
given:
|
||||
StubRepository repository = new StubRepository(new File('src/test/resources/emptyrepo'))
|
||||
StubRepository repository = new StubRepository(new File('src/test/resources/emptyrepo'), new StubRunnerOptionsBuilder().build())
|
||||
when:
|
||||
List<File> descriptors = repository.getStubs()
|
||||
then:
|
||||
@@ -43,8 +43,21 @@ class StubRepositorySpec extends Specification {
|
||||
|
||||
def 'should throw an exception if directory with mappings is missing'() {
|
||||
when:
|
||||
new StubRepository(new File('src/test/resources/nonexistingrepo'))
|
||||
new StubRepository(new File('src/test/resources/nonexistingrepo'), new StubRunnerOptionsBuilder().build())
|
||||
then:
|
||||
thrown(IllegalArgumentException)
|
||||
}
|
||||
|
||||
def 'should retrieve only those mappings that contain the consumer name'() {
|
||||
given:
|
||||
StubRepository repository = new StubRepository(REPOSITORY_LOCATION,
|
||||
new StubRunnerOptionsBuilder()
|
||||
.withStubPerConsumer(true)
|
||||
.withConsumerName("ping").build())
|
||||
int expectedDescriptorsSize = 1
|
||||
when:
|
||||
List<WiremockMappingDescriptor> descriptors = repository.getProjectDescriptors()
|
||||
then:
|
||||
descriptors.size() == expectedDescriptorsSize
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ class StubRunnerExecutorSpec extends Specification {
|
||||
|
||||
def setup() {
|
||||
portScanner = new AvailablePortScanner(MIN_PORT, MAX_PORT)
|
||||
repository = new StubRepository(new File('src/test/resources/repository'))
|
||||
repository = new StubRepository(new File('src/test/resources/repository'), new StubRunnerOptionsBuilder().build())
|
||||
}
|
||||
|
||||
def 'should provide URL for given relative path of stub'() {
|
||||
@@ -122,7 +122,8 @@ class StubRunnerExecutorSpec extends Specification {
|
||||
StubRunnerExecutor executor = new StubRunnerExecutor(portScanner)
|
||||
when:
|
||||
executor.runStubs(stubRunnerOptions,
|
||||
new StubRepository(new File('src/test/resources/repository/httpcontract')), stubConf)
|
||||
new StubRepository(new File('src/test/resources/repository/httpcontract'),
|
||||
new StubRunnerOptionsBuilder().build()), stubConf)
|
||||
then:
|
||||
!executor.trigger()
|
||||
!executor.trigger("missing", "label")
|
||||
@@ -137,7 +138,8 @@ class StubRunnerExecutorSpec extends Specification {
|
||||
StubRunnerExecutor executor = new StubRunnerExecutor(portScanner)
|
||||
when:
|
||||
RunningStubs stubs = executor.runStubs(stubRunnerOptions,
|
||||
new StubRepository(new File('src/test/resources/emptyrepo')), stubConf)
|
||||
new StubRepository(new File('src/test/resources/emptyrepo'),
|
||||
new StubRunnerOptionsBuilder().build()), stubConf)
|
||||
then:
|
||||
stubs.getPort('asd') == -1
|
||||
cleanup:
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Copyright 2013-2017 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.spring.cloud
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration
|
||||
import org.springframework.boot.test.context.SpringBootContextLoader
|
||||
import org.springframework.boot.test.context.SpringBootTest
|
||||
import org.springframework.boot.test.web.client.TestRestTemplate
|
||||
import org.springframework.cloud.contract.stubrunner.StubFinder
|
||||
import org.springframework.cloud.contract.stubrunner.spring.AutoConfigureStubRunner
|
||||
import org.springframework.cloud.contract.verifier.messaging.MessageVerifier
|
||||
import org.springframework.cloud.stream.annotation.EnableBinding
|
||||
import org.springframework.cloud.stream.messaging.Sink
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import org.springframework.core.env.Environment
|
||||
import org.springframework.http.ResponseEntity
|
||||
import org.springframework.messaging.Message
|
||||
import org.springframework.test.annotation.DirtiesContext
|
||||
import org.springframework.test.context.ContextConfiguration
|
||||
import spock.lang.Specification
|
||||
|
||||
/**
|
||||
* @author Marcin Grzejszczak
|
||||
*/
|
||||
// tag::test[]
|
||||
@ContextConfiguration(classes = Config, loader = SpringBootContextLoader)
|
||||
@SpringBootTest(properties = ["spring.application.name=bar-consumer"])
|
||||
@AutoConfigureStubRunner(ids = "org.springframework.cloud.contract.verifier.stubs:producerWithMultipleConsumers",
|
||||
repositoryRoot = "classpath:m2repo/repository/",
|
||||
stubsPerConsumer = true)
|
||||
@DirtiesContext
|
||||
class StubRunnerStubsPerConsumerSpec extends Specification {
|
||||
// end::test[]
|
||||
|
||||
@Autowired StubFinder stubFinder
|
||||
@Autowired Environment environment
|
||||
@Autowired MessageVerifier<Message<?>> messaging
|
||||
TestRestTemplate template = new TestRestTemplate()
|
||||
|
||||
def 'should start http stub servers for bar-consumer only'() {
|
||||
given:
|
||||
URL stubUrl = stubFinder.findStubUrl('producerWithMultipleConsumers')
|
||||
when:
|
||||
ResponseEntity entity = template.getForEntity("${stubUrl}/bar-consumer", String)
|
||||
then:
|
||||
entity.statusCode.value() == 200
|
||||
when:
|
||||
entity = template.getForEntity("${stubUrl}/foo-consumer", String)
|
||||
then:
|
||||
entity.statusCode.value() == 404
|
||||
}
|
||||
|
||||
def 'should trigger a message by label from proper consumer'() {
|
||||
when:
|
||||
stubFinder.trigger('return_book_for_bar')
|
||||
then:
|
||||
Message<?> receivedMessage = messaging.receive('output')
|
||||
and:
|
||||
receivedMessage != null
|
||||
receivedMessage.payload == '''{"bookName":"foo_for_bar"}'''
|
||||
receivedMessage.headers.get('BOOK-NAME') == 'foo_for_bar'
|
||||
}
|
||||
|
||||
def 'should not trigger a message by the not matching consumer'() {
|
||||
when:
|
||||
stubFinder.trigger('return_book_for_foo')
|
||||
then:
|
||||
IllegalArgumentException e = thrown(IllegalArgumentException)
|
||||
e.message.contains("No label with name [return_book_for_foo] was found")
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableAutoConfiguration
|
||||
@EnableBinding(Sink)
|
||||
static class Config {}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* Copyright 2013-2017 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.spring.cloud
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration
|
||||
import org.springframework.boot.test.context.SpringBootContextLoader
|
||||
import org.springframework.boot.test.context.SpringBootTest
|
||||
import org.springframework.boot.test.web.client.TestRestTemplate
|
||||
import org.springframework.cloud.contract.stubrunner.StubFinder
|
||||
import org.springframework.cloud.contract.stubrunner.spring.AutoConfigureStubRunner
|
||||
import org.springframework.cloud.contract.verifier.messaging.MessageVerifier
|
||||
import org.springframework.cloud.stream.annotation.EnableBinding
|
||||
import org.springframework.cloud.stream.messaging.Sink
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import org.springframework.core.env.Environment
|
||||
import org.springframework.http.ResponseEntity
|
||||
import org.springframework.messaging.Message
|
||||
import org.springframework.test.annotation.DirtiesContext
|
||||
import org.springframework.test.context.ContextConfiguration
|
||||
import spock.lang.Specification
|
||||
|
||||
/**
|
||||
* @author Marcin Grzejszczak
|
||||
*/
|
||||
// tag::test[]
|
||||
@ContextConfiguration(classes = Config, loader = SpringBootContextLoader)
|
||||
@SpringBootTest
|
||||
@AutoConfigureStubRunner(ids = "org.springframework.cloud.contract.verifier.stubs:producerWithMultipleConsumers",
|
||||
repositoryRoot = "classpath:m2repo/repository/",
|
||||
consumerName = "foo-consumer",
|
||||
stubsPerConsumer = true)
|
||||
@DirtiesContext
|
||||
class StubRunnerStubsPerConsumerWithConsumerNameSpec extends Specification {
|
||||
// end::test[]
|
||||
|
||||
@Autowired StubFinder stubFinder
|
||||
@Autowired Environment environment
|
||||
@Autowired MessageVerifier<Message<?>> messaging
|
||||
TestRestTemplate template = new TestRestTemplate()
|
||||
|
||||
def 'should start http stub servers for foo-consumer only'() {
|
||||
given:
|
||||
URL stubUrl = stubFinder.findStubUrl('producerWithMultipleConsumers')
|
||||
when:
|
||||
ResponseEntity entity = template.getForEntity("${stubUrl}/foo-consumer", String)
|
||||
then:
|
||||
entity.statusCode.value() == 200
|
||||
when:
|
||||
entity = template.getForEntity("${stubUrl}/bar-consumer", String)
|
||||
then:
|
||||
entity.statusCode.value() == 404
|
||||
}
|
||||
|
||||
def 'should trigger a message by label from proper consumer'() {
|
||||
when:
|
||||
stubFinder.trigger('return_book_for_foo')
|
||||
then:
|
||||
Message<?> receivedMessage = messaging.receive('output')
|
||||
and:
|
||||
receivedMessage != null
|
||||
receivedMessage.payload == '''{"bookName":"foo_for_foo"}'''
|
||||
receivedMessage.headers.get('BOOK-NAME') == 'foo_for_foo'
|
||||
}
|
||||
|
||||
def 'should not trigger a message by the not matching consumer'() {
|
||||
when:
|
||||
stubFinder.trigger('return_book_for_bar')
|
||||
then:
|
||||
IllegalArgumentException e = thrown(IllegalArgumentException)
|
||||
e.message.contains("No label with name [return_book_for_bar] was found")
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableAutoConfiguration
|
||||
@EnableBinding(Sink)
|
||||
static class Config {}
|
||||
}
|
||||
Binary file not shown.
@@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
~ Copyright 2013-2017 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.
|
||||
-->
|
||||
|
||||
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>org.springframework.cloud.contract.verifier.stubs</groupId>
|
||||
<artifactId>producerWithMultipleConsumers</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<packaging>pom</packaging>
|
||||
</project>
|
||||
@@ -0,0 +1,28 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
~ Copyright 2013-2017 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.
|
||||
-->
|
||||
|
||||
<metadata>
|
||||
<groupId>org.springframework.cloud.contract.verifier.stubs</groupId>
|
||||
<artifactId>producerWithMultipleConsumers</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<versioning>
|
||||
<versions>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
</versions>
|
||||
<lastUpdated>20160409062112</lastUpdated>
|
||||
</versioning>
|
||||
</metadata>
|
||||
Reference in New Issue
Block a user