diff --git a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubRepository.java b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubRepository.java index 40595babc8..86647d8381 100644 --- a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubRepository.java +++ b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubRepository.java @@ -29,6 +29,7 @@ import java.util.Collection; import java.util.Collections; import java.util.List; +import io.micrometer.common.lang.Nullable; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -57,7 +58,8 @@ class StubRepository { private final StubRunnerOptions options; - StubRepository(File repository, List httpServerStubs, StubRunnerOptions options) { + StubRepository(File repository, List httpServerStubs, StubRunnerOptions options, + @Nullable StubConfiguration stubConfiguration) { if (!repository.isDirectory()) { throw new IllegalArgumentException("Missing descriptor repository under path [" + repository + "]"); } @@ -70,13 +72,18 @@ class StubRepository { this.options = options; this.stubs = stubs(); this.contracts = contracts(); + if (options.isFailOnNoStubs() && this.stubs.isEmpty() && this.contracts.isEmpty()) { + throw new IllegalStateException("No stubs or contracts were found for [" + + (stubConfiguration != null ? stubConfiguration.toColonSeparatedDependencyNotation() : null) + + "] and the switch to fail on no stubs was set."); + } if (log.isTraceEnabled()) { log.trace("Found the following contracts " + this.contracts); } } StubRepository(File repository) { - this(repository, new ArrayList<>(), new StubRunnerOptionsBuilder().build()); + this(repository, new ArrayList<>(), new StubRunnerOptionsBuilder().build(), null); } public File getPath() { diff --git a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubRunner.java b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubRunner.java index 54d3c4a5da..7a5a09399d 100644 --- a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubRunner.java +++ b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubRunner.java @@ -64,7 +64,8 @@ public class StubRunner implements StubRunning { this.stubsConfiguration = stubsConfiguration; this.stubRunnerOptions = stubRunnerOptions; List serverStubs = SpringFactoriesLoader.loadFactories(HttpServerStub.class, null); - this.stubRepository = new StubRepository(new File(repositoryPath), serverStubs, this.stubRunnerOptions); + this.stubRepository = new StubRepository(new File(repositoryPath), serverStubs, this.stubRunnerOptions, + stubsConfiguration); AvailablePortScanner portScanner = new AvailablePortScanner(stubRunnerOptions.getMinPortValue(), stubRunnerOptions.getMaxPortValue()); this.localStubRunner = new StubRunnerExecutor(portScanner, contractVerifierMessaging, serverStubs); diff --git a/spring-cloud-contract-stub-runner/src/test/groovy/org/springframework/cloud/contract/stubrunner/StubRepositorySpec.groovy b/spring-cloud-contract-stub-runner/src/test/groovy/org/springframework/cloud/contract/stubrunner/StubRepositorySpec.groovy index 290eba00ab..c13e39ee1d 100644 --- a/spring-cloud-contract-stub-runner/src/test/groovy/org/springframework/cloud/contract/stubrunner/StubRepositorySpec.groovy +++ b/spring-cloud-contract-stub-runner/src/test/groovy/org/springframework/cloud/contract/stubrunner/StubRepositorySpec.groovy @@ -25,7 +25,7 @@ class StubRepositorySpec extends Specification { def 'should retrieve all descriptors for given project'() { given: StubRepository repository = new StubRepository(REPOSITORY_LOCATION, - [], new StubRunnerOptionsBuilder().build()) + [], new StubRunnerOptionsBuilder().build(), null) int expectedDescriptorsSize = 8 when: List descriptors = repository.getStubs() @@ -33,20 +33,18 @@ class StubRepositorySpec extends Specification { descriptors.size() == expectedDescriptorsSize } - def 'should return empty list if files are missing'() { - given: - StubRepository repository = new StubRepository(new File('src/test/resources/emptyrepo'), - [], new StubRunnerOptionsBuilder().build()) + def 'should throw an exception when no stubs or contracts are present'() { when: - List descriptors = repository.getStubs() + new StubRepository(new File('src/test/resources/emptyrepo'), + [], new StubRunnerOptionsBuilder().build(), null) then: - descriptors.empty + thrown(IllegalStateException) } def 'should throw an exception if directory with mappings is missing'() { when: new StubRepository(new File('src/test/resources/nonexistingrepo'), [], - new StubRunnerOptionsBuilder().build()) + new StubRunnerOptionsBuilder().build(), null) then: thrown(IllegalArgumentException) } @@ -56,7 +54,7 @@ class StubRepositorySpec extends Specification { StubRepository repository = new StubRepository(REPOSITORY_LOCATION, [], new StubRunnerOptionsBuilder() .withStubPerConsumer(true) - .withConsumerName("ping").build()) + .withConsumerName("ping").build(), null) int expectedDescriptorsSize = 1 when: List descriptors = repository.stubs diff --git a/spring-cloud-contract-stub-runner/src/test/groovy/org/springframework/cloud/contract/stubrunner/StubRunnerExecutorSpec.groovy b/spring-cloud-contract-stub-runner/src/test/groovy/org/springframework/cloud/contract/stubrunner/StubRunnerExecutorSpec.groovy index b76f97787b..d346964ae0 100644 --- a/spring-cloud-contract-stub-runner/src/test/groovy/org/springframework/cloud/contract/stubrunner/StubRunnerExecutorSpec.groovy +++ b/spring-cloud-contract-stub-runner/src/test/groovy/org/springframework/cloud/contract/stubrunner/StubRunnerExecutorSpec.groovy @@ -40,7 +40,7 @@ class StubRunnerExecutorSpec extends Specification { def setup() { portScanner = new AvailablePortScanner(MIN_PORT, MAX_PORT) repository = new StubRepository(new File('src/test/resources/repository'), - [], new StubRunnerOptionsBuilder().build()) + [], stubRunnerOptions, null) } def 'should provide URL for given relative path of stub'() { @@ -173,7 +173,7 @@ class StubRunnerExecutorSpec extends Specification { when: executor.runStubs(stubRunnerOptions, new StubRepository(new File('src/test/resources/repository/httpcontract'), - [], new StubRunnerOptionsBuilder().build()), stubConf) + [], new StubRunnerOptionsBuilder().build(), null), stubConf) then: !executor.trigger() !executor.trigger("missing", "label") @@ -186,10 +186,11 @@ class StubRunnerExecutorSpec extends Specification { given: def stubConf = new StubConfiguration('asd', 'asd', 'asd', '') StubRunnerExecutor executor = new StubRunnerExecutor(portScanner) + stubRunnerOptions = new StubRunnerOptionsBuilder().withFailOnNoStubs(false).build() when: RunningStubs stubs = executor.runStubs(stubRunnerOptions, new StubRepository(new File('src/test/resources/emptyrepo'), - [], new StubRunnerOptionsBuilder().build()), stubConf) + [], stubRunnerOptions, null), stubConf) then: stubs.getPort('asd') == -1 cleanup: @@ -204,7 +205,7 @@ class StubRunnerExecutorSpec extends Specification { def stubConf = new StubConfiguration('asd', 'asd', 'asd', '') executor.runStubs(stubRunnerOptions, new StubRepository(new File('src/test/resources/messages'), - [], new StubRunnerOptionsBuilder().build()), stubConf) + [], new StubRunnerOptionsBuilder().build(), null), stubConf) boolean triggered = executor.trigger("trigger") then: triggered diff --git a/spring-cloud-contract-stub-runner/src/test/groovy/org/springframework/cloud/contract/stubrunner/StubRunnerFactorySpec.groovy b/spring-cloud-contract-stub-runner/src/test/groovy/org/springframework/cloud/contract/stubrunner/StubRunnerFactorySpec.groovy index 46fa8a08b3..4f1b575ee7 100644 --- a/spring-cloud-contract-stub-runner/src/test/groovy/org/springframework/cloud/contract/stubrunner/StubRunnerFactorySpec.groovy +++ b/spring-cloud-contract-stub-runner/src/test/groovy/org/springframework/cloud/contract/stubrunner/StubRunnerFactorySpec.groovy @@ -24,6 +24,22 @@ import org.springframework.cloud.contract.verifier.messaging.noop.NoOpStubMessag class StubRunnerFactorySpec extends Specification { + static final String MAPPING = ''' +{ + "request": { + "method": "GET", + "url": "/hello" + }, + "response": { + "status": 200, + "body": "Hello world!", + "headers": { + "Content-Type": "text/plain" + } + } +} +''' + @Rule TemporaryFolder folder = new TemporaryFolder() @@ -42,6 +58,7 @@ class StubRunnerFactorySpec extends Specification { def "Should download stub definitions many times"() { given: folder.newFolder("mappings") + folder.newFile("hello.json").text = MAPPING 1 * downloader.downloadAndUnpackStubJar(_) >> new AbstractMap.SimpleEntry(new StubConfiguration('a:b'), folder.root) 1 * downloader.downloadAndUnpackStubJar(_) >> new AbstractMap.SimpleEntry(new StubConfiguration('c:d'), folder.root) when: diff --git a/spring-cloud-contract-stub-runner/src/test/groovy/org/springframework/cloud/contract/stubrunner/spring/cloud/StubRunnerStubsPerConsumerNotMatchingConsumerSpec.groovy b/spring-cloud-contract-stub-runner/src/test/groovy/org/springframework/cloud/contract/stubrunner/spring/cloud/StubRunnerStubsPerConsumerNotMatchingConsumerSpec.groovy new file mode 100644 index 0000000000..948cb2881d --- /dev/null +++ b/spring-cloud-contract-stub-runner/src/test/groovy/org/springframework/cloud/contract/stubrunner/spring/cloud/StubRunnerStubsPerConsumerNotMatchingConsumerSpec.groovy @@ -0,0 +1,73 @@ +/* + * Copyright 2013-2020 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 + * + * https://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 java.util.function.Function + +import org.junit.jupiter.api.Test + +import org.springframework.boot.autoconfigure.AutoConfigurations +import org.springframework.boot.autoconfigure.EnableAutoConfiguration +import org.springframework.boot.autoconfigure.ImportAutoConfiguration +import org.springframework.boot.test.context.runner.ApplicationContextRunner +import org.springframework.cloud.contract.stubrunner.spring.AutoConfigureStubRunner +import org.springframework.cloud.contract.stubrunner.spring.StubRunnerConfiguration +import org.springframework.cloud.contract.stubrunner.spring.StubRunnerProperties +import org.springframework.cloud.stream.binder.test.TestChannelBinderConfiguration +import org.springframework.cloud.zookeeper.ZookeeperAutoConfiguration +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import org.springframework.context.annotation.Import +import org.springframework.test.context.ActiveProfiles + +import static org.assertj.core.api.Assertions.assertThat + +@AutoConfigureStubRunner(ids = "org.springframework.cloud.contract.verifier.stubs:producerWithMultipleConsumers", + repositoryRoot = "classpath:m2repo/repository/", + stubsMode = StubRunnerProperties.StubsMode.REMOTE, + stubsPerConsumer = true) +@ActiveProfiles("streamconsumer") +class StubRunnerStubsPerConsumerNotMatchingConsumerSpec { + + private ApplicationContextRunner contextRunner = new ApplicationContextRunner() + .withConfiguration(AutoConfigurations.of(Config)) + .withPropertyValues("spring.application.name=bar-not-matching-consumer", + "stubrunner.jms.enabled=false", "spring.profiles.active=streamconsumer", "spring.cloud.discovery.enabled=false", "spring.cloud.service-registry.auto-registration.enabled=false", "stubrunner.ids=org.springframework.cloud.contract.verifier.stubs:producerWithMultipleConsumers", "stubrunner.repositoryRoot=classpath:m2repo/repository/", "stubrunner.stubsMode=REMOTE", "stubrunner.stubsPerConsumer=true"); + + @Test + void 'should fail to start when JAR is found but there are no stubs or contracts'() { + expect: + this.contextRunner.run((context) -> { + assertThat(context).hasFailed(); + assertThat(context.getStartupFailure()).hasMessageContaining("No stubs or contracts were found for [org.springframework.cloud.contract.verifier.stubs:producerWithMultipleConsumers:0.0.1-SNAPSHOT:stubs] and the switch to fail on no stubs was set") + }); + } + + @Configuration + @EnableAutoConfiguration(exclude = [ZookeeperAutoConfiguration]) + @ImportAutoConfiguration(TestChannelBinderConfiguration.class) + @Import(value = StubRunnerConfiguration) + static class Config { + @Bean + Function output() { + return { Object o -> + println(o) + return o + } + } + } +} diff --git a/spring-cloud-contract-stub-runner/src/test/java/org/springframework/cloud/contract/stubrunner/StubRepositoryTest.java b/spring-cloud-contract-stub-runner/src/test/java/org/springframework/cloud/contract/stubrunner/StubRepositoryTest.java index 09f4c66b4a..3c57b0af21 100644 --- a/spring-cloud-contract-stub-runner/src/test/java/org/springframework/cloud/contract/stubrunner/StubRepositoryTest.java +++ b/spring-cloud-contract-stub-runner/src/test/java/org/springframework/cloud/contract/stubrunner/StubRepositoryTest.java @@ -36,7 +36,7 @@ public class StubRepositoryTest { public void should_prefer_custom_yaml_converter_over_standard() { // given: StubRepository repository = new StubRepository(YAML_REPOSITORY_LOCATION, new ArrayList<>(), - new StubRunnerOptionsBuilder().build()); + new StubRunnerOptionsBuilder().build(), null); int expectedDescriptorsSize = 1; // when: