diff --git a/stub-runner/stub-runner-junit/README.adoc b/stub-runner/stub-runner-junit/README.adoc index 5a778ae104..8a512100ac 100644 --- a/stub-runner/stub-runner-junit/README.adoc +++ b/stub-runner/stub-runner-junit/README.adoc @@ -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] +---- + + diff --git a/stub-runner/stub-runner-junit/src/main/groovy/io/codearte/accurest/stubrunner/junit/AccurestRule.java b/stub-runner/stub-runner-junit/src/main/groovy/io/codearte/accurest/stubrunner/junit/AccurestRule.java index 7f29aaab71..ab31e7ff74 100644 --- a/stub-runner/stub-runner-junit/src/main/groovy/io/codearte/accurest/stubrunner/junit/AccurestRule.java +++ b/stub-runner/stub-runner-junit/src/main/groovy/io/codearte/accurest/stubrunner/junit/AccurestRule.java @@ -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 stubs = new HashSet(); + private LinkedList 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 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> 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 notations) { + for (String notation : notations) { + addStub(notation); + } + } + + private void addPort(String notation) { + stubRunnerOptions.putStubIdsToPortMapping(StubsParser.fromStringWithPort(notation)); + } } diff --git a/stub-runner/stub-runner-junit/src/test/groovy/io/codearte/accurest/stubrunner/junit/AccurestRuleCustomPortJUnitTest.java b/stub-runner/stub-runner-junit/src/test/groovy/io/codearte/accurest/stubrunner/junit/AccurestRuleCustomPortJUnitTest.java new file mode 100644 index 0000000000..ef05510e74 --- /dev/null +++ b/stub-runner/stub-runner-junit/src/test/groovy/io/codearte/accurest/stubrunner/junit/AccurestRuleCustomPortJUnitTest.java @@ -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); + } + } +} diff --git a/stub-runner/stub-runner-spring/src/main/groovy/io/codearte/accurest/stubrunner/spring/StubRunnerConfiguration.java b/stub-runner/stub-runner-spring/src/main/groovy/io/codearte/accurest/stubrunner/spring/StubRunnerConfiguration.java index a375442b2c..44f959d210 100644 --- a/stub-runner/stub-runner-spring/src/main/groovy/io/codearte/accurest/stubrunner/spring/StubRunnerConfiguration.java +++ b/stub-runner/stub-runner-spring/src/main/groovy/io/codearte/accurest/stubrunner/spring/StubRunnerConfiguration.java @@ -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 dependencies = StubsParser.fromString(stubs, stubsSuffix); BatchStubRunner batchStubRunner = new BatchStubRunnerFactory(stubRunnerOptions, dependencies, accurestMessaging != null ? accurestMessaging : new NoOpAccurestMessaging()).buildBatchStubRunner(); diff --git a/stub-runner/stub-runner/src/main/groovy/io/codearte/accurest/stubrunner/BatchStubRunnerFactory.groovy b/stub-runner/stub-runner/src/main/groovy/io/codearte/accurest/stubrunner/BatchStubRunnerFactory.groovy index a7e3412fa9..9b1a03929d 100644 --- a/stub-runner/stub-runner/src/main/groovy/io/codearte/accurest/stubrunner/BatchStubRunnerFactory.groovy +++ b/stub-runner/stub-runner/src/main/groovy/io/codearte/accurest/stubrunner/BatchStubRunnerFactory.groovy @@ -30,7 +30,7 @@ class BatchStubRunnerFactory { BatchStubRunnerFactory(StubRunnerOptions stubRunnerOptions, Collection dependencies, StubDownloader stubDownloader) { - this(stubRunnerOptions, dependencies, new GrapeStubDownloader(), new NoOpAccurestMessaging()) + this(stubRunnerOptions, dependencies, stubDownloader, new NoOpAccurestMessaging()) } BatchStubRunnerFactory(StubRunnerOptions stubRunnerOptions, diff --git a/stub-runner/stub-runner/src/main/groovy/io/codearte/accurest/stubrunner/StubRunner.groovy b/stub-runner/stub-runner/src/main/groovy/io/codearte/accurest/stubrunner/StubRunner.groovy index 6f7a458c5d..c525646c5e 100644 --- a/stub-runner/stub-runner/src/main/groovy/io/codearte/accurest/stubrunner/StubRunner.groovy +++ b/stub-runner/stub-runner/src/main/groovy/io/codearte/accurest/stubrunner/StubRunner.groovy @@ -46,7 +46,7 @@ class StubRunner implements StubRunning { @Override RunningStubs runStubs() { registerShutdownHook() - return localStubRunner.runStubs(stubRepository, stubsConfiguration) + return localStubRunner.runStubs(stubRunnerOptions,stubRepository, stubsConfiguration) } @Override diff --git a/stub-runner/stub-runner/src/main/groovy/io/codearte/accurest/stubrunner/StubRunnerExecutor.groovy b/stub-runner/stub-runner/src/main/groovy/io/codearte/accurest/stubrunner/StubRunnerExecutor.groovy index c7c0fe1dd7..0f30354e36 100644 --- a/stub-runner/stub-runner/src/main/groovy/io/codearte/accurest/stubrunner/StubRunnerExecutor.groovy +++ b/stub-runner/stub-runner/src/main/groovy/io/codearte/accurest/stubrunner/StubRunnerExecutor.groovy @@ -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 mappings = repository.getProjectDescriptors() Collection 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() + } } } diff --git a/stub-runner/stub-runner/src/main/groovy/io/codearte/accurest/stubrunner/StubRunnerOptions.groovy b/stub-runner/stub-runner/src/main/groovy/io/codearte/accurest/stubrunner/StubRunnerOptions.groovy index 8e6b3d7cd0..9343f59e81 100644 --- a/stub-runner/stub-runner/src/main/groovy/io/codearte/accurest/stubrunner/StubRunnerOptions.groovy +++ b/stub-runner/stub-runner/src/main/groovy/io/codearte/accurest/stubrunner/StubRunnerOptions.groovy @@ -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 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 stubIdsWithPortsFromString(String stubIdsToPortMapping) { + return stubIdsToPortMapping.split(',').collectEntries { String entry -> + return StubsParser.fromStringWithPort(entry) + } + } + + Integer port(StubConfiguration stubConfiguration) { + return stubIdsToPortMapping[stubConfiguration] + } + + void setStubIdsToPortMapping(Map stubIdsToPortMapping) { + this.stubIdsToPortMapping = stubIdsToPortMapping + } + + void putStubIdsToPortMapping(Map stubIdsToPortMapping) { + this.stubIdsToPortMapping.putAll(stubIdsToPortMapping) + } + + void setStubIdsToPortMapping(String stubIdsToPortMapping) { + this.stubIdsToPortMapping = stubIdsWithPortsFromString(stubIdsToPortMapping) + } } diff --git a/stub-runner/stub-runner/src/main/groovy/io/codearte/accurest/stubrunner/util/StubsParser.groovy b/stub-runner/stub-runner/src/main/groovy/io/codearte/accurest/stubrunner/util/StubsParser.groovy index 9749dec904..ae06a9bfe2 100644 --- a/stub-runner/stub-runner/src/main/groovy/io/codearte/accurest/stubrunner/util/StubsParser.groovy +++ b/stub-runner/stub-runner/src/main/groovy/io/codearte/accurest/stubrunner/util/StubsParser.groovy @@ -25,14 +25,41 @@ class StubsParser { * "a:b,c:d:e" */ static Set 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 fromString(Collection 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 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() + } } \ No newline at end of file diff --git a/stub-runner/stub-runner/src/test/groovy/io/codearte/accurest/stubrunner/StubRunnerExecutorSpec.groovy b/stub-runner/stub-runner/src/test/groovy/io/codearte/accurest/stubrunner/StubRunnerExecutorSpec.groovy index 8548ea38c4..fc30f2978e 100644 --- a/stub-runner/stub-runner/src/test/groovy/io/codearte/accurest/stubrunner/StubRunnerExecutorSpec.groovy +++ b/stub-runner/stub-runner/src/test/groovy/io/codearte/accurest/stubrunner/StubRunnerExecutorSpec.groovy @@ -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() + } + }