Fails on no stubs for stubs Per Consumer

without this change the JAR gets downloaded, it contains mappings but there are no mappings that are actually matching the given consumer. Regardless, the WireMock server starts with no mappings
with this change an exception is being thrown in that case

fixes gh-2114
This commit is contained in:
Marcin Grzejszczak
2024-06-27 14:08:33 +02:00
parent 03162b651c
commit 56673bde4d
7 changed files with 114 additions and 17 deletions

View File

@@ -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<HttpServerStub> httpServerStubs, StubRunnerOptions options) {
StubRepository(File repository, List<HttpServerStub> 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() {

View File

@@ -64,7 +64,8 @@ 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.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);

View File

@@ -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<File> 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<File> 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<File> descriptors = repository.stubs

View File

@@ -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

View File

@@ -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:

View File

@@ -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
}
}
}
}

View File

@@ -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: