Added possibility to pass a fixed port to a dependency (#250)

fixes #213
This commit is contained in:
Marcin Grzejszczak
2016-04-27 22:44:58 +02:00
parent 5f2c37868d
commit da1723ba4e
10 changed files with 246 additions and 34 deletions

View File

@@ -41,3 +41,38 @@ include::src/test/groovy/io/codearte/accurest/stubrunner/junit/AccurestRuleJUnit
Check the *Common properties for JUnit and Spring* for more information on how to apply global configuration of Stub Runner.
==== Providing fixed ports
You can also run your stubs on fixed ports. You can do it in two different ways. One is to pass it in the properties, and the other via fluent API.
===== System properties
You can provide the stubs to download via the `stubrunner.stubs.ids` system property. They follow the following pattern:
[source,java,indent=0]
----
groupId:artifactId:classifier:port
----
`classifier` and `port` are optional.
* If you don't provide the `classifier` then the default one will be taken.
* If you don't provide the `port` then a random one will be picked
==== Fluent API
When using the `AccurestRule` you can add a stub to download and then pass the port for the last downloaded stub.
[source,java,indent=0]
----
include::src/test/groovy/io/codearte/accurest/stubrunner/junit/AccurestRuleCustomPortJUnitTest.java[tags=classrule_with_port]
----
You can see that for this example the following test is valid:
[source,java,indent=0]
----
include::src/test/groovy/io/codearte/accurest/stubrunner/junit/AccurestRuleCustomPortJUnitTest.java[tags=test_with_port]
----

View File

@@ -1,5 +1,17 @@
package io.codearte.accurest.stubrunner.junit;
import java.net.URL;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
import io.codearte.accurest.dsl.GroovyDsl;
import io.codearte.accurest.stubrunner.BatchStubRunner;
import io.codearte.accurest.stubrunner.BatchStubRunnerFactory;
@@ -9,18 +21,6 @@ import io.codearte.accurest.stubrunner.StubFinder;
import io.codearte.accurest.stubrunner.StubRunnerOptions;
import io.codearte.accurest.stubrunner.util.StringUtils;
import io.codearte.accurest.stubrunner.util.StubsParser;
import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
import java.net.URL;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* JUnit class rule that allows you to download the provided stubs.
@@ -30,7 +30,7 @@ import java.util.Set;
public class AccurestRule implements TestRule, StubFinder {
private static final String DELIMITER = ":";
private Set<String> stubs = new HashSet<String>();
private LinkedList<String> stubs = new LinkedList<>();
private StubRunnerOptions stubRunnerOptions = defaultStubRunnerOptions();
private BatchStubRunner stubFinder;
@@ -63,7 +63,7 @@ public class AccurestRule implements TestRule, StubFinder {
if (StringUtils.hasText(stubsToDownload)) {
Collections.addAll(stubs, stubsToDownload.split(","));
}
return new StubRunnerOptions(minPort, maxPort, repoRoot, workOffline, stubSuffix);
return new StubRunnerOptions(minPort, maxPort, repoRoot, workOffline, stubSuffix, stubsToDownload);
}
/**
@@ -112,7 +112,7 @@ public class AccurestRule implements TestRule, StubFinder {
* Group Id, artifact Id and classifier of a single stub to download
*/
public AccurestRule downloadStub(String groupId, String artifactId, String classifier) {
stubs.add(groupId + DELIMITER + artifactId + DELIMITER + classifier);
addStub(groupId + DELIMITER + artifactId + DELIMITER + classifier);
return this;
}
@@ -120,7 +120,7 @@ public class AccurestRule implements TestRule, StubFinder {
* Group Id, artifact Id of a single stub to download. Default classifier will be picked.
*/
public AccurestRule downloadStub(String groupId, String artifactId) {
stubs.add(groupId + DELIMITER + artifactId);
addStub(groupId + DELIMITER + artifactId);
return this;
}
@@ -128,7 +128,7 @@ public class AccurestRule implements TestRule, StubFinder {
* Ivy notation of a single stub to download.
*/
public AccurestRule downloadStub(String ivyNotation) {
stubs.add(ivyNotation);
addStub(ivyNotation);
return this;
}
@@ -136,7 +136,7 @@ public class AccurestRule implements TestRule, StubFinder {
* Stubs to download in Ivy notations
*/
public AccurestRule downloadStubs(String... ivyNotations) {
stubs.addAll(Arrays.asList(ivyNotations));
addStub(Arrays.asList(ivyNotations));
return this;
}
@@ -144,7 +144,16 @@ public class AccurestRule implements TestRule, StubFinder {
* Stubs to download in Ivy notations
*/
public AccurestRule downloadStubs(List<String> ivyNotations) {
stubs.addAll(ivyNotations);
addStub(ivyNotations);
return this;
}
/**
* Appends port to last added stub
*/
public AccurestRule withPort(Integer port) {
String lastStub = stubs.peekLast();
addPort(lastStub + DELIMITER + port);
return this;
}
@@ -187,4 +196,23 @@ public class AccurestRule implements TestRule, StubFinder {
public Map<String, Collection<String>> labels() {
return stubFinder.labels();
}
private void addStub(String notation) {
if(StubsParser.hasPort(notation)) {
addPort(notation);
stubs.add(StubsParser.ivyFromStringWithPort(notation));
} else {
stubs.add(notation);
}
}
private void addStub(List<String> notations) {
for (String notation : notations) {
addStub(notation);
}
}
private void addPort(String notation) {
stubRunnerOptions.putStubIdsToPortMapping(StubsParser.fromStringWithPort(notation));
}
}

View File

@@ -0,0 +1,68 @@
package io.codearte.accurest.stubrunner.junit;
import java.io.InputStream;
import java.net.URI;
import org.apache.commons.io.IOUtils;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.ClassRule;
import org.junit.Test;
import static org.assertj.core.api.BDDAssertions.then;
/**
* @author Marcin Grzejszczak
*/
public class AccurestRuleCustomPortJUnitTest {
@BeforeClass
@AfterClass
public static void setupProps() {
System.getProperties().setProperty("stubrunner.stubs.repository.root", "");
System.getProperties().setProperty("stubrunner.stubs.classifier", "stubs");
}
// tag::classrule_with_port[]
@ClassRule public static AccurestRule rule = new AccurestRule()
.repoRoot(repoRoot())
.downloadStub("io.codearte.accurest.stubs", "loanIssuance")
.withPort(12345)
.downloadStub("io.codearte.accurest.stubs:fraudDetectionServer:12346");
// end::classrule_with_port[]
@Test
public void should_start_wiremock_servers() throws Exception {
// expect: 'WireMocks are running'
then(rule.findStubUrl("io.codearte.accurest.stubs", "loanIssuance")).isNotNull();
then(rule.findStubUrl("loanIssuance")).isNotNull();
then(rule.findStubUrl("loanIssuance")).isEqualTo(rule.findStubUrl("io.codearte.accurest.stubs", "loanIssuance"));
then(rule.findStubUrl("io.codearte.accurest.stubs:fraudDetectionServer")).isNotNull();
// and:
then(rule.findAllRunningStubs().isPresent("loanIssuance")).isTrue();
then(rule.findAllRunningStubs().isPresent("io.codearte.accurest.stubs", "fraudDetectionServer")).isTrue();
then(rule.findAllRunningStubs().isPresent("io.codearte.accurest.stubs:fraudDetectionServer")).isTrue();
// and: 'Stubs were registered'
then(httpGet(rule.findStubUrl("loanIssuance").toString() + "/name")).isEqualTo("loanIssuance");
then(httpGet(rule.findStubUrl("fraudDetectionServer").toString() + "/name")).isEqualTo("fraudDetectionServer");
// and: 'The port is fixed'
// tag::test_with_port[]
then(rule.findStubUrl("loanIssuance")).isEqualTo(URI.create("http://localhost:12345").toURL());
then(rule.findStubUrl("fraudDetectionServer")).isEqualTo(URI.create("http://localhost:12346").toURL());
// end::test_with_port[]
}
private static String repoRoot() {
try {
return AccurestRuleCustomPortJUnitTest.class.getResource("/m2repo/repository/").toURI().toString();
} catch (Exception e) {
return "";
}
}
private String httpGet(String url) throws Exception {
try(InputStream stream = URI.create(url).toURL().openStream()) {
return IOUtils.toString(stream);
}
}
}

View File

@@ -48,7 +48,7 @@ public class StubRunnerConfiguration {
@Value("${stubrunner.work-offline:false}") boolean workOffline,
@Value("${stubrunner.stubs.ids:}") String stubs) throws IOException {
StubRunnerOptions stubRunnerOptions = new StubRunnerOptions(minPortValue, maxPortValue, uriStringOrEmpty(stubRepositoryRoot),
stubRepositoryRoot == null || workOffline, stubsSuffix);
stubRepositoryRoot == null || workOffline, stubsSuffix, stubs);
Set<StubConfiguration> dependencies = StubsParser.fromString(stubs, stubsSuffix);
BatchStubRunner batchStubRunner = new BatchStubRunnerFactory(stubRunnerOptions, dependencies,
accurestMessaging != null ? accurestMessaging : new NoOpAccurestMessaging()).buildBatchStubRunner();

View File

@@ -30,7 +30,7 @@ class BatchStubRunnerFactory {
BatchStubRunnerFactory(StubRunnerOptions stubRunnerOptions,
Collection<StubConfiguration> dependencies,
StubDownloader stubDownloader) {
this(stubRunnerOptions, dependencies, new GrapeStubDownloader(), new NoOpAccurestMessaging())
this(stubRunnerOptions, dependencies, stubDownloader, new NoOpAccurestMessaging())
}
BatchStubRunnerFactory(StubRunnerOptions stubRunnerOptions,

View File

@@ -46,7 +46,7 @@ class StubRunner implements StubRunning {
@Override
RunningStubs runStubs() {
registerShutdownHook()
return localStubRunner.runStubs(stubRepository, stubsConfiguration)
return localStubRunner.runStubs(stubRunnerOptions,stubRepository, stubsConfiguration)
}
@Override

View File

@@ -28,8 +28,8 @@ class StubRunnerExecutor implements StubFinder {
this.accurestMessaging = new NoOpAccurestMessaging()
}
RunningStubs runStubs(StubRepository repository, StubConfiguration stubConfiguration) {
startStubServers(stubConfiguration, repository)
RunningStubs runStubs(StubRunnerOptions stubRunnerOptions, StubRepository repository, StubConfiguration stubConfiguration) {
startStubServers(stubRunnerOptions, stubConfiguration, repository)
RunningStubs runningCollaborators =
new RunningStubs([(stubServer.stubConfiguration): stubServer.port])
log.info("All stubs are now running [${runningCollaborators.toString()}")
@@ -120,11 +120,16 @@ class StubRunnerExecutor implements StubFinder {
return condition ? stubServer.stubUrl : null
}
private void startStubServers(StubConfiguration stubConfiguration, StubRepository repository) {
private void startStubServers(StubRunnerOptions stubRunnerOptions, StubConfiguration stubConfiguration, StubRepository repository) {
List<WiremockMappingDescriptor> mappings = repository.getProjectDescriptors()
Collection<GroovyDsl> contracts = repository.accurestContracts
stubServer = portScanner.tryToExecuteWithFreePort { int availablePort ->
return new StubServer(availablePort, stubConfiguration, mappings, contracts).start()
Integer port = stubRunnerOptions.port(stubConfiguration)
if (port) {
stubServer = new StubServer(port, stubConfiguration, mappings, contracts).start()
} else {
stubServer = portScanner.tryToExecuteWithFreePort { int availablePort ->
return new StubServer(availablePort, stubConfiguration, mappings, contracts).start()
}
}
}

View File

@@ -2,6 +2,7 @@ package io.codearte.accurest.stubrunner
import groovy.transform.CompileStatic
import groovy.transform.ToString
import io.codearte.accurest.stubrunner.util.StubsParser
/**
* Technical options related to running StubRunner
@@ -30,11 +31,26 @@ class StubRunnerOptions {
*/
boolean workOffline = false
/**
* colon separated list of ids to the desired port
*/
Map<StubConfiguration, Integer> stubIdsToPortMapping = [:]
/**
* stub definition suffix
*/
String stubsClassifier = "stubs"
StubRunnerOptions(Integer minPortValue, Integer maxPortValue, String stubRepositoryRoot,
boolean workOffline, String stubsClassifier, String stubIdsToPortMapping) {
this.minPortValue = minPortValue
this.maxPortValue = maxPortValue
this.stubRepositoryRoot = stubRepositoryRoot
this.workOffline = workOffline
this.stubsClassifier = stubsClassifier
this.stubIdsToPortMapping = stubIdsWithPortsFromString(stubIdsToPortMapping)
}
StubRunnerOptions(Integer minPortValue, Integer maxPortValue, String stubRepositoryRoot,
boolean workOffline, String stubsClassifier) {
this.minPortValue = minPortValue
@@ -49,4 +65,26 @@ class StubRunnerOptions {
}
StubRunnerOptions() {}
Map<StubConfiguration, Integer> stubIdsWithPortsFromString(String stubIdsToPortMapping) {
return stubIdsToPortMapping.split(',').collectEntries { String entry ->
return StubsParser.fromStringWithPort(entry)
}
}
Integer port(StubConfiguration stubConfiguration) {
return stubIdsToPortMapping[stubConfiguration]
}
void setStubIdsToPortMapping(Map<StubConfiguration, Integer> stubIdsToPortMapping) {
this.stubIdsToPortMapping = stubIdsToPortMapping
}
void putStubIdsToPortMapping(Map<StubConfiguration, Integer> stubIdsToPortMapping) {
this.stubIdsToPortMapping.putAll(stubIdsToPortMapping)
}
void setStubIdsToPortMapping(String stubIdsToPortMapping) {
this.stubIdsToPortMapping = stubIdsWithPortsFromString(stubIdsToPortMapping)
}
}

View File

@@ -25,14 +25,41 @@ class StubsParser {
* "a:b,c:d:e"
*/
static Set<StubConfiguration> fromString(String list, String defaultClassifier) {
return list.split(',').findAll { it }.collect { String string ->
new StubConfiguration(string, defaultClassifier)
} as Set
def splitList = list.split(',').findAll { it }
return fromString(splitList, defaultClassifier)
}
static Set<StubConfiguration> fromString(Collection<String> collection, String defaultClassifier) {
return collection.findAll { it }.collect { String string ->
new StubConfiguration(string, defaultClassifier)
return collection.findAll { it }.collect { String entry ->
def splitEntry = entry.split(':')
if (splitEntry.last().isInteger()) {
String id = entry - ":${splitEntry.last()}"
new StubConfiguration(id, defaultClassifier)
}
new StubConfiguration(entry, defaultClassifier)
} as Set
}
static Map<StubConfiguration, Integer> fromStringWithPort(String notation) {
def splitEntry = notation.split(':')
if (!splitEntry.last().isInteger()) {
return [:]
}
Integer port = splitEntry.last().toInteger()
String id = notation - ":${splitEntry.last()}"
return [(new StubConfiguration(id)): port]
}
static String ivyFromStringWithPort(String notation) {
def splitEntry = notation.split(':')
if (!splitEntry.last().isInteger()) {
return ''
}
return notation - ":${splitEntry.last()}"
}
static boolean hasPort(String notation) {
def splitEntry = notation.split(':')
return splitEntry.last().isInteger()
}
}

View File

@@ -11,6 +11,7 @@ class StubRunnerExecutorSpec extends Specification {
private AvailablePortScanner portScanner
private StubRepository repository
private StubConfiguration stub = new StubConfiguration("group:artifact", "stubs")
private StubRunnerOptions stubRunnerOptions = new StubRunnerOptions()
def setup() {
portScanner = new AvailablePortScanner(MIN_PORT, MAX_PORT)
@@ -21,7 +22,7 @@ class StubRunnerExecutorSpec extends Specification {
given:
StubRunnerExecutor executor = new StubRunnerExecutor(portScanner)
when:
executor.runStubs(repository, stub)
executor.runStubs(stubRunnerOptions, repository, stub)
then:
executor.findStubUrl("group", "artifact") == EXPECTED_STUB_URL
and:
@@ -36,9 +37,19 @@ class StubRunnerExecutorSpec extends Specification {
given:
StubRunnerExecutor executor = new StubRunnerExecutor(portScanner)
when:
executor.runStubs(repository, stub)
executor.runStubs(stubRunnerOptions, repository, stub)
then:
!executor.findStubUrl("unkowngroup", "unknownartifact")
}
def 'should start a stub on a given port'() {
given:
StubRunnerExecutor executor = new StubRunnerExecutor(portScanner)
stubRunnerOptions.setStubIdsToPortMapping('group:artifact:12345,someotherartifact:123')
when:
executor.runStubs(stubRunnerOptions, repository, stub)
then:
executor.findStubUrl("group", "artifact") == 'http://localhost:12345'.toURL()
}
}