Bumped WireMock to 2.25.0

fixes gh-1230
fixes gh-1231
This commit is contained in:
Marcin Grzejszczak
2019-10-01 14:39:43 +02:00
parent 19af0301d3
commit 25ada9ea0a
21 changed files with 602 additions and 354 deletions

View File

@@ -57,7 +57,8 @@
<groovy.version>2.5.8</groovy.version>
<!-- We need to have compatibility with Boot -->
<maven.version>3.5.4</maven.version>
<maven.resolver.version>1.4.1</maven.resolver.version>
<!-- Resolver has to be aligned with Maven (e.g resolver 1.4 and Maven 3.6 or resolver 1.3 and Maven 3.5) -->
<maven.resolver.version>1.3.3</maven.resolver.version>
<xpath2.processor.version>2.1.100</xpath2.processor.version>
<xerces.version>2.11.0</xerces.version>
<jacoco-maven-plugin.version>0.8.4</jacoco-maven-plugin.version>

View File

@@ -15,7 +15,7 @@
<name>spring-cloud-contract-dependencies</name>
<description>Spring Cloud Contract Dependencies</description>
<properties>
<wiremock.version>2.24.1</wiremock.version>
<wiremock.version>2.25.0</wiremock.version>
<jsonassert.version>0.4.13</jsonassert.version>
<rest-assured.version>4.0.0</rest-assured.version>
</properties>

View File

@@ -21,6 +21,7 @@ import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
@@ -30,7 +31,6 @@ import java.util.Set;
import groovy.json.JsonOutput;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import wiremock.org.eclipse.jetty.util.ConcurrentHashSet;
import org.springframework.cloud.contract.spec.Contract;
import org.springframework.cloud.contract.spec.internal.DslProperty;
@@ -47,7 +47,8 @@ import org.springframework.cloud.contract.verifier.util.BodyExtractor;
*/
class StubRunnerExecutor implements StubFinder {
static final Set<StubServer> STUB_SERVERS = new ConcurrentHashSet<>();
static final Set<StubServer> STUB_SERVERS = Collections
.synchronizedSet(new HashSet<>());
private static final Log log = LogFactory.getLog(StubRunnerExecutor.class);

View File

@@ -22,6 +22,10 @@
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-contract-spec</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-contract-shade</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-contract-verifier</artifactId>

View File

@@ -33,7 +33,6 @@ import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import wiremock.com.google.common.collect.ListMultimap;
import org.springframework.cloud.contract.spec.Contract;
import org.springframework.cloud.contract.verifier.config.ContractVerifierConfigProperties;
@@ -42,6 +41,7 @@ import org.springframework.cloud.contract.verifier.file.ContractFileScannerBuild
import org.springframework.cloud.contract.verifier.file.ContractMetadata;
import org.springframework.cloud.contract.verifier.util.NamesUtil;
import org.springframework.cloud.contract.verifier.wiremock.DslToWireMockClientConverter;
import org.springframework.util.MultiValueMap;
import org.springframework.util.StringUtils;
/**
@@ -117,12 +117,12 @@ public class RecursiveFilesConverter {
.baseDir(contractsDslDir).excluded(new HashSet<>(excludedFiles))
.ignored(new HashSet<>()).included(new HashSet<>())
.includeMatcher(includedContracts).build();
ListMultimap<Path, ContractMetadata> contracts = scanner.findContracts();
MultiValueMap<Path, ContractMetadata> contracts = scanner
.findContractsRecursively();
if (log.isDebugEnabled()) {
log.debug("Found the following contracts " + contracts);
}
for (Map.Entry<Path, Collection<ContractMetadata>> entry : contracts.asMap()
.entrySet()) {
for (Map.Entry<Path, List<ContractMetadata>> entry : contracts.entrySet()) {
for (ContractMetadata contract : entry.getValue()) {
if (log.isDebugEnabled()) {
log.debug("Will create a stub for contract [" + contract + "]");

View File

@@ -13,7 +13,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
wiremockVersion=2.24.1
wiremockVersion=2.25.0
jsonAssertVersion=0.4.13
verifierVersion=2.2.0.BUILD-SNAPSHOT
groovyVersion=2.4.17

View File

@@ -13,7 +13,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
wiremockVersion=2.24.1
wiremockVersion=2.25.0
jsonAssertVersion=0.4.13
verifierVersion=2.2.0.BUILD-SNAPSHOT
bootVersion=2.2.0.BUILD-SNAPSHOT

View File

@@ -13,7 +13,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
wiremockVersion=2.24.1
wiremockVersion=2.25.0
jsonAssertVersion=0.4.13
verifierVersion=2.2.0.BUILD-SNAPSHOT
bootVersion=2.2.0.BUILD-SNAPSHOT

View File

@@ -21,6 +21,10 @@
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-contract-spec</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-contract-shade</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-test-autoconfigure</artifactId>

View File

@@ -20,11 +20,11 @@ import java.nio.charset.StandardCharsets
import java.nio.file.Path
import java.util.concurrent.atomic.AtomicInteger
import groovy.transform.CompileDynamic
import groovy.transform.CompileStatic
import groovy.transform.PackageScope
import org.apache.commons.logging.Log
import org.apache.commons.logging.LogFactory
import wiremock.com.google.common.collect.ListMultimap
import org.springframework.cloud.contract.spec.ContractVerifierException
import org.springframework.cloud.contract.verifier.builder.JavaTestGenerator
@@ -35,6 +35,7 @@ import org.springframework.cloud.contract.verifier.file.ContractFileScannerBuild
import org.springframework.cloud.contract.verifier.file.ContractMetadata
import org.springframework.cloud.contract.verifier.util.NamesUtil
import org.springframework.core.io.support.SpringFactoriesLoader
import org.springframework.util.MultiValueMap
import org.springframework.util.StringUtils
import static org.springframework.cloud.contract.verifier.util.NamesUtil.afterLast
@@ -121,31 +122,38 @@ class TestGenerator {
@PackageScope
void generateTestClasses(final String basePackageName) {
ListMultimap<Path, ContractMetadata> contracts = contractFileScanner.
findContracts()
MultiValueMap<Path, ContractMetadata> contracts = contractFileScanner.
findContractsRecursively()
if (log.isDebugEnabled()) {
log.debug("Found the following contracts " + contracts.keySet())
}
Set<Map.Entry<Path,Collection<ContractMetadata>>> inProgress = contracts.asMap().entrySet()
.findAll { Map.Entry<Path, Collection<ContractMetadata>> entry -> entry.value.any { it.anyInProgress() }}
Set<Map.Entry<Path,List<ContractMetadata>>> inProgress = inProgress(contracts)
if (!inProgress.isEmpty() && configProperties.failOnInProgress) {
throw new IllegalStateException("In progress contracts found in paths [" + inProgress.collect { it.key.toString() }.join(",") + "] and the switch [failOnInProgress] is set to [true]. Either unmark those contracts as in progress, or set the switch to [false].")
}
processAllNotInProgress(contracts,basePackageName)
}
@PackageScope Set<Map.Entry<Path,Collection<ContractMetadata>>> processAllNotInProgress(ListMultimap<Path,ContractMetadata> contracts, String basePackageName) {
contracts.asMap().entrySet()
.findAll { Map.Entry<Path, Collection<ContractMetadata>> entry -> !entry.value.any { it.anyInProgress() }}
@CompileDynamic
private Set<Map.Entry<Path,List<ContractMetadata>>> inProgress(MultiValueMap<Path,ContractMetadata> contracts) {
return contracts.entrySet()
.findAll { Map.Entry<Path, List<ContractMetadata>> entry -> entry.getValue().any { it.anyInProgress() }}
}
@PackageScope
@CompileDynamic
Set<Map.Entry<Path,List<ContractMetadata>>> processAllNotInProgress(MultiValueMap<Path,ContractMetadata> contracts, String basePackageName) {
contracts.entrySet()
.findAll { Map.Entry<Path, List<ContractMetadata>> entry -> !entry.value.any { it.anyInProgress() }}
.each {
Map.Entry<Path, Collection<ContractMetadata>> entry ->
Map.Entry<Path, List<ContractMetadata>> entry ->
processIncludedDirectory(
relativizeContractPath(entry), (Collection<ContractMetadata>) entry.
getValue(), basePackageName)
}
}
private String relativizeContractPath(Map.Entry<Path, Collection<ContractMetadata>> entry) {
private String relativizeContractPath(Map.Entry<Path, List<ContractMetadata>> entry) {
Path relativePath = configProperties.contractsDslDir.toPath().
relativize(entry.getKey())
if (StringUtils.isEmpty(relativePath.toString())) {

View File

@@ -40,6 +40,6 @@ public enum TestMode {
/**
* Uses Spring's reactive WebTestClient.
*/
WEBTESTCLIENT;
WEBTESTCLIENT
}

View File

@@ -24,15 +24,18 @@ import java.util.regex.Pattern
import groovy.transform.CompileStatic
import groovy.util.logging.Commons
import wiremock.com.google.common.collect.ArrayListMultimap
import wiremock.com.google.common.collect.HashMultiset
import wiremock.com.google.common.collect.ListMultimap
import wiremock.com.google.common.collect.Multimap
import wiremock.com.google.common.collect.Multiset
import org.springframework.cloud.contract.spec.Contract
import org.springframework.cloud.contract.spec.ContractConverter
import org.springframework.cloud.contract.verifier.converter.YamlContractConverter
import org.springframework.cloud.contract.verifier.util.ContractVerifierDslConverter
import org.springframework.core.io.support.SpringFactoriesLoader
import org.springframework.util.CollectionUtils
import org.springframework.util.MultiValueMap
/**
* Scans the provided file path for the DSLs. There's a possibility to provide
* inclusion and exclusion filters.
@@ -85,9 +88,136 @@ class ContractFileScanner {
/**
* @return for a map of paths for which a list of matching contracts has been found
* @deprecated use the {@link ContractFileScanner#findContractsRecursively} version
*/
@Deprecated
ListMultimap<Path, ContractMetadata> findContracts() {
ListMultimap<Path, ContractMetadata> result = ArrayListMultimap.create()
MultiValueMap<Path, ContractMetadata> contracts = findContractsRecursively();
return new ListMultimap<Path, ContractMetadata>() {
@Override
List<ContractMetadata> get(Path key) {
return contracts.get(key)
}
@Override
List<ContractMetadata> removeAll(Object key) {
return contracts.remove(key)
}
@Override
List<ContractMetadata> replaceValues(Path key, Iterable<? extends ContractMetadata> values) {
return contracts.put(key, asList(values))
}
private static List<? extends ContractMetadata> asList(Iterable<? extends ContractMetadata> self) {
if (self instanceof List) {
return (List<? extends ContractMetadata>) self;
} else {
return toList(self.iterator());
}
}
private static List<? extends ContractMetadata> toList(Iterator<? extends ContractMetadata> self) {
List<? extends ContractMetadata> answer = new ArrayList<>();
while (self.hasNext()) {
answer.add(self.next());
}
return answer;
}
@Override
Map<Path, Collection<ContractMetadata>> asMap() {
return contracts.collectEntries {
[(it.key): (Collection<ContractMetadata>) it.value]
} as Map<Path, Collection<ContractMetadata>>
}
@Override
int size() {
return contracts.size()
}
@Override
boolean isEmpty() {
return contracts.isEmpty()
}
@Override
boolean containsKey(Object key) {
return contracts.containsKey(key)
}
@Override
boolean containsValue(Object value) {
return contracts.findResult { it.value.contains(value) }
}
@Override
boolean containsEntry(Object key, Object value) {
return contracts.findResult { it.key == key && it.value.contains(value) }
}
@Override
boolean put(Path key, ContractMetadata value) {
return contracts.add(key, value)
}
@Override
boolean remove(Object key, Object value) {
return contracts.getOrDefault(key, new ArrayList<ContractMetadata>()).remove(value)
}
@Override
boolean putAll(Path key, Iterable<? extends ContractMetadata> values) {
return contracts.getOrDefault(key, new ArrayList<ContractMetadata>()).addAll(values)
}
@Override
boolean putAll(Multimap<? extends Path, ? extends ContractMetadata> multimap) {
multimap.entries().each {
contracts.add(it.key, it.value)
}
return true
}
@Override
void clear() {
contracts.clear()
}
@Override
Set<Path> keySet() {
return contracts.keySet()
}
@Override
Multiset<Path> keys() {
return HashMultiset.create(contracts.keySet())
}
@Override
Collection<ContractMetadata> values() {
return (Collection<ContractMetadata>) contracts.values().flatten()
}
@Override
Collection<Map.Entry<Path, ContractMetadata>> entries() {
Collection<Map.Entry<Path, ContractMetadata>> entries = new LinkedList<>()
contracts.each {
Path path = it.key
List<ContractMetadata> list = it.value
list.each {
entries.add(new AbstractMap.SimpleEntry<Path, ContractMetadata>(path, it))
}
}
return entries
}
}
}
MultiValueMap<Path, ContractMetadata> findContractsRecursively() {
MultiValueMap<Path, ContractMetadata> result = CollectionUtils.toMultiValueMap(new LinkedHashMap<>());
appendRecursively(baseDir, result)
return result
}
@@ -96,7 +226,7 @@ class ContractFileScanner {
* We iterate over found contracts, filter out those that should be excluded
* and try to convert via pluggable Contract Converters any possible contracts
*/
private void appendRecursively(File baseDir, ListMultimap<Path, ContractMetadata> result) {
private void appendRecursively(File baseDir, MultiValueMap<Path, ContractMetadata> result) {
List<ContractConverter> converters = convertersWithYml()
if (log.isTraceEnabled()) {
log.trace("Found the following contract converters ${converters}")
@@ -148,7 +278,7 @@ class ContractFileScanner {
return SpringFactoriesLoader.loadFactories(ContractConverter, null)
}
private void addContractToTestGeneration(List<ContractConverter> converters, ListMultimap<Path, ContractMetadata> result,
private void addContractToTestGeneration(List<ContractConverter> converters, MultiValueMap<Path, ContractMetadata> result,
File[] files, File file, int index) {
boolean converted = false
if (!file.isDirectory()) {
@@ -182,7 +312,7 @@ class ContractFileScanner {
}
}
private void addContractToTestGeneration(ListMultimap<Path, ContractMetadata> result, File[] files, File file,
private void addContractToTestGeneration(MultiValueMap<Path, ContractMetadata> result, File[] files, File file,
int index, Collection<Contract> convertedContract) {
Path path = file.toPath()
Integer order = null
@@ -196,7 +326,7 @@ class ContractFileScanner {
if (log.isDebugEnabled()) {
log.debug("Creating a contract entry for path [" + path + "] and metadata [" + metadata + "]")
}
result.put(parent, metadata)
result.add(parent, metadata)
}
private boolean hasScenarioFilenamePattern(Path path) {

View File

@@ -18,21 +18,22 @@ package org.springframework.cloud.contract.verifier;
import java.io.File;
import java.nio.file.Path;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.assertj.core.api.BDDAssertions;
import org.junit.Test;
import org.mockito.BDDMockito;
import wiremock.com.google.common.collect.ArrayListMultimap;
import wiremock.com.google.common.collect.ListMultimap;
import org.springframework.cloud.contract.spec.Contract;
import org.springframework.cloud.contract.verifier.builder.SingleTestGenerator;
import org.springframework.cloud.contract.verifier.config.ContractVerifierConfigProperties;
import org.springframework.cloud.contract.verifier.file.ContractFileScanner;
import org.springframework.cloud.contract.verifier.file.ContractMetadata;
import org.springframework.util.CollectionUtils;
import org.springframework.util.MultiValueMap;
public class TestGeneratorTests {
@@ -46,9 +47,10 @@ public class TestGeneratorTests {
.mock(SingleTestGenerator.class);
FileSaver fileSaver = BDDMockito.mock(FileSaver.class);
// and:
ArrayListMultimap<Path, ContractMetadata> multimap = ArrayListMultimap.create();
MultiValueMap<Path, ContractMetadata> multimap = CollectionUtils
.toMultiValueMap(new LinkedHashMap<>());
Path path = new File(".").toPath();
multimap.put(path,
multimap.add(path,
new ContractMetadata(path, false, 0, null, Contract.make(it -> {
it.inProgress();
it.request(r -> {
@@ -61,7 +63,7 @@ public class TestGeneratorTests {
})));
ContractFileScanner scanner = new ContractFileScanner(null, null, null) {
@Override
public ListMultimap<Path, ContractMetadata> findContracts() {
public MultiValueMap<Path, ContractMetadata> findContractsRecursively() {
return multimap;
}
};
@@ -87,9 +89,10 @@ public class TestGeneratorTests {
.mock(SingleTestGenerator.class);
FileSaver fileSaver = BDDMockito.mock(FileSaver.class);
// and:
ArrayListMultimap<Path, ContractMetadata> multimap = ArrayListMultimap.create();
MultiValueMap<Path, ContractMetadata> multimap = CollectionUtils
.toMultiValueMap(new LinkedHashMap<>());
Path path = new File(".").toPath();
multimap.put(path,
multimap.add(path,
new ContractMetadata(path, false, 0, null, Contract.make(it -> {
it.inProgress();
it.request(r -> {
@@ -102,7 +105,7 @@ public class TestGeneratorTests {
})));
ContractFileScanner scanner = new ContractFileScanner(null, null, null) {
@Override
public ListMultimap<Path, ContractMetadata> findContracts() {
public MultiValueMap<Path, ContractMetadata> findContractsRecursively() {
return multimap;
}
};
@@ -110,8 +113,8 @@ public class TestGeneratorTests {
TestGenerator testGenerator = new TestGenerator(properties, singleTestGenerator,
fileSaver, scanner) {
@Override
Set<Map.Entry<Path, Collection<ContractMetadata>>> processAllNotInProgress(
ListMultimap<Path, ContractMetadata> contracts,
Set<Map.Entry<Path, List<ContractMetadata>>> processAllNotInProgress(
MultiValueMap<Path, ContractMetadata> contracts,
String basePackageName) {
return null;
}

View File

@@ -0,0 +1,207 @@
/*
* Copyright 2013-2019 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.verifier.file
import java.nio.file.Path
import org.junit.Rule
import org.junit.rules.TemporaryFolder
import spock.lang.Specification
import org.springframework.cloud.contract.spec.Contract
import org.springframework.cloud.contract.spec.ContractConverter
import org.springframework.util.FileSystemUtils
import org.springframework.util.MultiValueMap
/**
* @author Jakub Kubrynski, codearte.io
*/
class ContractFileScannerNewApiSpec extends Specification {
@Rule
TemporaryFolder tmp = new TemporaryFolder()
File tmpFolder
def setup() {
tmpFolder = new File(tmp.newFolder(), "contracts")
}
def "should find contract files"() {
given:
FileSystemUtils.copyRecursively(
new File(this.getClass().getResource("/directory/with/stubs").toURI()),
tmpFolder)
and:
File baseDir = tmpFolder
Set<String> excluded = ["package/**"] as Set
Set<String> ignored = ["other/different/**"] as Set
ContractFileScanner scanner = new ContractFileScanner(baseDir, excluded, ignored, [] as Set)
when:
MultiValueMap<Path, ContractMetadata> result = scanner.findContractsRecursively()
then:
result.keySet().size() == 3
result.get(baseDir.toPath().resolve("different")).size() == 1
result.get(baseDir.toPath().resolve("other")).size() == 2
and:
Collection<ContractMetadata> ignoredSet = result.get(baseDir.toPath().resolve("other").resolve("different"))
ignoredSet.size() == 1
ignoredSet.ignored == [true]
}
def "should find contract files in strange directories"() {
given:
File baseDir = new File(this.getClass().getResource("/strange_[3.3.3]_directory").toURI())
Set<String> excluded = ["foo/**"] as Set
Set<String> ignored = ["bar/**"] as Set
ContractFileScanner scanner = new ContractFileScanner(baseDir, excluded, ignored, [] as Set)
when:
MultiValueMap<Path, ContractMetadata> result = scanner.findContractsRecursively()
then:
result.entrySet().size() == 2
and:
Collection<ContractMetadata> ignoredSet = result.get(baseDir.toPath().resolve("bar"))
ignoredSet.size() == 1
ignoredSet.ignored == [true]
}
def "should find contracts group in scenario"() {
given:
File baseDir = new File(this.getClass().getResource("/directory/with/scenario").toURI())
ContractFileScanner scanner = new ContractFileScanner(baseDir, [] as Set, [] as Set, [] as Set)
when:
MultiValueMap<Path, ContractMetadata> contracts = scanner.findContractsRecursively()
then:
contracts.values().size() == 1
def firstEntry = contracts.values().first()
firstEntry.size() == 3
firstEntry.find {
it.path.fileName.toString().startsWith('01')
}.groupSize == 3
firstEntry.find {
it.path.fileName.toString().startsWith('01')
}.order == 0
firstEntry.find {
it.path.fileName.toString().startsWith('02')
}.order == 1
firstEntry.find {
it.path.fileName.toString().startsWith('03')
}.order == 2
}
def "should find contract files with converters"() {
given:
File baseDir = new File(this.getClass().getResource("/directory/with/mixed").toURI())
ContractFileScanner scanner = new ContractFileScanner(baseDir, null, null, null) {
@Override
protected List<ContractConverter> converters() {
return [new ContractConverter() {
@Override
boolean isAccepted(File file) {
return file.name.endsWith(".json")
}
@Override
Collection<Contract> convertFrom(File file) {
throw new RuntimeException("boom")
}
@Override
Object convertTo(Collection contract) {
throw new RuntimeException("boom")
}
}]
}
}
when:
scanner.findContractsRecursively()
then:
IllegalStateException e = thrown(IllegalStateException)
e.cause.message == "boom"
e.message.matches(".*Failed to convert file .*invalid.json.*")
}
def "should prefer custom yaml converter over standard yaml converter"() {
given:
File baseDir = new File(this.getClass().getResource("/directory/with/custom/yml").toURI())
ContractFileScanner scanner = new ContractFileScanner(baseDir, null, null) {
@Override
protected List<ContractConverter> converters() {
return [new ContractConverter() {
@Override
boolean isAccepted(File file) {
if (!file.name.endsWith(".yml") && !file.name.endsWith(".yaml")) {
return false
}
String line
file.withReader {
line = it.readLine()
}
return line != null && line.startsWith("custom_format: 1.0")
}
@Override
Collection<Contract> convertFrom(File file) {
return Collections.singleton(Contract.newInstance())
}
@Override
Object convertTo(Collection contract) {
return new Object()
}
}]
}
}
when:
MultiValueMap<Path, ContractMetadata> result = scanner.findContractsRecursively()
then:
result.keySet().size() == 1
result.entrySet().every { it.value.convertedContract }
}
def "should find contracts for include pattern"() {
given:
FileSystemUtils.copyRecursively(
new File(this.getClass().getResource("/directory/with/common-messaging").toURI()),
tmpFolder)
and:
File baseDir = tmpFolder
Set<String> included = ["social-service/**", "**/coupon-collected/**/*V1*"] as Set
ContractFileScanner scanner = new ContractFileScanner(baseDir, [] as Set, [] as Set, included)
when:
MultiValueMap<Path, ContractMetadata> result = scanner.findContractsRecursively()
then:
result.keySet().size() == 3
result.values().flatten().find {
(it.path.fileName.toString() == 'couponCollectedEventV1.groovy')
}.groupSize == 2
result.values().flatten().find {
(it.convertedContract.first().label == 'couponCollectedV1')
}
result.values().flatten().findAll {
(it.path.fileName.toString() == 'couponCollectedEventV2.groovy')
}.isEmpty()
result.values().flatten().find {
(it.path.fileName.toString() == 'shouldUpdateUserInfo.groovy')
}.groupSize == 1
result.values().flatten().find {
(it.path.fileName.toString() == 'shouldReturnEmptyFriendsWhenGetFriends.groovy')
}.groupSize == 1
result.get(baseDir.toPath().resolve("coupon-sent")) == null
result.get(baseDir.toPath().resolve("reward-rules")) == null
}
}

View File

@@ -199,7 +199,7 @@ class ContractFileScannerSpec extends Specification {
result.values().find {
(it.path.fileName.toString() == 'shouldReturnEmptyFriendsWhenGetFriends.groovy')
}.groupSize == 1
result.get(baseDir.toPath().resolve("coupon-sent")).size() == 0
result.get(baseDir.toPath().resolve("reward-rules")).size() == 0
result.get(baseDir.toPath().resolve("coupon-sent")) == null
result.get(baseDir.toPath().resolve("reward-rules")) == null
}
}

View File

@@ -18,8 +18,8 @@ package org.springframework.cloud.contract.verifier.messaging.amqp
import com.rabbitmq.client.Channel
import org.mockito.exceptions.verification.WantedButNotInvoked
import shaded.com.google.common.collect.ImmutableMap
import spock.lang.Specification
import wiremock.com.google.common.collect.ImmutableMap
import org.springframework.amqp.core.Binding
import org.springframework.amqp.core.BindingBuilder

View File

@@ -9,7 +9,7 @@
<logger name="com.github.jknack.handlebars.internal" level="INFO"/>
<!-- <logger name="org.springframework.cloud.function" level="INFO"/>-->
<logger name="org.springframework.cloud.function" level="INFO"/>
<root level="DEBUG">
<appender-ref ref="STDOUT"/>

View File

@@ -81,6 +81,10 @@
<artifactId>javax.servlet-api</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-server</artifactId>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>

View File

@@ -38,7 +38,6 @@ import com.github.tomakehurst.wiremock.http.Request;
import com.github.tomakehurst.wiremock.http.RequestMethod;
import org.apache.commons.codec.binary.Base64;
import wiremock.com.google.common.base.Optional;
import wiremock.org.eclipse.jetty.server.handler.ContextHandler;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
@@ -51,6 +50,8 @@ import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.support.StandardMultipartHttpServletRequest;
import static org.eclipse.jetty.server.handler.ContextHandler.StaticContext;
/**
* @author Dave Syer
*
@@ -263,7 +264,7 @@ class WireMockHttpRequestAdapter implements Request {
.request(this.result.getMethod(), this.result.getUriTemplate())
.contentType(this.result.getRequestHeaders().getContentType())
.content(this.result.getRequestBodyContent())
.buildRequest(new ContextHandler.StaticContext());
.buildRequest(new StaticContext());
try {
return new StandardMultipartHttpServletRequest(request).getParts().stream()
.map(part -> partFromServletPart(part)).collect(Collectors.toList());

View File

@@ -18,21 +18,18 @@ package org.springframework.cloud.contract.wiremock.restdocs;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import java.util.stream.Collectors;
import com.github.tomakehurst.wiremock.client.ResponseDefinitionBuilder;
import com.github.tomakehurst.wiremock.common.Gzip;
import com.github.tomakehurst.wiremock.http.Body;
import com.github.tomakehurst.wiremock.http.ContentTypeHeader;
import com.github.tomakehurst.wiremock.http.Cookie;
import com.github.tomakehurst.wiremock.http.HttpHeader;
@@ -40,30 +37,15 @@ import com.github.tomakehurst.wiremock.http.HttpHeaders;
import com.github.tomakehurst.wiremock.http.QueryParameter;
import com.github.tomakehurst.wiremock.http.Request;
import com.github.tomakehurst.wiremock.http.RequestMethod;
import com.github.tomakehurst.wiremock.servlet.WireMockHttpServletMultipartAdapter;
import wiremock.com.google.common.base.Function;
import wiremock.com.google.common.base.Optional;
import wiremock.com.google.common.collect.ImmutableList;
import wiremock.com.google.common.collect.ImmutableMultimap;
import wiremock.com.google.common.collect.Maps;
import wiremock.org.apache.commons.io.IOUtils;
import org.springframework.http.MediaType;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.ResultHandler;
import static com.github.tomakehurst.wiremock.common.Encoding.encodeBase64;
import static com.github.tomakehurst.wiremock.common.Exceptions.throwUnchecked;
import static com.github.tomakehurst.wiremock.common.Strings.stringFromBytes;
import static com.github.tomakehurst.wiremock.common.Urls.splitQuery;
import static java.util.Collections.list;
import static wiremock.com.google.common.base.Charsets.UTF_8;
import static wiremock.com.google.common.base.MoreObjects.firstNonNull;
import static wiremock.com.google.common.base.Strings.isNullOrEmpty;
import static wiremock.com.google.common.collect.FluentIterable.from;
import static wiremock.com.google.common.collect.Lists.newArrayList;
import static wiremock.com.google.common.io.ByteStreams.toByteArray;
import org.springframework.util.Base64Utils;
import org.springframework.util.StringUtils;
/**
* Converts a {@link MvcResult} to a WireMock response.
@@ -117,7 +99,191 @@ public class ContractResultHandler extends
@Override
protected Request getWireMockRequest(MvcResult result) {
return new WireMockHttpServletRequestAdapter(result.getRequest());
return new Request() {
@Override
public String getUrl() {
return result.getRequest().getRequestURI();
}
@Override
public String getAbsoluteUrl() {
return result.getRequest().getRequestURI();
}
@Override
public RequestMethod getMethod() {
return RequestMethod.fromString(result.getRequest().getMethod());
}
@Override
public String getScheme() {
return result.getRequest().getScheme();
}
@Override
public String getHost() {
return result.getRequest().getRemoteHost();
}
@Override
public int getPort() {
return result.getRequest().getServerPort();
}
@Override
public String getClientIp() {
return "";
}
@Override
public String getHeader(String key) {
return result.getRequest().getHeader(key);
}
@Override
public HttpHeader header(String key) {
return new HttpHeader(key, getHeader(key));
}
@Override
public ContentTypeHeader contentTypeHeader() {
return new ContentTypeHeader(result.getRequest().getContentType());
}
@Override
public HttpHeaders getHeaders() {
List<HttpHeader> headers = new ArrayList<>();
Enumeration<String> headerNames = result.getRequest().getHeaderNames();
while (headerNames.hasMoreElements()) {
String key = headerNames.nextElement();
String value = getHeader(key);
headers.add(new HttpHeader(key, value));
}
return new HttpHeaders(headers);
}
@Override
public boolean containsHeader(String key) {
Enumeration<String> headerNames = result.getRequest().getHeaderNames();
while (headerNames.hasMoreElements()) {
String name = headerNames.nextElement();
if (name.equals(key)) {
return true;
}
}
return false;
}
@Override
public Set<String> getAllHeaderKeys() {
return getHeaders().keys();
}
@Override
public Map<String, Cookie> getCookies() {
Map<String, Cookie> nameToCookie = new HashMap<>();
if (result.getRequest().getCookies() == null) {
return nameToCookie;
}
for (javax.servlet.http.Cookie cookie : result.getRequest()
.getCookies()) {
nameToCookie.put(cookie.getName(), new Cookie(cookie.getValue()));
}
return nameToCookie;
}
@Override
public QueryParameter queryParameter(String key) {
return new QueryParameter(key,
Collections.singletonList(result.getRequest().getParameter(key)));
}
@Override
public byte[] getBody() {
return result.getRequest().getContentAsByteArray();
}
@Override
public String getBodyAsString() {
try {
return result.getRequest().getContentAsString();
}
catch (Exception ex) {
return new String(result.getRequest().getContentAsByteArray());
}
}
@Override
public String getBodyAsBase64() {
return Base64Utils
.encodeToString(result.getRequest().getContentAsByteArray());
}
@Override
public boolean isMultipart() {
return StringUtils
.hasText(result.getRequest().getHeader("multipart/form-data"));
}
@Override
public Collection<Part> getParts() {
try {
return result.getRequest().getParts().stream()
.map(part -> new Part() {
@Override
public String getName() {
return part.getName();
}
@Override
public HttpHeader getHeader(String name) {
String header = part.getHeader(name);
return new HttpHeader(name, header);
}
@Override
public HttpHeaders getHeaders() {
List<HttpHeader> headers = new ArrayList<>();
for (String headerName : part.getHeaderNames()) {
headers.add(new HttpHeader(headerName,
getHeader(headerName).values()));
}
return new HttpHeaders(headers);
}
@Override
public Body getBody() {
try {
return new Body(IOUtils
.toByteArray(part.getInputStream()));
}
catch (IOException ex) {
throw new IllegalStateException(ex);
}
}
}).collect(Collectors.toList());
}
catch (Exception ex) {
throw new IllegalStateException(ex);
}
}
@Override
public Part getPart(String name) {
return getParts().stream().filter(part -> part.getName().equals(name))
.findFirst().orElse(null);
}
@Override
public boolean isBrowserProxyRequest() {
return false;
}
@Override
public Optional<Request> getOriginalRequest() {
return Optional.absent();
}
};
}
@Override
@@ -127,289 +293,8 @@ public class ContractResultHandler extends
@Override
protected byte[] getRequestBodyContent(MvcResult result) {
byte[] body = new WireMockHttpServletRequestAdapter(result.getRequest())
.getBody();
byte[] body = getWireMockRequest(result).getBody();
return body != null ? body : new byte[0];
}
}
// COPIED FROM WIREMOCK
class WireMockHttpServletRequestAdapter implements Request {
private static final String ORIGINAL_REQUEST_KEY = "wiremock.ORIGINAL_REQUEST";
private final HttpServletRequest request;
private byte[] cachedBody;
private Collection<Part> cachedMultiparts;
WireMockHttpServletRequestAdapter(HttpServletRequest request) {
this.request = request;
}
@Override
public String getUrl() {
String url = this.request.getRequestURI();
String contextPath = this.request.getContextPath();
if (!isNullOrEmpty(contextPath) && url.startsWith(contextPath)) {
url = url.substring(contextPath.length());
}
return withQueryStringIfPresent(url);
}
@Override
public String getAbsoluteUrl() {
return withQueryStringIfPresent(this.request.getRequestURL().toString());
}
private String withQueryStringIfPresent(String url) {
return url + (isNullOrEmpty(this.request.getQueryString()) ? ""
: "?" + this.request.getQueryString());
}
@Override
public RequestMethod getMethod() {
return RequestMethod.fromString(this.request.getMethod().toUpperCase());
}
@Override
public String getScheme() {
return this.request.getScheme();
}
@Override
public String getHost() {
return this.request.getServerName();
}
@Override
public int getPort() {
return this.request.getServerPort();
}
@Override
public String getClientIp() {
String forwardedForHeader = this.getHeader("X-Forwarded-For");
if (forwardedForHeader != null && forwardedForHeader.length() > 0) {
return forwardedForHeader;
}
return this.request.getRemoteAddr();
}
// Something's wrong with reading the body from request
@Override
public byte[] getBody() {
if (this.cachedBody == null || this.cachedBody.length == 0) {
try {
if (this.request instanceof MockHttpServletRequest) {
this.cachedBody = ((MockHttpServletRequest) this.request)
.getContentAsByteArray();
return this.cachedBody;
}
byte[] body = toByteArray(this.request.getInputStream());
boolean isGzipped = hasGzipEncoding() || Gzip.isGzipped(body);
this.cachedBody = isGzipped ? Gzip.unGzip(body) : body;
}
catch (IOException ioe) {
throw new RuntimeException(ioe);
}
}
return this.cachedBody;
}
private Charset encodingFromContentTypeHeaderOrUtf8() {
ContentTypeHeader contentTypeHeader = contentTypeHeader();
if (contentTypeHeader != null) {
return contentTypeHeader.charset();
}
return UTF_8;
}
private boolean hasGzipEncoding() {
String encodingHeader = this.request.getHeader("Content-Encoding");
return encodingHeader != null && encodingHeader.contains("gzip");
}
@Override
public String getBodyAsString() {
return stringFromBytes(getBody(), encodingFromContentTypeHeaderOrUtf8());
}
@Override
public String getBodyAsBase64() {
return encodeBase64(getBody());
}
@Override
public String getHeader(String key) {
List<String> headerNames = list(this.request.getHeaderNames());
for (String currentKey : headerNames) {
if (currentKey.toLowerCase().equals(key.toLowerCase())) {
return this.request.getHeader(currentKey);
}
}
return null;
}
@Override
public HttpHeader header(String key) {
List<String> headerNames = list(this.request.getHeaderNames());
for (String currentKey : headerNames) {
if (currentKey.toLowerCase().equals(key.toLowerCase())) {
List<String> valueList = list(this.request.getHeaders(currentKey));
if (valueList.isEmpty()) {
return HttpHeader.empty(key);
}
return new HttpHeader(key, valueList);
}
}
return HttpHeader.absent(key);
}
@Override
public ContentTypeHeader contentTypeHeader() {
return getHeaders().getContentTypeHeader();
}
@Override
public boolean containsHeader(String key) {
return header(key).isPresent();
}
@Override
public HttpHeaders getHeaders() {
List<HttpHeader> headerList = newArrayList();
for (String key : getAllHeaderKeys()) {
headerList.add(header(key));
}
return new HttpHeaders(headerList);
}
@Override
public Set<String> getAllHeaderKeys() {
LinkedHashSet<String> headerKeys = new LinkedHashSet<>();
for (Enumeration<String> headerNames = this.request.getHeaderNames(); headerNames
.hasMoreElements();) {
headerKeys.add(headerNames.nextElement());
}
return headerKeys;
}
@Override
public Map<String, Cookie> getCookies() {
ImmutableMultimap.Builder<String, String> builder = ImmutableMultimap.builder();
javax.servlet.http.Cookie[] cookies = firstNonNull(this.request.getCookies(),
new javax.servlet.http.Cookie[0]);
for (javax.servlet.http.Cookie cookie : cookies) {
builder.put(cookie.getName(), cookie.getValue());
}
return Maps.transformValues(builder.build().asMap(),
input -> new Cookie(null, ImmutableList.copyOf(input)));
}
@Override
public QueryParameter queryParameter(String key) {
return firstNonNull((splitQuery(this.request.getQueryString()).get(key)),
QueryParameter.absent(key));
}
@Override
public boolean isBrowserProxyRequest() {
if (!isJetty()) {
return false;
}
return false;
}
@Override
@SuppressWarnings("unchecked")
public Collection<Part> getParts() {
if (!isMultipart()) {
return null;
}
if (this.cachedMultiparts == null) {
try {
this.cachedMultiparts = from(safelyGetRequestParts()).transform(
(Function<javax.servlet.http.Part, Part>) WireMockHttpServletMultipartAdapter::from)
.toList();
}
catch (IOException | ServletException exception) {
return throwUnchecked(exception, Collection.class);
}
}
return (this.cachedMultiparts.size() > 0) ? this.cachedMultiparts : null;
}
private Collection<javax.servlet.http.Part> safelyGetRequestParts()
throws IOException, ServletException {
try {
return this.request.getParts();
}
catch (IOException ioe) {
if (ioe.getMessage().contains("Missing content for multipart")) {
return Collections.emptyList();
}
throw ioe;
}
}
@Override
public boolean isMultipart() {
String header = getHeader("Content-Type");
return (header != null && header.contains("multipart"));
}
@Override
public Part getPart(final String name) {
if (name == null || name.length() == 0) {
return null;
}
if (this.cachedMultiparts == null) {
if (getParts() == null) {
return null;
}
}
return from(this.cachedMultiparts)
.firstMatch(input -> name.equals(input.getName())).get();
}
@Override
public Optional<Request> getOriginalRequest() {
Request originalRequest = (Request) this.request
.getAttribute(ORIGINAL_REQUEST_KEY);
return Optional.fromNullable(originalRequest);
}
private boolean isJetty() {
try {
getClass("org.eclipse.jetty.server.Request");
return true;
}
catch (Exception e) {
}
return false;
}
private void getClass(String type) throws ClassNotFoundException {
ClassLoader contextCL = Thread.currentThread().getContextClassLoader();
ClassLoader loader = contextCL == null
? com.github.tomakehurst.wiremock.servlet.WireMockHttpServletRequestAdapter.class
.getClassLoader()
: contextCL;
Class.forName(type, false, loader);
}
@Override
public String toString() {
return this.request.toString() + getBodyAsString();
}
}

View File

@@ -17,7 +17,7 @@
package com.example.loan;
import org.junit.Test;
import wiremock.com.google.common.collect.ImmutableMap;
import shaded.com.google.common.collect.ImmutableMap;
import org.springframework.beans.BeanInstantiationException;
import org.springframework.beans.factory.BeanCreationException;