Merge branch '1.2.x' into 2.0.x

This commit is contained in:
Marcin Grzejszczak
2018-08-01 13:02:30 +02:00
8 changed files with 222 additions and 10 deletions

View File

@@ -20,6 +20,7 @@ import java.nio.charset.StandardCharsets
import java.nio.file.Path
import java.util.concurrent.atomic.AtomicInteger
import groovy.transform.CompileStatic
import groovy.transform.PackageScope
import org.apache.commons.logging.Log
import org.apache.commons.logging.LogFactory
@@ -39,14 +40,16 @@ import static org.springframework.cloud.contract.verifier.util.NamesUtil.beforeL
import static org.springframework.cloud.contract.verifier.util.NamesUtil.convertIllegalPackageChars
import static org.springframework.cloud.contract.verifier.util.NamesUtil.directoryToPackage
import static org.springframework.cloud.contract.verifier.util.NamesUtil.toLastDot
/**
* @author Jakub Kubrynski, codearte.io
*/
@CompileStatic
class TestGenerator {
private static final Log log = LogFactory.getLog(TestGenerator)
private static final String DEFAULT_CLASS_PREFIX = "ContractVerifier"
private static final String DEFAULT_TEST_PACKAGE = "org.springframework.cloud.contract.verifier.tests"
private static final Log log = LogFactory.getLog(TestGenerator)
private final ContractVerifierConfigProperties configProperties
private AtomicInteger counter = new AtomicInteger()
@@ -104,7 +107,7 @@ class TestGenerator {
}
}
private String relativizeContractPath(Map.Entry<Path, Collection<Path>> entry) {
private String relativizeContractPath(Map.Entry<Path, Collection<ContractMetadata>> entry) {
Path relativePath = configProperties.contractsDslDir.toPath().relativize(entry.getKey())
if (StringUtils.isEmpty(relativePath.toString())) {
return DEFAULT_CLASS_PREFIX
@@ -115,7 +118,7 @@ class TestGenerator {
private void processIncludedDirectory(
final String includedDirectoryRelativePath, Collection<ContractMetadata> contracts, final String basePackageNameForClass) {
if (log.isDebugEnabled()) {
log.debug("Collected contracts with metadata ${contracts}")
log.debug("Collected contracts with metadata ${contracts} relative path is [${includedDirectoryRelativePath}]")
}
if (contracts.size()) {
def className = afterLast(includedDirectoryRelativePath.toString(), File.separator) + resolveNameSuffix()

View File

@@ -18,6 +18,9 @@ package org.springframework.cloud.contract.verifier.builder
import groovy.transform.CompileStatic
import groovy.transform.PackageScope
import org.apache.commons.logging.Log
import org.apache.commons.logging.LogFactory
import org.springframework.cloud.contract.verifier.config.TestFramework
import org.springframework.cloud.contract.verifier.util.NamesUtil
import org.springframework.cloud.contract.verifier.config.ContractVerifierConfigProperties
@@ -33,6 +36,8 @@ import org.springframework.cloud.contract.verifier.config.ContractVerifierConfig
@PackageScope
class ClassBuilder {
private static final Log log = LogFactory.getLog(ClassBuilder)
private static final String SEPARATOR = "_REPLACEME_"
private final String className
@@ -72,11 +77,15 @@ class ClassBuilder {
}
protected static String retrieveBaseClass(ContractVerifierConfigProperties properties, String includedDirectoryRelativePath) {
String contractPathAsPackage = includedDirectoryRelativePath.replace(File.separator, ".")
String contractPackage = includedDirectoryRelativePath.replace(File.separator, SEPARATOR)
// package mapping takes super precedence
if (properties.baseClassMappings) {
Map.Entry<String, String> mapping = properties.baseClassMappings.find { String pattern, String fqn ->
return contractPackage.matches(pattern)
return contractPathAsPackage.matches(pattern)
}
if (log.isDebugEnabled()) {
log.debug("Matching pattern for contract package [${contractPathAsPackage}] with setup ${properties.baseClassMappings} is [${mapping}]")
}
if (mapping) {
return mapping.value

View File

@@ -40,7 +40,11 @@ class HandlebarsJsonPathHelper implements Helper<Map<String, Object>> {
private Object returnObjectForTest(Object model, String jsonPath) {
String body = removeSurroundingQuotes(((TestSideRequestTemplateModel) model).rawBody).replace('\\"', '"')
DocumentContext documentContext = JsonPath.parse(body)
return documentContext.read(jsonPath)
Object value = documentContext.read(jsonPath)
if (value instanceof Long) {
return String.valueOf(value) + "L"
}
return value
}
private String removeSurroundingQuotes(String body) {

View File

@@ -16,6 +16,7 @@
package org.springframework.cloud.contract.verifier.messaging.stream;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
@@ -28,6 +29,7 @@ import org.springframework.cloud.stream.test.binder.MessageCollector;
import org.springframework.context.ApplicationContext;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.util.StringUtils;
/**
* @author Marcin Grzejszczak
@@ -54,7 +56,7 @@ public class StreamStubMessages implements MessageVerifier<Message<?>> {
public void send(Message<?> message, String destination) {
try {
MessageChannel messageChannel = this.context
.getBean(resolvedDestination(destination), MessageChannel.class);
.getBean(resolvedDestination(destination, DefaultChannels.OUTPUT), MessageChannel.class);
messageChannel.send(message);
}
catch (Exception e) {
@@ -68,7 +70,7 @@ public class StreamStubMessages implements MessageVerifier<Message<?>> {
public Message<?> receive(String destination, long timeout, TimeUnit timeUnit) {
try {
MessageChannel messageChannel = this.context
.getBean(resolvedDestination(destination), MessageChannel.class);
.getBean(resolvedDestination(destination, DefaultChannels.INPUT), MessageChannel.class);
return this.messageCollector.forChannel(messageChannel).poll(timeout, timeUnit);
}
catch (Exception e) {
@@ -78,10 +80,11 @@ public class StreamStubMessages implements MessageVerifier<Message<?>> {
}
}
private String resolvedDestination(String destination) {
private String resolvedDestination(String destination, DefaultChannels defaultChannel) {
try {
BindingServiceProperties channelBindingServiceProperties = this.context
.getBean(BindingServiceProperties.class);
Map<String, String> channels = new HashMap<>();
for (Map.Entry<String, BindingProperties> entry : channelBindingServiceProperties
.getBindings().entrySet()) {
if (destination.equals(entry.getValue().getDestination())) {
@@ -89,9 +92,24 @@ public class StreamStubMessages implements MessageVerifier<Message<?>> {
log.debug("Found a channel named [{}] with destination [{}]",
entry.getKey(), destination);
}
return entry.getKey();
channels.put(entry.getKey().toLowerCase(), destination);
}
}
if (channels.size() == 1) {
return channels.keySet().iterator().next();
} else if (channels.size() > 0) {
if (log.isDebugEnabled()) {
log.debug("Found following channels [{}] for destination [{}]. "
+ "Will pick the one that matches the default channel name or the first one if none is matching",
channels, destination);
}
String defaultChannelName = channels.get(defaultChannel.name().toLowerCase());
String matchingChannelName = StringUtils.hasText(defaultChannelName) ? defaultChannel.name().toLowerCase() : channels.keySet().iterator().next();
if (log.isDebugEnabled()) {
log.debug("Picked channel name is [{}]", matchingChannelName);
}
return matchingChannelName;
}
} catch (Exception e) {
log.error("Exception took place while trying to resolve the destination. Will assume the name [" + destination + "]", e);
}
@@ -109,3 +127,8 @@ public class StreamStubMessages implements MessageVerifier<Message<?>> {
}
}
enum DefaultChannels {
INPUT, OUTPUT
}

View File

@@ -1,5 +1,7 @@
package org.springframework.cloud.contract.verifier.builder
import spock.lang.Issue
import org.springframework.cloud.contract.verifier.config.ContractVerifierConfigProperties
import spock.lang.Specification
@@ -49,6 +51,16 @@ class ClassBuilderSpec extends Specification {
ClassBuilder.retrieveBaseClass(props, contractRelativeFolder) == 'com.example.base.SuperClass'
}
@Issue("701")
def "should match base class when mapping regex has multiple folders"() {
given:
ContractVerifierConfigProperties props = new ContractVerifierConfigProperties(
baseClassMappings: ['.*bar.baz.some.*' : 'com.example.base.SuperClass'])
String contractRelativeFolder = 'foo/bar/baz/some/package'.split("/").join(File.separator)
expect:
ClassBuilder.retrieveBaseClass(props, contractRelativeFolder) == 'com.example.base.SuperClass'
}
def "should return the first matching base class when provided mapping doesn't match"() {
given:
ContractVerifierConfigProperties props = new ContractVerifierConfigProperties(

View File

@@ -491,6 +491,58 @@ DocumentContext parsedJson = JsonPath.parse(json);
"JaxRsClientJUnitMethodBodyBuilder" | { Contract dsl -> new JaxRsClientJUnitMethodBodyBuilder(dsl, properties) }
}
@Issue("#702")
def "should generate proper type for large numbers [#methodBuilderName]"() {
given:
Contract contractDsl = Contract.make {
request {
method 'PUT'
urlPath '/example/create'
headers {
contentType applicationJson()
}
body(
[
"name" : $(consumer(~/.+/), producer("string-1")),
"updatedTs" : $(consumer(~/\d{13}/), producer(1531916906000L)),
"isDisabled": $(consumer(regex(anyBoolean())), producer(true))
]
)
}
response {
status 200
headers {
contentType applicationJsonUtf8()
}
body(
[
"id" : $(consumer(2222L), producer(~/\d+/)),
"name" : fromRequest().body("name"),
"updatedTs" : fromRequest().body("updatedTs"),
"isDisabled": fromRequest().body("isDisabled")
]
)
}
}
MethodBodyBuilder builder = methodBuilder(contractDsl)
BlockBuilder blockBuilder = new BlockBuilder(" ")
when:
builder.appendTo(blockBuilder)
then:
String test = blockBuilder.toString()
SyntaxChecker.tryToCompileWithoutCompileStatic(methodBuilderName, test)
test.contains('''assertThatJson(parsedJson).field("['updatedTs']").isEqualTo(1531916906000L)''')
and:
stubMappingIsValidWireMockStub(contractDsl)
where:
methodBuilderName | methodBuilder
"MockMvcSpockMethodBuilder" | { Contract dsl -> new MockMvcSpockMethodRequestProcessingBodyBuilder(dsl, properties) }
"MockMvcJUnitMethodBuilder" | { Contract dsl -> new MockMvcJUnitMethodBodyBuilder(dsl, properties) }
"JaxRsClientSpockMethodRequestProcessingBodyBuilder" | { Contract dsl -> new JaxRsClientSpockMethodRequestProcessingBodyBuilder(dsl, properties) }
"JaxRsClientJUnitMethodBodyBuilder" | { Contract dsl -> new JaxRsClientJUnitMethodBodyBuilder(dsl, properties) }
}
@Issue("#465")
def "should work for '/' url for [#methodBuilderName]"() {
given:

View File

@@ -0,0 +1,109 @@
package org.springframework.cloud.contract.verifier.messaging.stream
import spock.lang.Issue
import spock.lang.Specification
import org.springframework.cloud.stream.config.BindingProperties
import org.springframework.cloud.stream.config.BindingServiceProperties
import org.springframework.cloud.stream.test.binder.MessageCollector
import org.springframework.context.ApplicationContext
import org.springframework.messaging.MessageChannel
/**
* @author Marcin Grzejszczak
*/
class StreamStubMessagesSpec extends Specification {
@Issue("694")
def "should resolve input channel if input and output have same destination and receive is called"() {
given:
ApplicationContext applicationContext = Mock(ApplicationContext)
BindingServiceProperties properties = new BindingServiceProperties(
bindings: [
input: new BindingProperties(destination: "verifications"),
output: new BindingProperties(destination: "verifications"),
]
)
MessageCollector collector = Stub(MessageCollector)
and:
applicationContext.getBean(BindingServiceProperties) >> properties
applicationContext.getBean(MessageCollector) >> collector
and:
StreamStubMessages messages = new StreamStubMessages(applicationContext)
when:
messages.receive("verifications")
then:
1 * applicationContext.getBean("input", MessageChannel) >> null
}
@Issue("694")
def "should resolve output channel if input and output have same destination and send is called"() {
given:
ApplicationContext applicationContext = Mock(ApplicationContext)
BindingServiceProperties properties = new BindingServiceProperties(
bindings: [
input: new BindingProperties(destination: "verifications"),
output: new BindingProperties(destination: "verifications"),
]
)
MessageCollector collector = Stub(MessageCollector)
MessageChannel channel = Stub(MessageChannel)
and:
applicationContext.getBean(BindingServiceProperties) >> properties
applicationContext.getBean(MessageCollector) >> collector
and:
StreamStubMessages messages = new StreamStubMessages(applicationContext)
when:
messages.send("foo", [:], "verifications")
then:
1 * applicationContext.getBean("output", MessageChannel) >> channel
}
def "should resolve channel via destination for send and receive"() {
given:
ApplicationContext applicationContext = Mock(ApplicationContext)
BindingServiceProperties properties = new BindingServiceProperties(
bindings: [
foo: new BindingProperties(destination: "verifications")
]
)
MessageCollector collector = Stub(MessageCollector)
MessageChannel channel = Stub(MessageChannel)
and:
applicationContext.getBean(BindingServiceProperties) >> properties
applicationContext.getBean(MessageCollector) >> collector
and:
StreamStubMessages messages = new StreamStubMessages(applicationContext)
when:
messageInteraction(messages)
then:
1 * applicationContext.getBean("foo", MessageChannel) >> channel
where:
messageInteraction << [ { StreamStubMessages stream -> stream.send("foo", [:], "verifications")},
{ StreamStubMessages stream -> stream.receive("verifications")}]
}
def "should resolve channel via channel name for send and receive"() {
given:
ApplicationContext applicationContext = Mock(ApplicationContext)
BindingServiceProperties properties = new BindingServiceProperties(
bindings: [
verifications: new BindingProperties(destination: "bar")
]
)
MessageCollector collector = Stub(MessageCollector)
MessageChannel channel = Stub(MessageChannel)
and:
applicationContext.getBean(BindingServiceProperties) >> properties
applicationContext.getBean(MessageCollector) >> collector
and:
StreamStubMessages messages = new StreamStubMessages(applicationContext)
when:
messageInteraction(messages)
then:
1 * applicationContext.getBean("verifications", MessageChannel) >> channel
where:
messageInteraction << [ { StreamStubMessages stream -> stream.send("foo", [:], "verifications")},
{ StreamStubMessages stream -> stream.receive("verifications")}]
}
}

View File

@@ -40,7 +40,7 @@ import javax.inject.Inject
@DirtiesContext
@SpringBootTest(properties = "debug=true")
@AutoConfigureMessageVerifier
public class StreamMessagingApplicationSpec extends Specification {
class StreamMessagingApplicationSpec extends Specification {
// ALL CASES
@Inject MessageVerifier<Message<?>> contractVerifierMessaging