JDK16 changes

This commit is contained in:
Marcin Grzejszczak
2021-07-14 15:22:45 +02:00
parent 98783853b8
commit 00ccfdadba
33 changed files with 708 additions and 1253 deletions

View File

@@ -223,9 +223,15 @@
<artifactId>gmavenplus-plugin</artifactId>
</plugin>
<plugin>
<!-- Will run JUnit 4 -->
<!-- Will run JUnit 5 -->
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
</plugin>
<plugin>
<!-- Will run JUnit 4 -->
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId>
<version>${maven-failsafe-plugin.version}</version>
<dependencies>
<!-- let JUnit vintage engine run JUnit 3 or JUnit 4 tests -->
<dependency>
@@ -239,32 +245,17 @@
<version>${junit-vintage.version}</version>
</dependency>
</dependencies>
<configuration>
<junitPlatformArtifactName>
org.junit.jupiter:junit-vintage-engine
</junitPlatformArtifactName>
</configuration>
</plugin>
<plugin>
<!-- Will run JUnit 5 -->
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId>
<version>${maven-failsafe-plugin.version}</version>
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>${junit-jupiter.version}</version>
</dependency>
</dependencies>
<configuration>
<includes>
<include>**/*Test.*</include>
<include>**/*Tests.*</include>
<include>**/*Spec.*</include>
<include>**/junit4/*Test.*</include>
<include>**/junit4/*Tests.*</include>
<include>**/junit4/*Spec.*</include>
</includes>
<reportFormat>plain</reportFormat>
<failIfNoTests>true</failIfNoTests>
<junitPlatformArtifactName>
org.junit.jupiter:junit-vintage-engine
</junitPlatformArtifactName>
</configuration>
<executions>
<execution>

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.cloud.contract.stubrunner.junit
package org.springframework.cloud.contract.stubrunner.junit4
import java.util.concurrent.TimeUnit
@@ -24,6 +24,7 @@ import org.junit.ClassRule
import spock.lang.Shared
import spock.lang.Specification
import org.springframework.cloud.contract.stubrunner.junit.StubRunnerRule
import org.springframework.cloud.contract.stubrunner.spring.StubRunnerProperties
import org.springframework.cloud.contract.verifier.converter.YamlContract
import org.springframework.cloud.contract.verifier.messaging.MessageVerifier

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.cloud.contract.stubrunner.junit;
package org.springframework.cloud.contract.stubrunner.junit4;
import java.io.InputStream;
import java.net.URI;
@@ -25,6 +25,7 @@ import org.junit.BeforeClass;
import org.junit.ClassRule;
import org.junit.Test;
import org.springframework.cloud.contract.stubrunner.junit.StubRunnerRule;
import org.springframework.cloud.contract.stubrunner.spring.StubRunnerProperties;
import org.springframework.util.StreamUtils;

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.cloud.contract.stubrunner.junit
package org.springframework.cloud.contract.stubrunner.junit4
import org.junit.AfterClass
import org.junit.BeforeClass
@@ -22,6 +22,7 @@ import org.junit.ClassRule
import spock.lang.Shared
import spock.lang.Specification
import org.springframework.cloud.contract.stubrunner.junit.StubRunnerRule
import org.springframework.cloud.contract.stubrunner.spring.StubRunnerProperties
/**

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.cloud.contract.stubrunner.junit;
package org.springframework.cloud.contract.stubrunner.junit4;
import java.io.InputStream;
import java.net.URI;
@@ -25,6 +25,7 @@ import org.junit.BeforeClass;
import org.junit.ClassRule;
import org.junit.Test;
import org.springframework.cloud.contract.stubrunner.junit.StubRunnerRule;
import org.springframework.cloud.contract.stubrunner.spring.StubRunnerProperties;
import org.springframework.util.StreamUtils;

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.cloud.contract.stubrunner.junit
package org.springframework.cloud.contract.stubrunner.junit4
import org.junit.AfterClass
import org.junit.BeforeClass
@@ -22,6 +22,7 @@ import org.junit.ClassRule
import spock.lang.Shared
import spock.lang.Specification
import org.springframework.cloud.contract.stubrunner.junit.StubRunnerRule
import org.springframework.cloud.contract.stubrunner.spring.StubRunnerProperties
/**

View File

@@ -31,8 +31,7 @@ import org.springframework.test.context.ContextConfiguration
* @author Marcin Grzejszczak
*/
// tag::boot_usage[]
@ContextConfiguration(classes = StubRunnerBoot, loader = SpringBootContextLoader)
@SpringBootTest(properties = "spring.cloud.zookeeper.enabled=false")
@SpringBootTest(classes = StubRunnerBoot, properties = "spring.cloud.zookeeper.enabled=false")
@ActiveProfiles("test")
class StubRunnerBootSpec extends Specification {

View File

@@ -20,15 +20,12 @@ import com.github.tomakehurst.wiremock.core.WireMockConfiguration
import groovy.transform.CompileStatic
import org.apache.commons.logging.Log
import org.apache.commons.logging.LogFactory
import org.junit.AfterClass
import org.junit.BeforeClass
import spock.lang.Issue
import spock.lang.Specification
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.beans.factory.annotation.Value
import org.springframework.boot.autoconfigure.EnableAutoConfiguration
import org.springframework.boot.test.context.SpringBootContextLoader
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.cloud.contract.stubrunner.HttpServerStubConfiguration
import org.springframework.cloud.contract.stubrunner.StubFinder
@@ -38,7 +35,6 @@ import org.springframework.cloud.contract.stubrunner.provider.wiremock.WireMockH
import org.springframework.context.annotation.Configuration
import org.springframework.core.env.Environment
import org.springframework.test.context.ActiveProfiles
import org.springframework.test.context.ContextConfiguration
import org.springframework.util.SocketUtils
/**
@@ -47,131 +43,132 @@ import org.springframework.util.SocketUtils
// Not necessary if Spring Cloud is used. TODO: make it work without this.
// tag::test[]
@ContextConfiguration(classes = Config, loader = SpringBootContextLoader)
@SpringBootTest(properties = [" stubrunner.cloud.enabled=false",
'foo=${stubrunner.runningstubs.fraudDetectionServer.port}',
'fooWithGroup=${stubrunner.runningstubs.org.springframework.cloud.contract.verifier.stubs.fraudDetectionServer.port}'])
@SpringBootTest(classes = Config, properties = [" stubrunner.cloud.enabled=false",
'foo=${stubrunner.runningstubs.fraudDetectionServer.port}',
'fooWithGroup=${stubrunner.runningstubs.org.springframework.cloud.contract.verifier.stubs.fraudDetectionServer.port}'])
// tag::annotation[]
@AutoConfigureStubRunner(mappingsOutputFolder = "target/outputmappings/",
httpServerStubConfigurer = HttpsForFraudDetection)
httpServerStubConfigurer = HttpsForFraudDetection)
// end::annotation[]
@ActiveProfiles("test")
class StubRunnerConfigurationSpec extends Specification {
@Autowired
StubFinder stubFinder
@Autowired
Environment environment
@StubRunnerPort("fraudDetectionServer")
int fraudDetectionServerPort
@StubRunnerPort("org.springframework.cloud.contract.verifier.stubs:fraudDetectionServer")
int fraudDetectionServerPortWithGroupId
@Value('${foo}')
Integer foo
@Autowired
StubFinder stubFinder
@Autowired
Environment environment
@StubRunnerPort("fraudDetectionServer")
int fraudDetectionServerPort
@StubRunnerPort("org.springframework.cloud.contract.verifier.stubs:fraudDetectionServer")
int fraudDetectionServerPortWithGroupId
@Value('${foo}')
Integer foo
@BeforeClass
@AfterClass
void setupProps() {
System.clearProperty("stubrunner.repository.root")
System.clearProperty("stubrunner.classifier")
WireMockHttpServerStubAccessor.clear()
}
void setupSpec() {
System.clearProperty("stubrunner.repository.root")
System.clearProperty("stubrunner.classifier")
WireMockHttpServerStubAccessor.clear()
}
def 'should mark all ports as random'() {
expect:
WireMockHttpServerStubAccessor.everyPortRandom()
}
void cleanupSpec() {
setupSpec()
}
def 'should start WireMock servers'() {
expect: 'WireMocks are running'
stubFinder.findStubUrl('org.springframework.cloud.contract.verifier.stubs', 'loanIssuance') != null
stubFinder.findStubUrl('loanIssuance') != null
stubFinder.findStubUrl('loanIssuance') == stubFinder.findStubUrl('org.springframework.cloud.contract.verifier.stubs', 'loanIssuance')
stubFinder.findStubUrl('loanIssuance') == stubFinder.findStubUrl('org.springframework.cloud.contract.verifier.stubs:loanIssuance')
stubFinder.findStubUrl('org.springframework.cloud.contract.verifier.stubs:loanIssuance:0.0.1-SNAPSHOT') == stubFinder.findStubUrl('org.springframework.cloud.contract.verifier.stubs:loanIssuance:0.0.1-SNAPSHOT:stubs')
stubFinder.findStubUrl('org.springframework.cloud.contract.verifier.stubs:fraudDetectionServer') != null
and:
stubFinder.findAllRunningStubs().isPresent('loanIssuance')
stubFinder.findAllRunningStubs().isPresent('org.springframework.cloud.contract.verifier.stubs', 'fraudDetectionServer')
stubFinder.findAllRunningStubs().isPresent('org.springframework.cloud.contract.verifier.stubs:fraudDetectionServer')
and: 'Stubs were registered'
"${stubFinder.findStubUrl('loanIssuance').toString()}/name".toURL().text == 'loanIssuance'
"${stubFinder.findStubUrl('fraudDetectionServer').toString()}/name".toURL().text == 'fraudDetectionServer'
and: 'Fraud Detection is an HTTPS endpoint'
stubFinder.findStubUrl('fraudDetectionServer').toString().startsWith("https")
}
def 'should mark all ports as random'() {
expect:
WireMockHttpServerStubAccessor.everyPortRandom()
}
def 'should throw an exception when stub is not found'() {
when:
stubFinder.findStubUrl('nonExistingService')
then:
thrown(StubNotFoundException)
when:
stubFinder.findStubUrl('nonExistingGroupId', 'nonExistingArtifactId')
then:
thrown(StubNotFoundException)
}
def 'should start WireMock servers'() {
expect: 'WireMocks are running'
stubFinder.findStubUrl('org.springframework.cloud.contract.verifier.stubs', 'loanIssuance') != null
stubFinder.findStubUrl('loanIssuance') != null
stubFinder.findStubUrl('loanIssuance') == stubFinder.findStubUrl('org.springframework.cloud.contract.verifier.stubs', 'loanIssuance')
stubFinder.findStubUrl('loanIssuance') == stubFinder.findStubUrl('org.springframework.cloud.contract.verifier.stubs:loanIssuance')
stubFinder.findStubUrl('org.springframework.cloud.contract.verifier.stubs:loanIssuance:0.0.1-SNAPSHOT') == stubFinder.findStubUrl('org.springframework.cloud.contract.verifier.stubs:loanIssuance:0.0.1-SNAPSHOT:stubs')
stubFinder.findStubUrl('org.springframework.cloud.contract.verifier.stubs:fraudDetectionServer') != null
and:
stubFinder.findAllRunningStubs().isPresent('loanIssuance')
stubFinder.findAllRunningStubs().isPresent('org.springframework.cloud.contract.verifier.stubs', 'fraudDetectionServer')
stubFinder.findAllRunningStubs().isPresent('org.springframework.cloud.contract.verifier.stubs:fraudDetectionServer')
and: 'Stubs were registered'
"${stubFinder.findStubUrl('loanIssuance').toString()}/name".toURL().text == 'loanIssuance'
"${stubFinder.findStubUrl('fraudDetectionServer').toString()}/name".toURL().text == 'fraudDetectionServer'
and: 'Fraud Detection is an HTTPS endpoint'
stubFinder.findStubUrl('fraudDetectionServer').toString().startsWith("https")
}
def 'should register started servers as environment variables'() {
expect:
environment.getProperty("stubrunner.runningstubs.loanIssuance.port") != null
stubFinder.findAllRunningStubs().getPort("loanIssuance") == (environment.getProperty("stubrunner.runningstubs.loanIssuance.port") as Integer)
and:
environment.getProperty("stubrunner.runningstubs.fraudDetectionServer.port") != null
stubFinder.findAllRunningStubs().getPort("fraudDetectionServer") == (environment.getProperty("stubrunner.runningstubs.fraudDetectionServer.port") as Integer)
and:
environment.getProperty("stubrunner.runningstubs.fraudDetectionServer.port") != null
stubFinder.findAllRunningStubs().getPort("fraudDetectionServer") == (environment.getProperty("stubrunner.runningstubs.org.springframework.cloud.contract.verifier.stubs.fraudDetectionServer.port") as Integer)
}
def 'should throw an exception when stub is not found'() {
when:
stubFinder.findStubUrl('nonExistingService')
then:
thrown(StubNotFoundException)
when:
stubFinder.findStubUrl('nonExistingGroupId', 'nonExistingArtifactId')
then:
thrown(StubNotFoundException)
}
def 'should be able to interpolate a running stub in the passed test property'() {
given:
int fraudPort = stubFinder.findAllRunningStubs().getPort("fraudDetectionServer")
expect:
fraudPort > 0
environment.getProperty("foo", Integer) == fraudPort
environment.getProperty("fooWithGroup", Integer) == fraudPort
foo == fraudPort
}
def 'should register started servers as environment variables'() {
expect:
environment.getProperty("stubrunner.runningstubs.loanIssuance.port") != null
stubFinder.findAllRunningStubs().getPort("loanIssuance") == (environment.getProperty("stubrunner.runningstubs.loanIssuance.port") as Integer)
and:
environment.getProperty("stubrunner.runningstubs.fraudDetectionServer.port") != null
stubFinder.findAllRunningStubs().getPort("fraudDetectionServer") == (environment.getProperty("stubrunner.runningstubs.fraudDetectionServer.port") as Integer)
and:
environment.getProperty("stubrunner.runningstubs.fraudDetectionServer.port") != null
stubFinder.findAllRunningStubs().getPort("fraudDetectionServer") == (environment.getProperty("stubrunner.runningstubs.org.springframework.cloud.contract.verifier.stubs.fraudDetectionServer.port") as Integer)
}
@Issue("#573")
def 'should be able to retrieve the port of a running stub via an annotation'() {
given:
int fraudPort = stubFinder.findAllRunningStubs().getPort("fraudDetectionServer")
expect:
fraudPort > 0
fraudDetectionServerPort == fraudPort
fraudDetectionServerPortWithGroupId == fraudPort
}
def 'should be able to interpolate a running stub in the passed test property'() {
given:
int fraudPort = stubFinder.findAllRunningStubs().getPort("fraudDetectionServer")
expect:
fraudPort > 0
environment.getProperty("foo", Integer) == fraudPort
environment.getProperty("fooWithGroup", Integer) == fraudPort
foo == fraudPort
}
def 'should dump all mappings to a file'() {
when:
def url = stubFinder.findStubUrl("fraudDetectionServer")
then:
new File("target/outputmappings/", "fraudDetectionServer_${url.port}").exists()
}
@Issue("#573")
def 'should be able to retrieve the port of a running stub via an annotation'() {
given:
int fraudPort = stubFinder.findAllRunningStubs().getPort("fraudDetectionServer")
expect:
fraudPort > 0
fraudDetectionServerPort == fraudPort
fraudDetectionServerPortWithGroupId == fraudPort
}
@Configuration
@EnableAutoConfiguration
static class Config {}
def 'should dump all mappings to a file'() {
when:
def url = stubFinder.findStubUrl("fraudDetectionServer")
then:
new File("target/outputmappings/", "fraudDetectionServer_${url.port}").exists()
}
// tag::wireMockHttpServerStubConfigurer[]
@CompileStatic
static class HttpsForFraudDetection extends WireMockHttpServerStubConfigurer {
@Configuration
@EnableAutoConfiguration
static class Config {}
private static final Log log = LogFactory.getLog(HttpsForFraudDetection)
// tag::wireMockHttpServerStubConfigurer[]
@CompileStatic
static class HttpsForFraudDetection extends WireMockHttpServerStubConfigurer {
@Override
WireMockConfiguration configure(WireMockConfiguration httpStubConfiguration, HttpServerStubConfiguration httpServerStubConfiguration) {
if (httpServerStubConfiguration.stubConfiguration.artifactId == "fraudDetectionServer") {
int httpsPort = SocketUtils.findAvailableTcpPort()
log.info("Will set HTTPs port [" + httpsPort + "] for fraud detection server")
return httpStubConfiguration
.httpsPort(httpsPort)
}
return httpStubConfiguration
}
}
// end::wireMockHttpServerStubConfigurer[]
private static final Log log = LogFactory.getLog(HttpsForFraudDetection)
@Override
WireMockConfiguration configure(WireMockConfiguration httpStubConfiguration, HttpServerStubConfiguration httpServerStubConfiguration) {
if (httpServerStubConfiguration.stubConfiguration.artifactId == "fraudDetectionServer") {
int httpsPort = SocketUtils.findAvailableTcpPort()
log.info("Will set HTTPs port [" + httpsPort + "] for fraud detection server")
return httpStubConfiguration
.httpsPort(httpsPort)
}
return httpStubConfiguration
}
}
// end::wireMockHttpServerStubConfigurer[]
}
// end::test[]

View File

@@ -16,22 +16,18 @@
package org.springframework.cloud.contract.stubrunner.spring
import spock.lang.Specification
import org.springframework.beans.factory.annotation.Value
import org.springframework.boot.autoconfigure.EnableAutoConfiguration
import org.springframework.boot.test.context.SpringBootContextLoader
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.context.annotation.Configuration
import org.springframework.test.context.ActiveProfiles
import org.springframework.test.context.ContextConfiguration
/**
* @author Marcin Grzejszczak
*/
@ContextConfiguration(classes = Config, loader = SpringBootContextLoader)
@SpringBootTest(properties = ['some.property1=org.springframework.cloud.contract.verifier.stubs:loanIssuance'])
@SpringBootTest(classes = Config, properties = ['some.property1=org.springframework.cloud.contract.verifier.stubs:loanIssuance'])
@AutoConfigureStubRunner
@ActiveProfiles("test-with-placeholders")
class StubRunnerOptionsBuilderSpec extends Specification {

View File

@@ -16,13 +16,11 @@
package org.springframework.cloud.contract.stubrunner.spring.cloud
import org.junit.AfterClass
import org.junit.BeforeClass
import spock.lang.Specification
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.autoconfigure.EnableAutoConfiguration
import org.springframework.boot.test.context.SpringBootContextLoader
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.cloud.client.loadbalancer.LoadBalanced
import org.springframework.cloud.consul.ConsulAutoConfiguration
import org.springframework.cloud.contract.stubrunner.StubFinder
@@ -35,62 +33,64 @@ import org.springframework.cloud.zookeeper.ZookeeperAutoConfiguration
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.test.context.ActiveProfiles
import org.springframework.test.context.ContextConfiguration
import org.springframework.web.client.RestTemplate
/**
* @author Marcin Grzejszczak
*/
@ContextConfiguration(classes = Config, loader = SpringBootContextLoader)
@SpringBootTest(classes = Config)
@ActiveProfiles("cloudtest")
// tag::autoconfigure[]
@AutoConfigureStubRunner(
ids = ["org.springframework.cloud.contract.verifier.stubs:loanIssuance",
"org.springframework.cloud.contract.verifier.stubs:fraudDetectionServer",
"org.springframework.cloud.contract.verifier.stubs:bootService"],
stubsMode = StubRunnerProperties.StubsMode.REMOTE,
repositoryRoot = "classpath:m2repo/repository/")
ids = ["org.springframework.cloud.contract.verifier.stubs:loanIssuance",
"org.springframework.cloud.contract.verifier.stubs:fraudDetectionServer",
"org.springframework.cloud.contract.verifier.stubs:bootService"],
stubsMode = StubRunnerProperties.StubsMode.REMOTE,
repositoryRoot = "classpath:m2repo/repository/")
// end::autoconfigure[]
class StubRunnerSpringCloudAutoConfigurationSpec extends Specification {
@Autowired
StubFinder stubFinder
@Autowired
@LoadBalanced
RestTemplate restTemplate
@Autowired
LoadBalancerClientFactory loadBalancerClientFactory;
@Autowired
StubFinder stubFinder
@Autowired
@LoadBalanced
RestTemplate restTemplate
@Autowired
LoadBalancerClientFactory loadBalancerClientFactory;
@BeforeClass
@AfterClass
static void setupProps() {
System.clearProperty("stubrunner.repository.root")
System.clearProperty("stubrunner.classifier")
}
void setupSpec() {
System.clearProperty("stubrunner.repository.root")
System.clearProperty("stubrunner.classifier")
}
def setup() {
assert loadBalancerClientFactory instanceof StubRunnerLoadBalancerClientFactory
}
void cleanupSpec() {
setupSpec()
}
// tag::test[]
def 'should make service discovery work'() {
expect: 'WireMocks are running'
"${stubFinder.findStubUrl('loanIssuance').toString()}/name".toURL().text == 'loanIssuance'
"${stubFinder.findStubUrl('fraudDetectionServer').toString()}/name".toURL().text == 'fraudDetectionServer'
and: 'Stubs can be reached via load service discovery'
restTemplate.getForObject('http://loanIssuance/name', String) == 'loanIssuance'
restTemplate.getForObject('http://someNameThatShouldMapFraudDetectionServer/name', String) == 'fraudDetectionServer'
}
// end::test[]
def setup() {
assert loadBalancerClientFactory instanceof StubRunnerLoadBalancerClientFactory
}
@Configuration
@EnableAutoConfiguration(exclude = [EurekaClientAutoConfiguration,
ConsulAutoConfiguration, ZookeeperAutoConfiguration])
static class Config {
// tag::test[]
def 'should make service discovery work'() {
expect: 'WireMocks are running'
"${stubFinder.findStubUrl('loanIssuance').toString()}/name".toURL().text == 'loanIssuance'
"${stubFinder.findStubUrl('fraudDetectionServer').toString()}/name".toURL().text == 'fraudDetectionServer'
and: 'Stubs can be reached via load service discovery'
restTemplate.getForObject('http://loanIssuance/name', String) == 'loanIssuance'
restTemplate.getForObject('http://someNameThatShouldMapFraudDetectionServer/name', String) == 'fraudDetectionServer'
}
// end::test[]
@Bean
@LoadBalanced
RestTemplate restTemplate() {
return new RestTemplate()
}
}
@Configuration
@EnableAutoConfiguration(exclude = [EurekaClientAutoConfiguration,
ConsulAutoConfiguration, ZookeeperAutoConfiguration])
static class Config {
@Bean
@LoadBalanced
RestTemplate restTemplate() {
return new RestTemplate()
}
}
}

View File

@@ -22,7 +22,7 @@ import spock.lang.Specification
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.autoconfigure.EnableAutoConfiguration
import org.springframework.boot.test.context.SpringBootContextLoader
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.cloud.client.ServiceInstance
import org.springframework.cloud.client.discovery.ReactiveDiscoveryClient
import org.springframework.cloud.client.loadbalancer.LoadBalanced
@@ -37,61 +37,61 @@ import org.springframework.cloud.zookeeper.ZookeeperAutoConfiguration
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.test.context.ActiveProfiles
import org.springframework.test.context.ContextConfiguration
import org.springframework.web.client.RestTemplate
/**
* @author Marcin Grzejszczak
*/
@ContextConfiguration(classes = Config, loader = SpringBootContextLoader)
@SpringBootTest(classes = Config)
@ActiveProfiles("cloudtest")
@AutoConfigureStubRunner(
ids = ["org.springframework.cloud.contract.verifier.stubs:loanIssuance",
"org.springframework.cloud.contract.verifier.stubs:fraudDetectionServer",
"org.springframework.cloud.contract.verifier.stubs:bootService"],
stubsMode = StubRunnerProperties.StubsMode.REMOTE,
repositoryRoot = "classpath:m2repo/repository/")
ids = ["org.springframework.cloud.contract.verifier.stubs:loanIssuance",
"org.springframework.cloud.contract.verifier.stubs:fraudDetectionServer",
"org.springframework.cloud.contract.verifier.stubs:bootService"],
stubsMode = StubRunnerProperties.StubsMode.REMOTE,
repositoryRoot = "classpath:m2repo/repository/")
class StubRunnerSpringCloudReactiveAutoConfigurationSpec extends Specification {
@Autowired
StubFinder stubFinder
@Autowired
ReactiveDiscoveryClient reactiveDiscoveryClient;
@Autowired
LoadBalancerClientFactory loadBalancerClientFactory;
RestTemplate restTemplate = new RestTemplate()
@Autowired
StubFinder stubFinder
@Autowired
ReactiveDiscoveryClient reactiveDiscoveryClient;
@Autowired
LoadBalancerClientFactory loadBalancerClientFactory;
RestTemplate restTemplate = new RestTemplate()
@BeforeClass
@AfterClass
static void setupProps() {
System.clearProperty("stubrunner.repository.root")
System.clearProperty("stubrunner.classifier")
}
@BeforeClass
@AfterClass
static void setupProps() {
System.clearProperty("stubrunner.repository.root")
System.clearProperty("stubrunner.classifier")
}
def setup() {
assert loadBalancerClientFactory instanceof StubRunnerLoadBalancerClientFactory
}
def setup() {
assert loadBalancerClientFactory instanceof StubRunnerLoadBalancerClientFactory
}
// tag::test[]
def 'should make service discovery work'() {
expect: 'WireMocks are running'
"${stubFinder.findStubUrl('loanIssuance').toString()}/name".toURL().text == 'loanIssuance'
"${stubFinder.findStubUrl('fraudDetectionServer').toString()}/name".toURL().text == 'fraudDetectionServer'
and: 'Stubs can be reached via load service discovery'
ServiceInstance loanIssuance = reactiveDiscoveryClient.getInstances('loanIssuance').blockFirst()
restTemplate.getForObject(loanIssuance.uri.toString() + '/name', String) == 'loanIssuance'
ServiceInstance fraudDetection = reactiveDiscoveryClient.getInstances('someNameThatShouldMapFraudDetectionServer').blockFirst()
restTemplate.getForObject(fraudDetection.uri.toString() + '/name', String)== 'fraudDetectionServer'
}
// end::test[]
// tag::test[]
def 'should make service discovery work'() {
expect: 'WireMocks are running'
"${stubFinder.findStubUrl('loanIssuance').toString()}/name".toURL().text == 'loanIssuance'
"${stubFinder.findStubUrl('fraudDetectionServer').toString()}/name".toURL().text == 'fraudDetectionServer'
and: 'Stubs can be reached via load service discovery'
ServiceInstance loanIssuance = reactiveDiscoveryClient.getInstances('loanIssuance').blockFirst()
restTemplate.getForObject(loanIssuance.uri.toString() + '/name', String) == 'loanIssuance'
ServiceInstance fraudDetection = reactiveDiscoveryClient.getInstances('someNameThatShouldMapFraudDetectionServer').blockFirst()
restTemplate.getForObject(fraudDetection.uri.toString() + '/name', String) == 'fraudDetectionServer'
}
// end::test[]
@Configuration
@EnableAutoConfiguration(exclude = [EurekaClientAutoConfiguration,
ConsulAutoConfiguration, ZookeeperAutoConfiguration])
static class Config {
@Configuration
@EnableAutoConfiguration(exclude = [EurekaClientAutoConfiguration,
ConsulAutoConfiguration, ZookeeperAutoConfiguration])
static class Config {
@Bean
@LoadBalanced
RestTemplate restTemplate() {
return new RestTemplate()
}
}
@Bean
@LoadBalanced
RestTemplate restTemplate() {
return new RestTemplate()
}
}
}

View File

@@ -42,12 +42,11 @@ import org.springframework.test.context.ContextConfiguration
* @author Marcin Grzejszczak
*/
// tag::test[]
@ContextConfiguration(classes = Config, loader = SpringBootContextLoader)
@SpringBootTest(properties = ["spring.application.name=bar-consumer"])
@SpringBootTest(classes = Config, properties = ["spring.application.name=bar-consumer"])
@AutoConfigureStubRunner(ids = "org.springframework.cloud.contract.verifier.stubs:producerWithMultipleConsumers",
repositoryRoot = "classpath:m2repo/repository/",
stubsMode = StubRunnerProperties.StubsMode.REMOTE,
stubsPerConsumer = true)
repositoryRoot = "classpath:m2repo/repository/",
stubsMode = StubRunnerProperties.StubsMode.REMOTE,
stubsPerConsumer = true)
@ActiveProfiles("streamconsumer")
class StubRunnerStubsPerConsumerSpec extends Specification {
// end::test[]

View File

@@ -23,7 +23,6 @@ import spock.lang.Specification
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.autoconfigure.EnableAutoConfiguration
import org.springframework.boot.autoconfigure.ImportAutoConfiguration
import org.springframework.boot.test.context.SpringBootContextLoader
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.boot.test.web.client.TestRestTemplate
import org.springframework.cloud.contract.stubrunner.StubFinder
@@ -37,18 +36,16 @@ import org.springframework.core.env.Environment
import org.springframework.http.ResponseEntity
import org.springframework.messaging.Message
import org.springframework.test.context.ActiveProfiles
import org.springframework.test.context.ContextConfiguration
/**
* @author Marcin Grzejszczak
*/
// tag::test[]
@ContextConfiguration(classes = Config, loader = SpringBootContextLoader)
@SpringBootTest
@SpringBootTest(classes = Config)
@AutoConfigureStubRunner(ids = "org.springframework.cloud.contract.verifier.stubs:producerWithMultipleConsumers",
repositoryRoot = "classpath:m2repo/repository/",
consumerName = "foo-consumer",
stubsMode = StubRunnerProperties.StubsMode.REMOTE,
stubsPerConsumer = true)
repositoryRoot = "classpath:m2repo/repository/",
consumerName = "foo-consumer",
stubsMode = StubRunnerProperties.StubsMode.REMOTE,
stubsPerConsumer = true)
@ActiveProfiles("streamconsumer")
class StubRunnerStubsPerConsumerWithConsumerNameSpec extends Specification {
// end::test[]

View File

@@ -18,8 +18,7 @@ package org.springframework.cloud.contract.stubrunner.spring.cloud.consul
import com.ecwid.consul.v1.ConsulClient
import com.ecwid.consul.v1.agent.model.NewService
import org.junit.AfterClass
import org.junit.BeforeClass
import groovy.transform.CompileStatic
import org.mockito.ArgumentMatcher
import spock.lang.Specification
@@ -41,8 +40,7 @@ import static org.mockito.Mockito.mock
/**
* @author Marcin Grzejszczak
*/
@ContextConfiguration(classes = Config, loader = SpringBootContextLoader)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
@SpringBootTest(classes = Config, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
properties = ["eureka.client.enabled=false",
"spring.cloud.zookeeper.enabled=false",
"stubrunner.cloud.stubbed.discovery.enabled=false",
@@ -51,24 +49,25 @@ import static org.mockito.Mockito.mock
"stubrunner.cloud.consul.enabled=true",
"stubrunner.cloud.zookeeper.enabled=false",
"debug=true"])
@AutoConfigureStubRunner(ids =
["org.springframework.cloud.contract.verifier.stubs:loanIssuance",
"org.springframework.cloud.contract.verifier.stubs:fraudDetectionServer",
"org.springframework.cloud.contract.verifier.stubs:bootService"],
stubsMode = StubRunnerProperties.StubsMode.REMOTE,
repositoryRoot = "classpath:m2repo/repository/")
@AutoConfigureStubRunner(ids = ["org.springframework.cloud.contract.verifier.stubs:loanIssuance",
"org.springframework.cloud.contract.verifier.stubs:fraudDetectionServer",
"org.springframework.cloud.contract.verifier.stubs:bootService"] ,
stubsMode = StubRunnerProperties.StubsMode.REMOTE ,
repositoryRoot = "classpath:m2repo/repository/" )
class StubRunnerSpringCloudConsulAutoConfigurationSpec extends Specification {
@Autowired
ConsulClient client
@BeforeClass
@AfterClass
static void setupProps() {
void setupSpec() {
System.clearProperty("stubrunner.stubs.repository.root")
System.clearProperty("stubrunner.stubs.classifier")
}
void cleanupSpec() {
setupSpec()
}
def 'should make service discovery work for #serviceName'() {
given:
final String expectedId = serviceName.split(':')[0]
@@ -97,6 +96,7 @@ class StubRunnerSpringCloudConsulAutoConfigurationSpec extends Specification {
}
}
@CompileStatic
@Configuration
@EnableAutoConfiguration
@EnableDiscoveryClient

View File

@@ -24,6 +24,7 @@ import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
import org.springframework.cloud.contract.stubrunner.junit4.StubRunnerRuleCustomPortJUnitTest;
import org.springframework.cloud.contract.stubrunner.spring.StubRunnerProperties;
import org.springframework.cloud.contract.verifier.converter.YamlContract;
import org.springframework.cloud.contract.verifier.messaging.MessageVerifier;

View File

@@ -21,6 +21,7 @@ import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
import org.springframework.cloud.contract.stubrunner.junit4.StubRunnerRuleCustomPortJUnitTest;
import org.springframework.cloud.contract.stubrunner.spring.StubRunnerProperties;
import static org.junit.jupiter.api.Assertions.assertThrows;

View File

@@ -24,6 +24,7 @@ import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
import org.springframework.cloud.contract.stubrunner.junit4.StubRunnerRuleJUnitTest;
import org.springframework.cloud.contract.stubrunner.spring.StubRunnerProperties;
import static org.assertj.core.api.Assertions.assertThat;

View File

@@ -24,6 +24,7 @@ import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
import org.springframework.cloud.contract.stubrunner.junit4.StubRunnerRuleJUnitTest;
import org.springframework.cloud.contract.stubrunner.spring.StubRunnerProperties;
import static org.assertj.core.api.Assertions.assertThat;

View File

@@ -26,23 +26,20 @@ import spock.lang.Issue
import spock.lang.Specification
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.context.SpringBootContextLoader
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.cloud.contract.spec.Contract
import org.springframework.cloud.contract.verifier.messaging.boot.AutoConfigureMessageVerifier
import org.springframework.cloud.contract.verifier.messaging.internal.ContractVerifierMessage
import org.springframework.cloud.contract.verifier.messaging.internal.ContractVerifierMessaging
import org.springframework.cloud.contract.verifier.messaging.internal.ContractVerifierObjectMapper
import org.springframework.test.context.ContextConfiguration
import static com.toomuchcoding.jsonassert.JsonAssertion.assertThatJson
import static org.assertj.core.api.Assertions.assertThat
import static org.springframework.cloud.contract.verifier.messaging.util.ContractVerifierMessagingUtil.headers
// Context configuration would end up in base class
@ContextConfiguration(classes = [AmqpMessagingApplication], loader = SpringBootContextLoader)
@AutoConfigureMessageVerifier
@SpringBootTest(properties = "stubrunner.amqp.enabled=true")
@SpringBootTest(classes = AmqpMessagingApplication, properties = "stubrunner.amqp.enabled=true")
class AmqpMessagingApplicationSpec extends Specification {
// ALL CASES

View File

@@ -23,12 +23,12 @@ import com.jayway.jsonpath.JsonPath
import com.toomuchcoding.jsonassert.JsonAssertion
import org.apache.camel.Message
import org.apache.camel.model.ModelCamelContext
import org.junit.BeforeClass
import spock.lang.Specification
import spock.util.concurrent.PollingConditions
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.context.SpringBootContextLoader
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.cloud.contract.spec.Contract
import org.springframework.cloud.contract.verifier.messaging.MessageVerifier
import org.springframework.cloud.contract.verifier.messaging.boot.AutoConfigureMessageVerifier
@@ -39,8 +39,8 @@ import org.springframework.test.context.ContextConfiguration
* SPIKE ON TESTS FROM NOTES IN MessagingSpec
*/
// Context configuration would end up in base class
@ContextConfiguration(classes = [CamelMessagingApplication], loader = SpringBootContextLoader)
@AutoConfigureMessageVerifier
@SpringBootTest(classes = CamelMessagingApplication)
class CamelMessagingApplicationSpec extends Specification {
// ALL CASES
@@ -53,8 +53,7 @@ class CamelMessagingApplicationSpec extends Specification {
ContractVerifierObjectMapper contractVerifierObjectMapper = new ContractVerifierObjectMapper()
@BeforeClass
static void init() {
void setupSpec() {
System.setProperty("org.apache.activemq.SERIALIZABLE_PACKAGES", "*")
}

View File

@@ -24,17 +24,16 @@ import com.toomuchcoding.jsonassert.JsonAssertion
import spock.lang.Specification
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.context.SpringBootContextLoader
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.cloud.contract.spec.Contract
import org.springframework.cloud.contract.verifier.messaging.MessageVerifier
import org.springframework.cloud.contract.verifier.messaging.boot.AutoConfigureMessageVerifier
import org.springframework.cloud.contract.verifier.messaging.internal.ContractVerifierObjectMapper
import org.springframework.messaging.Message
import org.springframework.test.context.ContextConfiguration
// Context configuration would end up in base class
@ContextConfiguration(classes = [IntegrationMessagingApplication], loader = SpringBootContextLoader)
@AutoConfigureMessageVerifier
@SpringBootTest(classes = IntegrationMessagingApplication)
class IntegrationMessagingApplicationSpec extends Specification {
// ALL CASES

View File

@@ -23,12 +23,11 @@ import javax.jms.Message
import com.jayway.jsonpath.DocumentContext
import com.jayway.jsonpath.JsonPath
import com.toomuchcoding.jsonassert.JsonAssertion
import org.junit.BeforeClass
import spock.lang.Specification
import spock.util.concurrent.PollingConditions
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.context.SpringBootContextLoader
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.cloud.contract.spec.Contract
import org.springframework.cloud.contract.verifier.messaging.MessageVerifier
import org.springframework.cloud.contract.verifier.messaging.boot.AutoConfigureMessageVerifier
@@ -38,137 +37,138 @@ import org.springframework.cloud.contract.verifier.messaging.internal.ContractVe
import org.springframework.jms.core.JmsTemplate
import org.springframework.jms.core.MessagePostProcessor
import org.springframework.test.annotation.DirtiesContext
import org.springframework.test.context.ContextConfiguration
/**
* SPIKE ON TESTS FROM NOTES IN MessagingSpec
*/
// Context configuration would end up in base class
@ContextConfiguration(classes = [JmsMessagingApplication], loader = SpringBootContextLoader)
@AutoConfigureMessageVerifier
@SpringBootTest(classes = JmsMessagingApplication)
class JmsMessagingApplicationSpec extends Specification {
// ALL CASES
@Autowired
JmsTemplate jmsTemplate
@Autowired
BookDeleter bookDeleter
@Inject MessageVerifier<Message> messageVerifier
@Inject ContractVerifierMessaging contractVerifierMessaging
@Inject ContractVerifierObjectMapper contractVerifierObjectMapper
// ALL CASES
@Autowired
JmsTemplate jmsTemplate
@Autowired
BookDeleter bookDeleter
@Inject
MessageVerifier<Message> messageVerifier
@Inject
ContractVerifierMessaging contractVerifierMessaging
@Inject
ContractVerifierObjectMapper contractVerifierObjectMapper
@BeforeClass
static void init() {
System.setProperty("org.apache.activemq.SERIALIZABLE_PACKAGES", "*")
System.setProperty("debug", "true")
}
void setupSpec() {
System.setProperty("org.apache.activemq.SERIALIZABLE_PACKAGES", "*")
System.setProperty("debug", "true")
}
def "should work for triggered based messaging"() {
given:
Contract.make {
label 'some_label'
input {
triggeredBy('bookReturnedTriggered()')
}
outputMessage {
sentTo('output')
body('''{ "bookName" : "foo" }''')
headers {
header('BOOK-NAME', 'foo')
}
}
}
// generated test should look like this:
when:
bookReturnedTriggered()
then:
ContractVerifierMessage response = contractVerifierMessaging.receive('output')
response.getHeader('BOOK-NAME') == 'foo'
and:
DocumentContext parsedJson = JsonPath.
parse(contractVerifierObjectMapper.writeValueAsString(response.getPayload()))
JsonAssertion.assertThat(parsedJson).field('bookName').isEqualTo('foo')
}
def "should work for triggered based messaging"() {
given:
Contract.make {
label 'some_label'
input {
triggeredBy('bookReturnedTriggered()')
}
outputMessage {
sentTo('output')
body('''{ "bookName" : "foo" }''')
headers {
header('BOOK-NAME', 'foo')
}
}
}
// generated test should look like this:
when:
bookReturnedTriggered()
then:
ContractVerifierMessage response = contractVerifierMessaging.receive('output')
response.getHeader('BOOK-NAME') == 'foo'
and:
DocumentContext parsedJson = JsonPath.
parse(contractVerifierObjectMapper.writeValueAsString(response.getPayload()))
JsonAssertion.assertThat(parsedJson).field('bookName').isEqualTo('foo')
}
@DirtiesContext
def "should generate tests triggered by a message"() {
given:
Contract.make {
label 'some_label'
input {
messageFrom('input2')
messageBody([
bookName: 'foo'
])
messageHeaders {
header('sample', 'header')
}
}
outputMessage {
sentTo('output2')
body([
bookName: 'foo'
])
headers {
header('BOOK-NAME', 'foo')
}
}
}
// generated test should look like this:
when:
messageVerifier.send(
contractVerifierObjectMapper.writeValueAsString([bookName: 'foo']),
[sample: 'header'], 'input2')
then:
ContractVerifierMessage response = contractVerifierMessaging.receive('output2')
response.getHeader('BOOK-NAME') == 'foo'
and:
DocumentContext parsedJson = JsonPath.
parse(contractVerifierObjectMapper.writeValueAsString(response.getPayload()))
JsonAssertion.assertThat(parsedJson).field('bookName').isEqualTo('foo')
}
@DirtiesContext
def "should generate tests triggered by a message"() {
given:
Contract.make {
label 'some_label'
input {
messageFrom('input2')
messageBody([
bookName: 'foo'
])
messageHeaders {
header('sample', 'header')
}
}
outputMessage {
sentTo('output2')
body([
bookName: 'foo'
])
headers {
header('BOOK-NAME', 'foo')
}
}
}
// generated test should look like this:
when:
messageVerifier.send(
contractVerifierObjectMapper.writeValueAsString([bookName: 'foo']),
[sample: 'header'], 'input2')
then:
ContractVerifierMessage response = contractVerifierMessaging.receive('output2')
response.getHeader('BOOK-NAME') == 'foo'
and:
DocumentContext parsedJson = JsonPath.
parse(contractVerifierObjectMapper.writeValueAsString(response.getPayload()))
JsonAssertion.assertThat(parsedJson).field('bookName').isEqualTo('foo')
}
def "should generate tests without destination, triggered by a message"() {
given:
Contract.make {
label 'some_label'
input {
messageFrom('delete')
messageBody([
bookName: 'foo'
])
messageHeaders {
header('sample', 'header')
}
assertThat('bookWasDeleted()')
}
}
// generated test should look like this:
when:
messageVerifier.
send(contractVerifierObjectMapper.writeValueAsString([bookName: 'foo']),
[sample: 'header'], 'delete')
then:
noExceptionThrown()
bookWasDeleted()
}
def "should generate tests without destination, triggered by a message"() {
given:
Contract.make {
label 'some_label'
input {
messageFrom('delete')
messageBody([
bookName: 'foo'
])
messageHeaders {
header('sample', 'header')
}
assertThat('bookWasDeleted()')
}
}
// generated test should look like this:
when:
messageVerifier.
send(contractVerifierObjectMapper.writeValueAsString([bookName: 'foo']),
[sample: 'header'], 'delete')
then:
noExceptionThrown()
bookWasDeleted()
}
void bookReturnedTriggered() {
jmsTemplate.convertAndSend("output", '''{"bookName" : "foo" }''', new MessagePostProcessor() {
@Override
Message postProcessMessage(Message message) throws JMSException {
message.setStringProperty("BOOK-NAME", "foo")
return message
}
})
}
void bookReturnedTriggered() {
jmsTemplate.convertAndSend("output", '''{"bookName" : "foo" }''', new MessagePostProcessor() {
@Override
Message postProcessMessage(Message message) throws JMSException {
message.setStringProperty("BOOK-NAME", "foo")
return message
}
})
}
PollingConditions pollingConditions = new PollingConditions()
PollingConditions pollingConditions = new PollingConditions()
void bookWasDeleted() {
pollingConditions.eventually {
assert bookDeleter.bookSuccessfulyDeleted.get()
}
}
void bookWasDeleted() {
pollingConditions.eventually {
assert bookDeleter.bookSuccessfulyDeleted.get()
}
}
}

View File

@@ -26,7 +26,6 @@ import spock.lang.Specification
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.autoconfigure.EnableAutoConfiguration
import org.springframework.boot.autoconfigure.ImportAutoConfiguration
import org.springframework.boot.test.context.SpringBootContextLoader
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.cloud.contract.spec.Contract
import org.springframework.cloud.contract.verifier.messaging.MessageVerifier
@@ -35,134 +34,132 @@ import org.springframework.cloud.contract.verifier.messaging.internal.ContractVe
import org.springframework.cloud.stream.binder.test.TestChannelBinderConfiguration
import org.springframework.context.annotation.Configuration
import org.springframework.messaging.Message
import org.springframework.test.context.ContextConfiguration
/**
* SPIKE ON TESTS FROM NOTES IN MessagingSpec
*/
// Context configuration would end up in base class
@ContextConfiguration(classes = [Config, StreamMessagingApplication], loader = SpringBootContextLoader)
@SpringBootTest(properties = "debug=true")
@SpringBootTest(classes = [Config, StreamMessagingApplication], properties = "debug=true")
@AutoConfigureMessageVerifier
class StreamMessagingApplicationSpec extends Specification {
// ALL CASES
@Inject
MessageVerifier<Message<?>> contractVerifierMessaging
ContractVerifierObjectMapper contractVerifierObjectMapper = new ContractVerifierObjectMapper()
// ALL CASES
@Inject
MessageVerifier<Message<?>> contractVerifierMessaging
ContractVerifierObjectMapper contractVerifierObjectMapper = new ContractVerifierObjectMapper()
def "should work for triggered based messaging"() {
given:
def dsl = Contract.make {
label 'some_label'
input {
triggeredBy('bookReturnedTriggered()')
}
outputMessage {
sentTo('bookReturned')
body('''{ "bookName" : "foo" }''')
headers {
header('BOOK-NAME', 'foo')
}
}
}
// generated test should look like this:
when:
bookReturnedTriggered()
then:
def response = contractVerifierMessaging.receive('bookReturned')
response.headers.get('BOOK-NAME') == 'foo'
and:
DocumentContext parsedJson = JsonPath.
parse(contractVerifierObjectMapper.writeValueAsString(response.payload))
JsonAssertion.assertThat(parsedJson).field('bookName').isEqualTo('foo')
}
def "should work for triggered based messaging"() {
given:
def dsl = Contract.make {
label 'some_label'
input {
triggeredBy('bookReturnedTriggered()')
}
outputMessage {
sentTo('bookReturned')
body('''{ "bookName" : "foo" }''')
headers {
header('BOOK-NAME', 'foo')
}
}
}
// generated test should look like this:
when:
bookReturnedTriggered()
then:
def response = contractVerifierMessaging.receive('bookReturned')
response.headers.get('BOOK-NAME') == 'foo'
and:
DocumentContext parsedJson = JsonPath.
parse(contractVerifierObjectMapper.writeValueAsString(response.payload))
JsonAssertion.assertThat(parsedJson).field('bookName').isEqualTo('foo')
}
def "should generate tests triggered by a message"() {
given:
def dsl = Contract.make {
label 'some_label'
input {
messageFrom('inputDestination')
messageBody([
bookName: 'foo'
])
messageHeaders {
header('sample', 'header')
}
}
outputMessage {
sentTo('bookReturned')
body([
bookName: 'foo'
])
headers {
header('BOOK-NAME', 'foo')
}
}
}
def "should generate tests triggered by a message"() {
given:
def dsl = Contract.make {
label 'some_label'
input {
messageFrom('inputDestination')
messageBody([
bookName: 'foo'
])
messageHeaders {
header('sample', 'header')
}
}
outputMessage {
sentTo('bookReturned')
body([
bookName: 'foo'
])
headers {
header('BOOK-NAME', 'foo')
}
}
}
// generated test should look like this:
// generated test should look like this:
when:
contractVerifierMessaging.send(
contractVerifierObjectMapper.writeValueAsString([bookName: 'foo']),
[sample: 'header'], 'inputDestination')
then:
def response = contractVerifierMessaging.receive('bookReturned')
response.headers.get('BOOK-NAME') == 'foo'
and:
DocumentContext parsedJson = JsonPath.
parse(contractVerifierObjectMapper.writeValueAsString(response.payload))
JsonAssertion.assertThat(parsedJson).field('bookName').isEqualTo('foo')
}
when:
contractVerifierMessaging.send(
contractVerifierObjectMapper.writeValueAsString([bookName: 'foo']),
[sample: 'header'], 'inputDestination')
then:
def response = contractVerifierMessaging.receive('bookReturned')
response.headers.get('BOOK-NAME') == 'foo'
and:
DocumentContext parsedJson = JsonPath.
parse(contractVerifierObjectMapper.writeValueAsString(response.payload))
JsonAssertion.assertThat(parsedJson).field('bookName').isEqualTo('foo')
}
def "should generate tests without destination, triggered by a message"() {
given:
def dsl = Contract.make {
label 'some_label'
input {
messageFrom("bookDeleted")
messageBody([
bookName: 'foo'
])
messageHeaders {
header('sample', 'header')
}
assertThat('bookWasDeleted()')
}
}
def "should generate tests without destination, triggered by a message"() {
given:
def dsl = Contract.make {
label 'some_label'
input {
messageFrom("bookDeleted")
messageBody([
bookName: 'foo'
])
messageHeaders {
header('sample', 'header')
}
assertThat('bookWasDeleted()')
}
}
// generated test should look like this:
// generated test should look like this:
when:
contractVerifierMessaging.
send(contractVerifierObjectMapper.writeValueAsString([bookName: 'foo']),
[sample: 'header'], "bookDeleted")
then:
noExceptionThrown()
bookWasDeleted()
}
when:
contractVerifierMessaging.
send(contractVerifierObjectMapper.writeValueAsString([bookName: 'foo']),
[sample: 'header'], "bookDeleted")
then:
noExceptionThrown()
bookWasDeleted()
}
// BASE CLASS WOULD HAVE THIS:
// BASE CLASS WOULD HAVE THIS:
@Autowired
BookService bookService
@Autowired
BookDeletedListener bookDeletedListener
@Autowired
BookService bookService
@Autowired
BookDeletedListener bookDeletedListener
void bookReturnedTriggered() {
bookService.returnBook(new BookReturned("foo"))
}
void bookReturnedTriggered() {
bookService.returnBook(new BookReturned("foo"))
}
void bookWasDeleted() {
assert bookDeletedListener.bookSuccessfulyDeleted.get()
}
void bookWasDeleted() {
assert bookDeletedListener.bookSuccessfulyDeleted.get()
}
@Configuration
@EnableAutoConfiguration
@ImportAutoConfiguration(TestChannelBinderConfiguration)
static class Config {
@Configuration
@EnableAutoConfiguration
@ImportAutoConfiguration(TestChannelBinderConfiguration)
static class Config {
}
}
}

View File

@@ -21,14 +21,13 @@ import org.mockito.Captor
import spock.lang.Specification
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.context.SpringBootContextLoader
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.cloud.contract.stubrunner.StubTrigger
import org.springframework.cloud.contract.stubrunner.spring.AutoConfigureStubRunner
import org.springframework.test.context.ActiveProfiles
import org.springframework.test.context.ContextConfiguration
@ContextConfiguration(classes = [AmqpMessagingApplication], loader = SpringBootContextLoader)
@AutoConfigureStubRunner
@SpringBootTest(classes = AmqpMessagingApplication)
@ActiveProfiles("listener")
class AmqpStubRunnerRabbitListenerSpec extends Specification {

View File

@@ -22,6 +22,7 @@ import spock.lang.Specification
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.context.SpringBootContextLoader
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.boot.test.mock.mockito.SpyBean
import org.springframework.cloud.contract.spec.Contract
import org.springframework.cloud.contract.stubrunner.StubTrigger
@@ -30,8 +31,8 @@ import org.springframework.test.context.ContextConfiguration
import static org.mockito.BDDMockito.then
@ContextConfiguration(classes = [AmqpMessagingApplication], loader = SpringBootContextLoader)
@AutoConfigureStubRunner
@SpringBootTest(classes = AmqpMessagingApplication)
class AmqpStubRunnerSpec extends Specification {
@Autowired

View File

@@ -55,20 +55,19 @@ import org.springframework.web.client.RestTemplate
* @author Marcin Grzejszczak
*/
//TODO: Speed up this test somehow (move it out of Spring Cloud Contract core to samples)
@ContextConfiguration(classes = Config, loader = SpringBootContextLoader)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
properties = ["stubrunner.cloud.eureka.enabled=true",
"stubrunner.cloud.stubbed.discovery.enabled=false",
"stubrunner.cloud.ribbon.enabled=false",
"eureka.client.enabled=true",
"eureka.instance.leaseRenewalIntervalInSeconds=1",
"ribbon.ServerListRefreshInterval=100"])
@SpringBootTest(classes = Config, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
properties = ["stubrunner.cloud.eureka.enabled=true",
"stubrunner.cloud.stubbed.discovery.enabled=false",
"stubrunner.cloud.ribbon.enabled=false",
"eureka.client.enabled=true",
"eureka.instance.leaseRenewalIntervalInSeconds=1",
"ribbon.ServerListRefreshInterval=100"])
@AutoConfigureStubRunner(ids =
["org.springframework.cloud.contract.verifier.stubs:loanIssuance",
"org.springframework.cloud.contract.verifier.stubs:fraudDetectionServer",
"org.springframework.cloud.contract.verifier.stubs:bootService"],
repositoryRoot = "classpath:m2repo/repository/",
stubsMode = StubRunnerProperties.StubsMode.REMOTE)
["org.springframework.cloud.contract.verifier.stubs:loanIssuance",
"org.springframework.cloud.contract.verifier.stubs:fraudDetectionServer",
"org.springframework.cloud.contract.verifier.stubs:bootService"] ,
repositoryRoot = "classpath:m2repo/repository/" ,
stubsMode = StubRunnerProperties.StubsMode.REMOTE )
class StubRunnerSpringCloudEurekaAutoConfigurationSpec extends Specification {
@Autowired

View File

@@ -17,14 +17,11 @@
package org.springframework.cloud.contract.stubrunner.spring.cloud.zookeeper
import org.apache.curator.test.TestingServer
import org.junit.AfterClass
import org.junit.BeforeClass
import spock.lang.Ignore
import spock.lang.Specification
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.autoconfigure.EnableAutoConfiguration
import org.springframework.boot.test.context.SpringBootContextLoader
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.cloud.client.discovery.EnableDiscoveryClient
import org.springframework.cloud.client.loadbalancer.LoadBalanced
@@ -35,23 +32,21 @@ import org.springframework.cloud.zookeeper.ZookeeperProperties
import org.springframework.cloud.zookeeper.discovery.ZookeeperDiscoveryClient
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.test.context.ContextConfiguration
import org.springframework.util.SocketUtils
import org.springframework.web.client.RestTemplate
/**
* @author Marcin Grzejszczak
*/
@ContextConfiguration(classes = Config, loader = SpringBootContextLoader)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
properties = ["stubrunner.cloud.stubbed.discovery.enabled=false",
"debug=true"])
@SpringBootTest(classes = Config, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
properties = ["stubrunner.cloud.stubbed.discovery.enabled=false",
"debug=true"])
@AutoConfigureStubRunner(ids =
["org.springframework.cloud.contract.verifier.stubs:loanIssuance",
"org.springframework.cloud.contract.verifier.stubs:fraudDetectionServer",
"org.springframework.cloud.contract.verifier.stubs:bootService"],
repositoryRoot = "classpath:m2repo/repository/",
stubsMode = StubRunnerProperties.StubsMode.REMOTE)
["org.springframework.cloud.contract.verifier.stubs:loanIssuance",
"org.springframework.cloud.contract.verifier.stubs:fraudDetectionServer",
"org.springframework.cloud.contract.verifier.stubs:bootService"] ,
repositoryRoot = "classpath:m2repo/repository/" ,
stubsMode = StubRunnerProperties.StubsMode.REMOTE )
class StubRunnerSpringCloudZookeeperAutoConfigurationSpec extends Specification {
@Autowired
@@ -62,13 +57,15 @@ class StubRunnerSpringCloudZookeeperAutoConfigurationSpec extends Specification
@Autowired
ZookeeperDiscoveryClient zookeeperServiceDiscovery
@BeforeClass
@AfterClass
static void setupProps() {
void setupSpec() {
System.clearProperty("stubrunner.stubs.repository.root")
System.clearProperty("stubrunner.stubs.classifier")
}
void cleanupSpec() {
setupSpec()
}
@Ignore
def 'should make service discovery work'() {
expect: 'WireMocks are running'

View File

@@ -1,283 +0,0 @@
/*
* 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.messaging.camel
import groovy.json.JsonOutput
import groovy.json.JsonSlurper
import org.apache.activemq.spring.ActiveMQConnectionFactory
import org.apache.camel.CamelContext
import org.apache.camel.ConsumerTemplate
import org.apache.camel.Exchange
import org.apache.camel.ProducerTemplate
import org.apache.camel.component.activemq.ActiveMQComponent
import org.apache.camel.component.jms.JmsConfiguration
import org.apache.camel.impl.engine.DefaultShutdownStrategy
import spock.lang.Ignore
import spock.lang.Specification
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.beans.factory.annotation.Value
import org.springframework.boot.autoconfigure.EnableAutoConfiguration
import org.springframework.boot.test.context.SpringBootContextLoader
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.cloud.contract.spec.Contract
import org.springframework.cloud.contract.stubrunner.StubFinder
import org.springframework.cloud.contract.stubrunner.spring.AutoConfigureStubRunner
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.ComponentScan
import org.springframework.context.annotation.Configuration
import org.springframework.test.annotation.DirtiesContext
import org.springframework.test.context.ContextConfiguration
/**
* @author Marcin Grzejszczak
*/
@ContextConfiguration(classes = Config, loader = SpringBootContextLoader)
@SpringBootTest(properties = "debug=true")
@AutoConfigureStubRunner
@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)
@Ignore
class CamelStubRunnerSpec extends Specification {
//TODO: fix extremely slow test that causes pipeline to fail and then unignore
@Autowired
StubFinder stubFinder
@Autowired
CamelContext camelContext
ConsumerTemplate consumerTemplate
ProducerTemplate producerTemplate
def setup() {
consumerTemplate = camelContext.createConsumerTemplate()
producerTemplate = camelContext.createProducerTemplate()
}
def cleanup() {
// ensure that message were taken from the queue
consumerTemplate.receive('jms:output', 100)
consumerTemplate.receive('jms:input', 100)
this.producerTemplate.stop()
this.consumerTemplate.stop()
def strategy = new DefaultShutdownStrategy(this.camelContext)
strategy.timeout = 1
this.camelContext.shutdownStrategy = strategy
}
def 'should not trigger a message that does not match input'() {
when:
producerTemplate.
sendBodyAndHeaders('jms:input', new BookReturned('notmatching'), [wrong: 'header_value'])
then:
Exchange receivedMessage = consumerTemplate.receive('jms:output', 100)
and:
receivedMessage == null
}
def 'should download the stub and register a route for it'() {
when:
// tag::client_send[]
producerTemplate.
sendBodyAndHeaders('jms:input', new BookReturned('foo'), [sample: 'header'])
// end::client_send[]
then:
// tag::client_receive[]
Exchange receivedMessage = consumerTemplate.receive('jms:output', 5000)
// end::client_receive[]
and:
// tag::client_receive_message[]
receivedMessage != null
assertThatBodyContainsBookNameFoo(receivedMessage.in.body)
receivedMessage.in.headers.get('BOOK-NAME') == 'foo'
// end::client_receive_message[]
}
def 'should trigger a message by label'() {
when:
// tag::client_trigger[]
stubFinder.trigger('return_book_1')
// end::client_trigger[]
then:
// tag::client_trigger_receive[]
Exchange receivedMessage = consumerTemplate.receive('jms:output', 5000)
// end::client_trigger_receive[]
and:
// tag::client_trigger_message[]
receivedMessage != null
assertThatBodyContainsBookNameFoo(receivedMessage.in.body)
receivedMessage.in.headers.get('BOOK-NAME') == 'foo'
// end::client_trigger_message[]
}
def 'should trigger a label for the existing groupId:artifactId'() {
when:
// tag::trigger_group_artifact[]
stubFinder.
trigger('org.springframework.cloud.contract.verifier.stubs:camelService', 'return_book_1')
// end::trigger_group_artifact[]
then:
Exchange receivedMessage = consumerTemplate.receive('jms:output', 5000)
and:
receivedMessage != null
assertThatBodyContainsBookNameFoo(receivedMessage.in.body)
receivedMessage.in.headers.get('BOOK-NAME') == 'foo'
}
def 'should trigger a label for the existing artifactId'() {
when:
// tag::trigger_artifact[]
stubFinder.trigger('camelService', 'return_book_1')
// end::trigger_artifact[]
then:
Exchange receivedMessage = consumerTemplate.receive('jms:output', 5000)
and:
receivedMessage != null
assertThatBodyContainsBookNameFoo(receivedMessage.in.body)
receivedMessage.in.headers.get('BOOK-NAME') == 'foo'
}
def 'should throw an exception when missing label is passed'() {
when:
stubFinder.trigger('missing label')
then:
thrown(IllegalArgumentException)
}
def 'should throw an exception when missing label and artifactid is passed'() {
when:
stubFinder.trigger('some:service', 'return_book_1')
then:
thrown(IllegalArgumentException)
}
def 'should trigger messages by running all triggers'() {
when:
// tag::trigger_all[]
stubFinder.trigger()
// end::trigger_all[]
then:
Exchange receivedMessage = consumerTemplate.receive('jms:output', 5000)
and:
receivedMessage != null
assertThatBodyContainsBookNameFoo(receivedMessage.in.body)
receivedMessage.in.headers.get('BOOK-NAME') == 'foo'
}
def 'should trigger a label with no output message'() {
when:
// tag::trigger_no_output[]
producerTemplate.
sendBodyAndHeaders('jms:delete', new BookReturned('foo'), [sample: 'header'])
// end::trigger_no_output[]
then:
noExceptionThrown()
}
private boolean assertThatBodyContainsBookNameFoo(Object payload) {
String objectAsString = payload instanceof String ? payload :
JsonOutput.toJson(payload)
def json = new JsonSlurper().parseText(objectAsString)
return json.bookName == 'foo'
}
@Configuration
@ComponentScan
@EnableAutoConfiguration
static class Config {
@Bean
ActiveMQConnectionFactory activeMQConnectionFactory(@Value('${activemq.url:vm://localhost?broker.persistent=false}') String url) {
ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory(brokerURL: url)
try {
factory.trustAllPackages = true
}
catch (Throwable e) {
}
return factory
}
@Bean
JmsConfiguration jmsConfiguration(ActiveMQConnectionFactory activeMQConnectionFactory) {
return new JmsConfiguration(connectionFactory: activeMQConnectionFactory)
}
@Bean
ActiveMQComponent activeMQComponent(JmsConfiguration jmsConfiguration) {
return new ActiveMQComponent(configuration: jmsConfiguration)
}
}
Contract dsl =
// tag::sample_dsl[]
Contract.make {
label 'return_book_1'
input {
triggeredBy('bookReturnedTriggered()')
}
outputMessage {
sentTo('jms:output')
body('''{ "bookName" : "foo" }''')
headers {
header('BOOK-NAME', 'foo')
}
}
}
// end::sample_dsl[]
Contract dsl2 =
// tag::sample_dsl_2[]
Contract.make {
label 'return_book_2'
input {
messageFrom('jms:input')
messageBody([
bookName: 'foo'
])
messageHeaders {
header('sample', 'header')
}
}
outputMessage {
sentTo('jms:output')
body([
bookName: 'foo'
])
headers {
header('BOOK-NAME', 'foo')
}
}
}
// end::sample_dsl_2[]
Contract dsl3 =
// tag::sample_dsl_3[]
Contract.make {
label 'delete_book'
input {
messageFrom('jms:delete')
messageBody([
bookName: 'foo'
])
messageHeaders {
header('sample', 'header')
}
assertThat('bookWasDeleted()')
}
}
// end::sample_dsl_3[]
}

View File

@@ -25,7 +25,7 @@ import spock.lang.Specification
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.autoconfigure.EnableAutoConfiguration
import org.springframework.boot.test.context.SpringBootContextLoader
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.cloud.contract.spec.Contract
import org.springframework.cloud.contract.stubrunner.StubFinder
import org.springframework.cloud.contract.stubrunner.spring.AutoConfigureStubRunner
@@ -34,14 +34,12 @@ import org.springframework.context.annotation.ComponentScan
import org.springframework.context.annotation.Configuration
import org.springframework.context.annotation.ImportResource
import org.springframework.messaging.Message
import org.springframework.test.context.ContextConfiguration
/**
* @author Marcin Grzejszczak
*/
@ContextConfiguration(classes = Config, loader = SpringBootContextLoader)
@ImportResource("classpath*:integration-context.xml")
@AutoConfigureStubRunner
@SpringBootTest(classes = Config)
@IgnoreIf({ os.windows })
class IntegrationStubRunnerSpec extends Specification {

View File

@@ -29,7 +29,6 @@ import spock.lang.Specification
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.beans.factory.annotation.Value
import org.springframework.boot.autoconfigure.EnableAutoConfiguration
import org.springframework.boot.test.context.SpringBootContextLoader
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.cloud.contract.spec.Contract
import org.springframework.cloud.contract.stubrunner.StubFinder
@@ -40,13 +39,10 @@ import org.springframework.context.annotation.Configuration
import org.springframework.jms.annotation.EnableJms
import org.springframework.jms.core.JmsTemplate
import org.springframework.jms.core.MessagePostProcessor
import org.springframework.test.context.ContextConfiguration
/**
* @author Marcin Grzejszczak
*/
@ContextConfiguration(classes = Config, loader = SpringBootContextLoader)
@SpringBootTest(properties = ["debug=true"])
@SpringBootTest(classes = Config, properties = ["debug=true"])
@AutoConfigureStubRunner
@IgnoreIf({ os.windows })
class JmsStubRunnerSpec extends Specification {

View File

@@ -28,9 +28,7 @@ import spock.util.concurrent.PollingConditions
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.autoconfigure.EnableAutoConfiguration
import org.springframework.boot.test.context.SpringBootContextLoader
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.cloud.contract.spec.Contract
import org.springframework.cloud.contract.stubrunner.StubFinder
import org.springframework.cloud.contract.stubrunner.spring.AutoConfigureStubRunner
import org.springframework.context.annotation.Bean
@@ -45,230 +43,228 @@ import org.springframework.messaging.Message
import org.springframework.messaging.MessageHeaders
import org.springframework.messaging.support.MessageBuilder
import org.springframework.stereotype.Component
import org.springframework.test.context.ContextConfiguration
/**
* @author Marcin Grzejszczak
*/
@ContextConfiguration(classes = Config, loader = SpringBootContextLoader)
@SpringBootTest(properties = ["debug=true"])
@SpringBootTest(classes = Config, properties = ["debug=true"])
@AutoConfigureStubRunner
@IgnoreIf({ os.windows })
@EmbeddedKafka(topics = ["input", "input2", "output", "delete"])
@Commons
class KafkaStubRunnerSpec extends Specification {
@Autowired
StubFinder stubFinder
@Autowired
KafkaTemplate kafkaTemplate
@Autowired
MyMessageListener myMessageListener
PollingConditions await = new PollingConditions(timeout: 15, initialDelay: 1, delay: 1)
@Autowired
StubFinder stubFinder
@Autowired
KafkaTemplate kafkaTemplate
@Autowired
MyMessageListener myMessageListener
PollingConditions await = new PollingConditions(timeout: 15, initialDelay: 1, delay: 1)
def setup() {
this.myMessageListener.clear()
}
def setup() {
this.myMessageListener.clear()
}
def cleanup() {
this.myMessageListener.clear()
}
def cleanup() {
this.myMessageListener.clear()
}
private Message receiveFromOutput() {
Message m = this.myMessageListener.output()
log.info("Received message [" + m + "]")
return m
}
private Message receiveFromOutput() {
Message m = this.myMessageListener.output()
log.info("Received message [" + m + "]")
return m
}
// Skipping the test on Jenkins cause it's for some reason flakey only there
def 'should download the stub and register a route for it'() {
expect:
await.eventually {
log.info("Sending the message")
// tag::client_send[]
Message message = MessageBuilder.createMessage(new BookReturned('foo'), new MessageHeaders([sample: "header",]))
kafkaTemplate.setDefaultTopic('input')
kafkaTemplate.send(message)
// end::client_send[]
log.info("Message sent")
log.info("Receiving the message")
// tag::client_receive[]
Message receivedMessage = receiveFromOutput()
// end::client_receive[]
log.info("Message received [" + receivedMessage + "]")
// tag::client_receive_message[]
assert receivedMessage != null
assert assertThatBodyContainsBookNameFoo(receivedMessage.getPayload())
assert receivedMessage.getHeaders().get('BOOK-NAME') == 'foo'
// end::client_receive_message[]
}
}
// Skipping the test on Jenkins cause it's for some reason flakey only there
def 'should download the stub and register a route for it'() {
expect:
await.eventually {
log.info("Sending the message")
// tag::client_send[]
Message message = MessageBuilder.createMessage(new BookReturned('foo'), new MessageHeaders([sample: "header",]))
kafkaTemplate.setDefaultTopic('input')
kafkaTemplate.send(message)
// end::client_send[]
log.info("Message sent")
log.info("Receiving the message")
// tag::client_receive[]
Message receivedMessage = receiveFromOutput()
// end::client_receive[]
log.info("Message received [" + receivedMessage + "]")
// tag::client_receive_message[]
assert receivedMessage != null
assert assertThatBodyContainsBookNameFoo(receivedMessage.getPayload())
assert receivedMessage.getHeaders().get('BOOK-NAME') == 'foo'
// end::client_receive_message[]
}
}
def 'should propagate the Kafka record key via message headers'() {
expect:
await.eventually {
log.info("Sending the message")
// tag::client_send[]
Message message = MessageBuilder.createMessage(new BookReturned('bar'), new MessageHeaders([kafka_messageKey: "bar5150",]))
kafkaTemplate.setDefaultTopic('input2')
kafkaTemplate.send(message)
// end::client_send[]
log.info("Message sent")
log.info("Receiving the message")
// tag::client_receive[]
Message receivedMessage = receiveFromOutput()
// end::client_receive[]
log.info("Message received [" + receivedMessage + "]")
// tag::client_receive_message[]
assert receivedMessage != null
assert assertThatBodyContainsBookName(receivedMessage.getPayload(), 'bar')
assert receivedMessage.getHeaders().get('BOOK-NAME') == 'bar'
assert receivedMessage.getHeaders().get("kafka_receivedMessageKey") == 'bar5150'
// end::client_receive_message[]
}
}
def 'should propagate the Kafka record key via message headers'() {
expect:
await.eventually {
log.info("Sending the message")
// tag::client_send[]
Message message = MessageBuilder.createMessage(new BookReturned('bar'), new MessageHeaders([kafka_messageKey: "bar5150",]))
kafkaTemplate.setDefaultTopic('input2')
kafkaTemplate.send(message)
// end::client_send[]
log.info("Message sent")
log.info("Receiving the message")
// tag::client_receive[]
Message receivedMessage = receiveFromOutput()
// end::client_receive[]
log.info("Message received [" + receivedMessage + "]")
// tag::client_receive_message[]
assert receivedMessage != null
assert assertThatBodyContainsBookName(receivedMessage.getPayload(), 'bar')
assert receivedMessage.getHeaders().get('BOOK-NAME') == 'bar'
assert receivedMessage.getHeaders().get("kafka_receivedMessageKey") == 'bar5150'
// end::client_receive_message[]
}
}
def 'should trigger a message by label'() {
expect:
await.eventually {
// tag::client_trigger[]
stubFinder.trigger('return_book_1')
// end::client_trigger[]
// tag::client_trigger_receive[]
Message receivedMessage = receiveFromOutput()
// end::client_trigger_receive[]
// tag::client_trigger_message[]
assert receivedMessage != null
assert assertThatBodyContainsBookNameFoo(receivedMessage.getPayload())
assert receivedMessage.getHeaders().get('BOOK-NAME') == 'foo'
// end::client_trigger_message[]
}
}
def 'should trigger a message by label'() {
expect:
await.eventually {
// tag::client_trigger[]
stubFinder.trigger('return_book_1')
// end::client_trigger[]
// tag::client_trigger_receive[]
Message receivedMessage = receiveFromOutput()
// end::client_trigger_receive[]
// tag::client_trigger_message[]
assert receivedMessage != null
assert assertThatBodyContainsBookNameFoo(receivedMessage.getPayload())
assert receivedMessage.getHeaders().get('BOOK-NAME') == 'foo'
// end::client_trigger_message[]
}
}
def 'should trigger a label for the existing groupId:artifactId'() {
expect:
await.eventually {
// tag::trigger_group_artifact[]
stubFinder.
trigger('my:stubs', 'return_book_1')
// end::trigger_group_artifact[]
Message receivedMessage = receiveFromOutput()
assert receivedMessage != null
assert assertThatBodyContainsBookNameFoo(receivedMessage.getPayload())
assert receivedMessage.getHeaders().get('BOOK-NAME') == 'foo'
}
}
def 'should trigger a label for the existing groupId:artifactId'() {
expect:
await.eventually {
// tag::trigger_group_artifact[]
stubFinder.
trigger('my:stubs', 'return_book_1')
// end::trigger_group_artifact[]
Message receivedMessage = receiveFromOutput()
assert receivedMessage != null
assert assertThatBodyContainsBookNameFoo(receivedMessage.getPayload())
assert receivedMessage.getHeaders().get('BOOK-NAME') == 'foo'
}
}
def 'should trigger a label for the existing artifactId'() {
expect:
await.eventually {
// tag::trigger_artifact[]
stubFinder.trigger('stubs', 'return_book_1')
// end::trigger_artifact[]
Message receivedMessage = receiveFromOutput()
assert receivedMessage != null
assert assertThatBodyContainsBookNameFoo(receivedMessage.getPayload())
assert receivedMessage.getHeaders().get('BOOK-NAME') == 'foo'
}
}
def 'should trigger a label for the existing artifactId'() {
expect:
await.eventually {
// tag::trigger_artifact[]
stubFinder.trigger('stubs', 'return_book_1')
// end::trigger_artifact[]
Message receivedMessage = receiveFromOutput()
assert receivedMessage != null
assert assertThatBodyContainsBookNameFoo(receivedMessage.getPayload())
assert receivedMessage.getHeaders().get('BOOK-NAME') == 'foo'
}
}
def 'should throw an exception when missing label is passed'() {
when:
stubFinder.trigger('missing label')
then:
thrown(IllegalArgumentException)
}
def 'should throw an exception when missing label is passed'() {
when:
stubFinder.trigger('missing label')
then:
thrown(IllegalArgumentException)
}
def 'should throw an exception when missing label and artifactid is passed'() {
when:
stubFinder.trigger('some:service', 'return_book_1')
then:
thrown(IllegalArgumentException)
}
def 'should throw an exception when missing label and artifactid is passed'() {
when:
stubFinder.trigger('some:service', 'return_book_1')
then:
thrown(IllegalArgumentException)
}
def 'should trigger messages by running all triggers'() {
expect:
await.eventually {
// tag::trigger_all[]
stubFinder.trigger()
// end::trigger_all[]
Message receivedMessage = receiveFromOutput()
assert receivedMessage != null
assert assertThatBodyContainsBookNameFoo(receivedMessage.getPayload())
assert receivedMessage.getHeaders().get('BOOK-NAME') == 'foo'
}
}
def 'should trigger messages by running all triggers'() {
expect:
await.eventually {
// tag::trigger_all[]
stubFinder.trigger()
// end::trigger_all[]
Message receivedMessage = receiveFromOutput()
assert receivedMessage != null
assert assertThatBodyContainsBookNameFoo(receivedMessage.getPayload())
assert receivedMessage.getHeaders().get('BOOK-NAME') == 'foo'
}
}
def 'should trigger a label with no output message'() {
when:
// tag::trigger_no_output[]
Message message = MessageBuilder.createMessage(new BookReturned('foo'), new MessageHeaders([sample: "header",]))
kafkaTemplate.setDefaultTopic('delete')
kafkaTemplate.send(message)
// end::trigger_no_output[]
then:
noExceptionThrown()
}
def 'should trigger a label with no output message'() {
when:
// tag::trigger_no_output[]
Message message = MessageBuilder.createMessage(new BookReturned('foo'), new MessageHeaders([sample: "header",]))
kafkaTemplate.setDefaultTopic('delete')
kafkaTemplate.send(message)
// end::trigger_no_output[]
then:
noExceptionThrown()
}
def 'should not trigger a message that does not match input'() {
when:
Message message = MessageBuilder.createMessage(new BookReturned('notmatching'), new MessageHeaders([wrong: "header",]))
kafkaTemplate.setDefaultTopic('input')
kafkaTemplate.send(message)
then:
Message receivedMessage = receiveFromOutput()
and:
receivedMessage == null
}
def 'should not trigger a message that does not match input'() {
when:
Message message = MessageBuilder.createMessage(new BookReturned('notmatching'), new MessageHeaders([wrong: "header",]))
kafkaTemplate.setDefaultTopic('input')
kafkaTemplate.send(message)
then:
Message receivedMessage = receiveFromOutput()
and:
receivedMessage == null
}
private boolean assertThatBodyContainsBookNameFoo(Object payload) {
return assertThatBodyContainsBookName(payload, 'foo')
}
private boolean assertThatBodyContainsBookNameFoo(Object payload) {
return assertThatBodyContainsBookName(payload, 'foo')
}
private boolean assertThatBodyContainsBookName(Object payload, String expectedValue) {
log.info("Got payload [" + payload + "]")
String objectAsString = payload instanceof String ? payload :
JsonOutput.toJson(payload)
def json = new JsonSlurper().parseText(objectAsString)
return json.bookName == expectedValue
}
private boolean assertThatBodyContainsBookName(Object payload, String expectedValue) {
log.info("Got payload [" + payload + "]")
String objectAsString = payload instanceof String ? payload :
JsonOutput.toJson(payload)
def json = new JsonSlurper().parseText(objectAsString)
return json.bookName == expectedValue
}
@Configuration
@ComponentScan
@EnableAutoConfiguration
@EnableKafka
static class Config {
@Configuration
@ComponentScan
@EnableAutoConfiguration
@EnableKafka
static class Config {
@Bean
DefaultKafkaHeaderMapper headerMapper() {
return new DefaultKafkaHeaderMapper();
}
@Bean
DefaultKafkaHeaderMapper headerMapper() {
return new DefaultKafkaHeaderMapper();
}
}
}
@Commons
@Component
static class MyMessageListener {
@Commons
@Component
static class MyMessageListener {
CountDownLatch latch = new CountDownLatch(1)
CountDownLatch latch = new CountDownLatch(1)
Message output
Message output
@KafkaListener(topics = ["output"])
void output(Message message) {
log.info("I got the message [${message}]")
this.output = message
this.latch.countDown()
}
@KafkaListener(topics = ["output"])
void output(Message message) {
log.info("I got the message [${message}]")
this.output = message
this.latch.countDown()
}
void clear() {
this.output = null
this.latch = new CountDownLatch(1)
}
void clear() {
this.output = null
this.latch = new CountDownLatch(1)
}
Message output() {
this.latch.await(2, TimeUnit.SECONDS)
return this.output
}
}
Message output() {
this.latch.await(2, TimeUnit.SECONDS)
return this.output
}
}
}

View File

@@ -20,22 +20,19 @@ import spock.lang.Specification
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.autoconfigure.EnableAutoConfiguration
import org.springframework.boot.test.context.SpringBootContextLoader
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.cloud.contract.stubrunner.StubFinder
import org.springframework.cloud.contract.stubrunner.spring.AutoConfigureStubRunner
import org.springframework.cloud.contract.stubrunner.spring.StubRunnerProperties
import org.springframework.context.annotation.Configuration
import org.springframework.test.context.ActiveProfiles
import org.springframework.test.context.ContextConfiguration
/**
* @author Marcin Grzejszczak
*/
@ContextConfiguration(classes = MocoConfig, loader = SpringBootContextLoader)
// tag::[classpath_stub_runner]
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE)
@SpringBootTest(classes = MocoConfig, webEnvironment = SpringBootTest.WebEnvironment.NONE)
@AutoConfigureStubRunner(ids = ["com.example:fraudDetectionServerMoco"],
stubsMode = StubRunnerProperties.StubsMode.CLASSPATH)
stubsMode = StubRunnerProperties.StubsMode.CLASSPATH)
// end::[classpath_stub_runner]
@ActiveProfiles("test")
class MocoHttpServerStubSpec extends Specification {

View File

@@ -1,225 +0,0 @@
/*
* 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.messaging.stream
import java.util.concurrent.TimeUnit
import groovy.json.JsonOutput
import groovy.json.JsonSlurper
import spock.lang.Ignore
import spock.lang.Specification
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.autoconfigure.EnableAutoConfiguration
import org.springframework.boot.autoconfigure.ImportAutoConfiguration
import org.springframework.boot.test.context.SpringBootContextLoader
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.cloud.contract.spec.Contract
import org.springframework.cloud.contract.stubrunner.StubFinder
import org.springframework.cloud.contract.stubrunner.spring.AutoConfigureStubRunner
import org.springframework.cloud.contract.verifier.messaging.MessageVerifier
import org.springframework.cloud.contract.verifier.messaging.boot.AutoConfigureMessageVerifier
import org.springframework.cloud.stream.binder.test.TestChannelBinderConfiguration
import org.springframework.context.annotation.Configuration
import org.springframework.messaging.Message
import org.springframework.test.context.ContextConfiguration
/**
* @author Marcin Grzejszczak
*/
@ContextConfiguration(classes = Config, loader = SpringBootContextLoader)
@SpringBootTest(properties = "debug=true")
@AutoConfigureStubRunner
@AutoConfigureMessageVerifier
//@IgnoreIf({ os.windows })
@Ignore("Wait until the feature of runtime message sending and polling is available")
class StreamStubRunnerSpec extends Specification {
@Autowired
StubFinder stubFinder
@Autowired
MessageVerifier<Message<?>> messaging
def 'should download the stub and register a route for it'() {
when:
// tag::client_send[]
messaging.send(new BookReturned('foo'), [sample: 'header'], 'bookStorage')
// end::client_send[]
then:
// tag::client_receive[]
Message<?> receivedMessage = messaging.receive('returnBook')
// end::client_receive[]
and:
// tag::client_receive_message[]
receivedMessage != null
assertJsons(receivedMessage.payload)
receivedMessage.headers.get('BOOK-NAME') == 'foo'
// end::client_receive_message[]
}
def 'should trigger a message by label'() {
when:
// tag::client_trigger[]
stubFinder.trigger('return_book_1')
// end::client_trigger[]
then:
// tag::client_trigger_receive[]
Message<?> receivedMessage = messaging.receive('returnBook')
// end::client_trigger_receive[]
and:
// tag::client_trigger_message[]
receivedMessage != null
assertJsons(receivedMessage.payload)
receivedMessage.headers.get('BOOK-NAME') == 'foo'
// end::client_trigger_message[]
}
def 'should trigger a label for the existing groupId:artifactId'() {
when:
// tag::trigger_group_artifact[]
stubFinder.trigger('org.springframework.cloud.contract.verifier.stubs:streamService', 'return_book_1')
// end::trigger_group_artifact[]
then:
Message<?> receivedMessage = messaging.receive('returnBook')
and:
receivedMessage != null
assertJsons(receivedMessage.payload)
receivedMessage.headers.get('BOOK-NAME') == 'foo'
}
def 'should trigger a label for the existing artifactId'() {
when:
// tag::trigger_artifact[]
stubFinder.trigger('streamService', 'return_book_1')
// end::trigger_artifact[]
then:
Message<?> receivedMessage = messaging.receive('returnBook')
and:
receivedMessage != null
assertJsons(receivedMessage.payload)
receivedMessage.headers.get('BOOK-NAME') == 'foo'
}
def 'should throw exception when missing label is passed'() {
when:
stubFinder.trigger('missing label')
then:
thrown(IllegalArgumentException)
}
def 'should throw exception when missing label and artifactid is passed'() {
when:
stubFinder.trigger('some:service', 'return_book_1')
then:
thrown(IllegalArgumentException)
}
def 'should trigger messages by running all triggers'() {
when:
// tag::trigger_all[]
stubFinder.trigger()
// end::trigger_all[]
then:
Message<?> receivedMessage = messaging.receive('returnBook')
and:
receivedMessage != null
assertJsons(receivedMessage.payload)
receivedMessage.headers.get('BOOK-NAME') == 'foo'
}
def 'should trigger a label with no output message'() {
when:
// tag::trigger_no_output[]
messaging.send(new BookReturned('foo'), [sample: 'header'], 'delete')
// end::trigger_no_output[]
then:
noExceptionThrown()
}
def 'should not trigger a message that does not match input'() {
when:
messaging.send(new BookReturned('not_matching'), [wrong: 'header_value'], 'bookStorage')
then:
Message<?> receivedMessage = messaging.receive('returnBook', 100, TimeUnit.MILLISECONDS)
and:
receivedMessage == null
}
private boolean assertJsons(Object payload) {
String objectAsString = payload instanceof String ? payload :
payload instanceof byte[] ? new String(payload)
: JsonOutput.toJson(payload)
def json = new JsonSlurper().parseText(objectAsString)
return json.bookName == 'foo'
}
Contract dsl =
// tag::sample_dsl[]
Contract.make {
label 'return_book_1'
input { triggeredBy('bookReturnedTriggered()') }
outputMessage {
sentTo('returnBook')
body('''{ "bookName" : "foo" }''')
headers { header('BOOK-NAME', 'foo') }
}
}
// end::sample_dsl[]
Contract dsl2 =
// tag::sample_dsl_2[]
Contract.make {
label 'return_book_2'
input {
messageFrom('bookStorage')
messageBody([
bookName: 'foo'
])
messageHeaders { header('sample', 'header') }
}
outputMessage {
sentTo('returnBook')
body([
bookName: 'foo'
])
headers { header('BOOK-NAME', 'foo') }
}
}
// end::sample_dsl_2[]
Contract dsl3 =
// tag::sample_dsl_3[]
Contract.make {
label 'delete_book'
input {
messageFrom('delete')
messageBody([
bookName: 'foo'
])
messageHeaders { header('sample', 'header') }
assertThat('bookWasDeleted()')
}
}
// end::sample_dsl_3[]
@ImportAutoConfiguration(TestChannelBinderConfiguration.class)
@Configuration
@EnableAutoConfiguration
protected static class Config {
}
}