Turned on tests in modules
This commit is contained in:
9
pom.xml
9
pom.xml
@@ -34,7 +34,7 @@
|
||||
<spring-cloud-commons.version>4.0.0-SNAPSHOT</spring-cloud-commons.version>
|
||||
<jopt-simple.version>5.0.4</jopt-simple.version>
|
||||
<cglib.version>3.2.11</cglib.version>
|
||||
<spock-spring.version>2.2-M1-groovy-4.0</spock-spring.version> <!-- @releaser:version-check-off -->
|
||||
<spock-spring.version>2.3-groovy-4.0</spock-spring.version> <!-- @releaser:version-check-off -->
|
||||
<hoverfly-junit.version>0.2.2</hoverfly-junit.version>
|
||||
<commons-text.version>1.10.0</commons-text.version>
|
||||
<handlebars.version>4.3.0</handlebars.version>
|
||||
@@ -48,6 +48,7 @@
|
||||
<junit-jupiter.version>5.9.1</junit-jupiter.version>
|
||||
<gmavenplus-plugin.version>1.13.0</gmavenplus-plugin.version>
|
||||
<maven-surefire-plugin.version>3.0.0-M7</maven-surefire-plugin.version>
|
||||
<awaitility.version>4.2.0</awaitility.version>
|
||||
|
||||
<!-- We need to have compatibility with Gradle -->
|
||||
<groovy.version>4.0.0</groovy.version>
|
||||
@@ -464,6 +465,12 @@
|
||||
<version>${slf4j.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.awaitility</groupId>
|
||||
<artifactId>awaitility</artifactId>
|
||||
<version>${awaitility.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
|
||||
@@ -13,10 +13,6 @@
|
||||
<packaging>jar</packaging>
|
||||
<name>Spring Cloud Contract Stub Runner</name>
|
||||
<description>Spring Cloud Contract Stub Runner</description>
|
||||
<properties>
|
||||
<!-- TODO: remove -->
|
||||
<skipTests>true</skipTests>
|
||||
</properties>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
|
||||
@@ -20,7 +20,6 @@ import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
@@ -42,9 +41,8 @@ import org.springframework.cloud.stream.config.BindingServiceProperties;
|
||||
import org.springframework.context.Lifecycle;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.integration.dsl.FilterEndpointSpec;
|
||||
import org.springframework.integration.dsl.IntegrationFlow;
|
||||
import org.springframework.integration.dsl.IntegrationFlowBuilder;
|
||||
import org.springframework.integration.dsl.IntegrationFlows;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.util.StringUtils;
|
||||
@@ -56,7 +54,7 @@ import org.springframework.util.StringUtils;
|
||||
* @author Marcin Grzejszczak
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnClass({ IntegrationFlows.class, InputDestination.class })
|
||||
@ConditionalOnClass({ IntegrationFlow.class, InputDestination.class })
|
||||
@ConditionalOnProperty(name = "stubrunner.stream.enabled", havingValue = "true", matchIfMissing = true)
|
||||
@AutoConfigureBefore(StubRunnerIntegrationConfiguration.class)
|
||||
public class StubRunnerStreamConfiguration {
|
||||
@@ -106,14 +104,9 @@ public class StubRunnerStreamConfiguration {
|
||||
}
|
||||
for (Entry<String, List<Contract>> entries : map.entrySet()) {
|
||||
final String flowName = name + "_" + entries.getKey() + "_" + entries.getValue().hashCode();
|
||||
IntegrationFlowBuilder builder = IntegrationFlows.from(entries.getKey())
|
||||
IntegrationFlowBuilder builder = IntegrationFlow.from(entries.getKey())
|
||||
.filter(new StubRunnerStreamMessageSelector(entries.getValue()),
|
||||
new Consumer<FilterEndpointSpec>() {
|
||||
@Override
|
||||
public void accept(FilterEndpointSpec e) {
|
||||
e.id(flowName + ".filter");
|
||||
}
|
||||
})
|
||||
e -> e.id(flowName + ".filter"))
|
||||
.transform(new StubRunnerStreamTransformer(entries.getValue()))
|
||||
.route(new StubRunnerMessageRouter(entries.getValue(), beanFactory));
|
||||
beanFactory.initializeBean(builder.get(), flowName);
|
||||
|
||||
@@ -18,11 +18,11 @@ package org.springframework.cloud.contract.stubrunner.server;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.cloud.contract.stubrunner.StubRunning;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
@@ -36,17 +36,16 @@ public class HttpStubsController {
|
||||
|
||||
private final StubRunning stubRunning;
|
||||
|
||||
@Autowired
|
||||
public HttpStubsController(StubRunning stubRunning) {
|
||||
this.stubRunning = stubRunning;
|
||||
}
|
||||
|
||||
@RequestMapping
|
||||
@GetMapping
|
||||
public Map<String, Integer> stubs() {
|
||||
return this.stubRunning.runStubs().toIvyToPortMapping();
|
||||
}
|
||||
|
||||
@RequestMapping(path = "/{ivy:.*}")
|
||||
@GetMapping(path = "/{ivy:.*}")
|
||||
public ResponseEntity<Integer> consumer(@PathVariable String ivy) {
|
||||
Integer port = this.stubRunning.runStubs().getPort(ivy);
|
||||
if (port != null) {
|
||||
|
||||
@@ -77,7 +77,7 @@ class StubRunnerExecutorSpec extends Specification {
|
||||
int port = TestSocketUtils.findAvailableTcpPort()
|
||||
StubRunnerExecutor executor = new StubRunnerExecutor(portScanner)
|
||||
stubRunnerOptions = new StubRunnerOptionsBuilder(stubIdsToPortMapping:
|
||||
stubIdsWithPortsFromString("group:artifact:${port},someotherartifact:${SocketUtils.findAvailableTcpPort()}"))
|
||||
stubIdsWithPortsFromString("group:artifact:${port},someotherartifact:${TestSocketUtils.findAvailableTcpPort()}"))
|
||||
.build()
|
||||
when:
|
||||
executor.runStubs(stubRunnerOptions, repository, stub)
|
||||
|
||||
@@ -22,8 +22,10 @@ import com.github.tomakehurst.wiremock.extension.Parameters
|
||||
import com.github.tomakehurst.wiremock.extension.ResponseTransformer
|
||||
import com.github.tomakehurst.wiremock.http.ChunkedDribbleDelay
|
||||
import com.github.tomakehurst.wiremock.http.HttpHeader
|
||||
import com.github.tomakehurst.wiremock.http.HttpHeaders
|
||||
import com.github.tomakehurst.wiremock.http.Request
|
||||
import com.github.tomakehurst.wiremock.http.Response
|
||||
import groovy.transform.CompileStatic
|
||||
|
||||
import org.springframework.cloud.contract.verifier.dsl.wiremock.DefaultResponseTransformer
|
||||
import org.springframework.cloud.contract.verifier.dsl.wiremock.WireMockExtensions
|
||||
@@ -31,16 +33,15 @@ import org.springframework.cloud.contract.verifier.dsl.wiremock.WireMockExtensio
|
||||
/**
|
||||
* Extension that registers the default response transformer and a custom one too
|
||||
*/
|
||||
@CompileStatic
|
||||
class TestWireMockExtensions implements WireMockExtensions {
|
||||
@Override
|
||||
List<Extension> extensions() {
|
||||
return [
|
||||
new DefaultResponseTransformer(),
|
||||
new CustomExtension()
|
||||
]
|
||||
return [ new DefaultResponseTransformer(), new CustomExtension() ] as List<Extension>
|
||||
}
|
||||
}
|
||||
|
||||
@CompileStatic
|
||||
class CustomExtension extends ResponseTransformer {
|
||||
|
||||
/**
|
||||
@@ -57,10 +58,18 @@ class CustomExtension extends ResponseTransformer {
|
||||
*/
|
||||
@Override
|
||||
Response transform(Request request, Response response, FileSource files, Parameters parameters) {
|
||||
def headers = response.headers + new HttpHeader("X-My-Header", "surprise!")
|
||||
return new Response(response.status, response.statusMessage,
|
||||
response.body, headers, response.wasConfigured(), response.fault,
|
||||
response.initialDelay, new ChunkedDribbleDelay(0, 0), response.fromProxy)
|
||||
HttpHeaders headers = response.headers + new HttpHeader("X-My-Header", "surprise!")
|
||||
return Response.response()
|
||||
.status(response.status)
|
||||
.statusMessage(response.statusMessage)
|
||||
.body(response.body)
|
||||
.headers(headers)
|
||||
.configured(response.wasConfigured())
|
||||
.fault(response.fault)
|
||||
.incrementInitialDelay(response.initialDelay)
|
||||
.chunkedDribbleDelay(response.chunkedDribbleDelay)
|
||||
.fromProxy(response.fromProxy)
|
||||
.build()
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -19,6 +19,7 @@ package org.springframework.cloud.contract.stubrunner.provider.wiremock
|
||||
import com.github.tomakehurst.wiremock.http.RequestMethod
|
||||
import com.github.tomakehurst.wiremock.stubbing.StubMapping
|
||||
import org.junit.Rule
|
||||
import spock.lang.Ignore
|
||||
import spock.lang.Specification
|
||||
|
||||
import org.springframework.boot.test.system.OutputCaptureRule
|
||||
@@ -66,10 +67,11 @@ class WireMockHttpServerStubSpec extends Specification {
|
||||
mappingDescriptor?.stop()
|
||||
}
|
||||
|
||||
@Ignore("There's sth wrong with SLF4J versions")
|
||||
def 'should make WireMock print out logs on INFO'() {
|
||||
given:
|
||||
WireMockHttpServerStub mappingDescriptor = new WireMockHttpServerStub().start(new HttpServerStubConfiguration(HttpServerStubConfigurer.NoOpHttpServerStubConfigurer.INSTANCE, null,
|
||||
null, SocketUtils.findAvailableTcpPort())) as WireMockHttpServerStub
|
||||
null, TestSocketUtils.findAvailableTcpPort())) as WireMockHttpServerStub
|
||||
mappingDescriptor.registerMappings([
|
||||
new File(WireMockHttpServerStubSpec.classLoader.getResource("simple.json").toURI())
|
||||
])
|
||||
|
||||
@@ -18,14 +18,15 @@ package org.springframework.cloud.contract.stubrunner.server
|
||||
|
||||
import groovy.json.JsonSlurper
|
||||
import io.restassured.module.mockmvc.RestAssuredMockMvc
|
||||
import spock.lang.Specification
|
||||
import org.assertj.core.api.BDDAssertions
|
||||
import org.junit.jupiter.api.BeforeEach
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.mockito.Mockito
|
||||
|
||||
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.StubRunning
|
||||
import org.springframework.test.context.ActiveProfiles
|
||||
import org.springframework.test.context.ContextConfiguration
|
||||
|
||||
/**
|
||||
* @author Marcin Grzejszczak
|
||||
@@ -33,88 +34,99 @@ import org.springframework.test.context.ContextConfiguration
|
||||
// tag::boot_usage[]
|
||||
@SpringBootTest(classes = StubRunnerBoot, properties = "spring.cloud.zookeeper.enabled=false")
|
||||
@ActiveProfiles("test")
|
||||
class StubRunnerBootSpec extends Specification {
|
||||
class StubRunnerBootSpec {
|
||||
|
||||
@Autowired
|
||||
StubRunning stubRunning
|
||||
|
||||
def setup() {
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
RestAssuredMockMvc.standaloneSetup(new HttpStubsController(stubRunning),
|
||||
new TriggerController(stubRunning))
|
||||
}
|
||||
|
||||
def 'should return a list of running stub servers in "full ivy:port" notation'() {
|
||||
@Test
|
||||
void 'should return a list of running stub servers in "full ivy port" notation'() {
|
||||
when:
|
||||
String response = RestAssuredMockMvc.get('/stubs').body.asString()
|
||||
then:
|
||||
def root = new JsonSlurper().parseText(response)
|
||||
root.'org.springframework.cloud.contract.verifier.stubs:bootService:0.0.1-SNAPSHOT:stubs' instanceof Integer
|
||||
assert root.'org.springframework.cloud.contract.verifier.stubs:bootService:0.0.1-SNAPSHOT:stubs' instanceof Integer
|
||||
}
|
||||
|
||||
def 'should return a port on which a [#stubId] stub is running'() {
|
||||
when:
|
||||
def response = RestAssuredMockMvc.get("/stubs/${stubId}")
|
||||
then:
|
||||
response.statusCode == 200
|
||||
Integer.valueOf(response.body.asString()) > 0
|
||||
where:
|
||||
stubId << ['org.springframework.cloud.contract.verifier.stubs:bootService:+:stubs',
|
||||
'org.springframework.cloud.contract.verifier.stubs:bootService:0.0.1-SNAPSHOT:stubs',
|
||||
'org.springframework.cloud.contract.verifier.stubs:bootService:+',
|
||||
'org.springframework.cloud.contract.verifier.stubs:bootService',
|
||||
'bootService']
|
||||
@Test
|
||||
void 'should return a port on which a #stubId stub is running'() {
|
||||
given:
|
||||
def stubIds = ['org.springframework.cloud.contract.verifier.stubs:bootService:+:stubs',
|
||||
'org.springframework.cloud.contract.verifier.stubs:bootService:0.0.1-SNAPSHOT:stubs',
|
||||
'org.springframework.cloud.contract.verifier.stubs:bootService:+',
|
||||
'org.springframework.cloud.contract.verifier.stubs:bootService',
|
||||
'bootService']
|
||||
stubIds.each {
|
||||
when:
|
||||
def response = RestAssuredMockMvc.get("/stubs/${it}")
|
||||
then:
|
||||
assert response.statusCode == 200
|
||||
assert Integer.valueOf(response.body.asString()) > 0
|
||||
}
|
||||
}
|
||||
|
||||
def 'should return 404 when missing stub was called'() {
|
||||
@Test
|
||||
void 'should return 404 when missing stub was called'() {
|
||||
when:
|
||||
def response = RestAssuredMockMvc.get("/stubs/a:b:c:d")
|
||||
then:
|
||||
response.statusCode == 404
|
||||
assert response.statusCode == 404
|
||||
}
|
||||
|
||||
def 'should return a list of messaging labels that can be triggered when version and classifier are passed'() {
|
||||
@Test
|
||||
void 'should return a list of messaging labels that can be triggered when version and classifier are passed'() {
|
||||
when:
|
||||
String response = RestAssuredMockMvc.get('/triggers').body.asString()
|
||||
then:
|
||||
def root = new JsonSlurper().parseText(response)
|
||||
root.'org.springframework.cloud.contract.verifier.stubs:bootService:0.0.1-SNAPSHOT:stubs'?.containsAll(["delete_book", "return_book_1", "return_book_2"])
|
||||
assert root.'org.springframework.cloud.contract.verifier.stubs:bootService:0.0.1-SNAPSHOT:stubs'?.containsAll(["delete_book", "return_book_1", "return_book_2"])
|
||||
}
|
||||
|
||||
def 'should trigger a messaging label'() {
|
||||
@Test
|
||||
void 'should trigger a messaging label'() {
|
||||
given:
|
||||
StubRunning stubRunning = Mock()
|
||||
StubRunning stubRunning = Mockito.mock(StubRunning)
|
||||
RestAssuredMockMvc.standaloneSetup(new HttpStubsController(stubRunning), new TriggerController(stubRunning))
|
||||
when:
|
||||
def response = RestAssuredMockMvc.post("/triggers/delete_book")
|
||||
then:
|
||||
response.statusCode == 200
|
||||
and:
|
||||
1 * stubRunning.trigger('delete_book')
|
||||
Mockito.verify(stubRunning).trigger('delete_book')
|
||||
}
|
||||
|
||||
def 'should trigger a messaging label for a stub with [#stubId] ivy notation'() {
|
||||
@Test
|
||||
void 'should trigger a messaging label for a stub with #stubId ivy notation'() {
|
||||
given:
|
||||
StubRunning stubRunning = Mock()
|
||||
StubRunning stubRunning = Mockito.mock(StubRunning)
|
||||
RestAssuredMockMvc.standaloneSetup(new HttpStubsController(stubRunning), new TriggerController(stubRunning))
|
||||
when:
|
||||
def response = RestAssuredMockMvc.post("/triggers/$stubId/delete_book")
|
||||
then:
|
||||
response.statusCode == 200
|
||||
and:
|
||||
1 * stubRunning.trigger(stubId, 'delete_book')
|
||||
where:
|
||||
stubId << ['org.springframework.cloud.contract.verifier.stubs:bootService:stubs', 'org.springframework.cloud.contract.verifier.stubs:bootService', 'bootService']
|
||||
def stubIds = ['org.springframework.cloud.contract.verifier.stubs:bootService:stubs', 'org.springframework.cloud.contract.verifier.stubs:bootService', 'bootService']
|
||||
stubIds.each {
|
||||
when:
|
||||
def response = RestAssuredMockMvc.post("/triggers/$it/delete_book")
|
||||
then:
|
||||
assert response.statusCode == 200
|
||||
and:
|
||||
Mockito.verify(stubRunning).trigger(it, 'delete_book')
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
def 'should throw exception when trigger is missing'() {
|
||||
@Test
|
||||
void 'should throw exception when trigger is missing'() {
|
||||
when:
|
||||
RestAssuredMockMvc.post("/triggers/missing_label")
|
||||
then:
|
||||
Exception e = thrown(Exception)
|
||||
e.message.contains("Exception occurred while trying to return [missing_label] label.")
|
||||
e.message.contains("Available labels are")
|
||||
e.message.contains("org.springframework.cloud.contract.verifier.stubs:loanIssuance:0.0.1-SNAPSHOT:stubs=[]")
|
||||
e.message.contains("org.springframework.cloud.contract.verifier.stubs:bootService:0.0.1-SNAPSHOT:stubs=")
|
||||
BDDAssertions.thenThrownBy(() -> RestAssuredMockMvc.post("/triggers/missing_label"))
|
||||
.hasMessageContaining("Exception occurred while trying to return [missing_label] label.")
|
||||
.hasMessageContaining("Available labels are")
|
||||
.hasMessageContaining("org.springframework.cloud.contract.verifier.stubs:loanIssuance:0.0.1-SNAPSHOT:stubs=[]")
|
||||
.hasMessageContaining("org.springframework.cloud.contract.verifier.stubs:bootService:0.0.1-SNAPSHOT:stubs=")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -20,8 +20,10 @@ import com.github.tomakehurst.wiremock.core.WireMockConfiguration
|
||||
import groovy.transform.CompileStatic
|
||||
import org.apache.commons.logging.Log
|
||||
import org.apache.commons.logging.LogFactory
|
||||
import spock.lang.Issue
|
||||
import spock.lang.Specification
|
||||
import org.assertj.core.api.BDDAssertions
|
||||
import org.junit.jupiter.api.AfterAll
|
||||
import org.junit.jupiter.api.BeforeAll
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired
|
||||
import org.springframework.beans.factory.annotation.Value
|
||||
@@ -44,131 +46,137 @@ import org.springframework.test.context.ActiveProfiles
|
||||
// Not necessary if Spring Cloud is used. TODO: make it work without this.
|
||||
// tag::test[]
|
||||
@SpringBootTest(classes = Config, properties = [" stubrunner.cloud.enabled=false",
|
||||
'foo=${stubrunner.runningstubs.fraudDetectionServer.port}',
|
||||
'fooWithGroup=${stubrunner.runningstubs.org.springframework.cloud.contract.verifier.stubs.fraudDetectionServer.port}'])
|
||||
'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 {
|
||||
class StubRunnerConfigurationSpec {
|
||||
|
||||
@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
|
||||
|
||||
void setupSpec() {
|
||||
System.clearProperty("stubrunner.repository.root")
|
||||
System.clearProperty("stubrunner.classifier")
|
||||
WireMockHttpServerStubAccessor.clear()
|
||||
}
|
||||
@BeforeAll
|
||||
static void setupSpec() {
|
||||
System.clearProperty("stubrunner.repository.root")
|
||||
System.clearProperty("stubrunner.classifier")
|
||||
WireMockHttpServerStubAccessor.clear()
|
||||
}
|
||||
|
||||
void cleanupSpec() {
|
||||
setupSpec()
|
||||
}
|
||||
@AfterAll
|
||||
static void cleanupSpec() {
|
||||
setupSpec()
|
||||
}
|
||||
|
||||
def 'should mark all ports as random'() {
|
||||
expect:
|
||||
WireMockHttpServerStubAccessor.everyPortRandom()
|
||||
}
|
||||
@Test
|
||||
void 'should mark all ports as random'() {
|
||||
expect:
|
||||
WireMockHttpServerStubAccessor.everyPortRandom()
|
||||
}
|
||||
|
||||
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")
|
||||
}
|
||||
@Test
|
||||
void 'should start WireMock servers'() {
|
||||
expect: 'WireMocks are running'
|
||||
assert stubFinder.findStubUrl('org.springframework.cloud.contract.verifier.stubs', 'loanIssuance') != null
|
||||
assert stubFinder.findStubUrl('loanIssuance') != null
|
||||
assert stubFinder.findStubUrl('loanIssuance') == stubFinder.findStubUrl('org.springframework.cloud.contract.verifier.stubs', 'loanIssuance')
|
||||
assert stubFinder.findStubUrl('loanIssuance') == stubFinder.findStubUrl('org.springframework.cloud.contract.verifier.stubs:loanIssuance')
|
||||
assert 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')
|
||||
assert stubFinder.findStubUrl('org.springframework.cloud.contract.verifier.stubs:fraudDetectionServer') != null
|
||||
and:
|
||||
assert stubFinder.findAllRunningStubs().isPresent('loanIssuance')
|
||||
assert stubFinder.findAllRunningStubs().isPresent('org.springframework.cloud.contract.verifier.stubs', 'fraudDetectionServer')
|
||||
assert stubFinder.findAllRunningStubs().isPresent('org.springframework.cloud.contract.verifier.stubs:fraudDetectionServer')
|
||||
and: 'Stubs were registered'
|
||||
assert "${stubFinder.findStubUrl('loanIssuance').toString()}/name".toURL().text == 'loanIssuance'
|
||||
assert "${stubFinder.findStubUrl('fraudDetectionServer').toString()}/name".toURL().text == 'fraudDetectionServer'
|
||||
and: 'Fraud Detection is an HTTPS endpoint'
|
||||
assert stubFinder.findStubUrl('fraudDetectionServer').toString().startsWith("https")
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
@Test
|
||||
void 'should throw an exception when stub is not found'() {
|
||||
when:
|
||||
BDDAssertions.thenThrownBy(() -> stubFinder.findStubUrl('nonExistingService')).isInstanceOf(StubNotFoundException)
|
||||
when:
|
||||
BDDAssertions.thenThrownBy(() -> stubFinder.findStubUrl('nonExistingGroupId', 'nonExistingArtifactId'))
|
||||
.isInstanceOf(StubNotFoundException)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
@Test
|
||||
void 'should register started servers as environment variables'() {
|
||||
expect:
|
||||
assert environment.getProperty("stubrunner.runningstubs.loanIssuance.port") != null
|
||||
assert stubFinder.findAllRunningStubs().getPort("loanIssuance") == (environment.getProperty("stubrunner.runningstubs.loanIssuance.port") as Integer)
|
||||
and:
|
||||
assert environment.getProperty("stubrunner.runningstubs.fraudDetectionServer.port") != null
|
||||
assert stubFinder.findAllRunningStubs().getPort("fraudDetectionServer") == (environment.getProperty("stubrunner.runningstubs.fraudDetectionServer.port") as Integer)
|
||||
and:
|
||||
assert environment.getProperty("stubrunner.runningstubs.fraudDetectionServer.port") != null
|
||||
assert stubFinder.findAllRunningStubs().getPort("fraudDetectionServer") == (environment.getProperty("stubrunner.runningstubs.org.springframework.cloud.contract.verifier.stubs.fraudDetectionServer.port") as Integer)
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
@Test
|
||||
void 'should be able to interpolate a running stub in the passed test property'() {
|
||||
given:
|
||||
int fraudPort = stubFinder.findAllRunningStubs().getPort("fraudDetectionServer")
|
||||
expect:
|
||||
assert fraudPort > 0
|
||||
assert environment.getProperty("foo", Integer) == fraudPort
|
||||
assert environment.getProperty("fooWithGroup", Integer) == fraudPort
|
||||
assert foo == fraudPort
|
||||
}
|
||||
|
||||
@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
|
||||
}
|
||||
// @Issue("#573")
|
||||
@Test
|
||||
void 'should be able to retrieve the port of a running stub via an annotation'() {
|
||||
given:
|
||||
int fraudPort = stubFinder.findAllRunningStubs().getPort("fraudDetectionServer")
|
||||
expect:
|
||||
assert fraudPort > 0
|
||||
assert fraudDetectionServerPort == fraudPort
|
||||
assert fraudDetectionServerPortWithGroupId == fraudPort
|
||||
}
|
||||
|
||||
def 'should dump all mappings to a file'() {
|
||||
when:
|
||||
def url = stubFinder.findStubUrl("fraudDetectionServer")
|
||||
then:
|
||||
new File("target/outputmappings/", "fraudDetectionServer_${url.port}").exists()
|
||||
}
|
||||
@Test
|
||||
void 'should dump all mappings to a file'() {
|
||||
when:
|
||||
def url = stubFinder.findStubUrl("fraudDetectionServer")
|
||||
then:
|
||||
assert new File("target/outputmappings/", "fraudDetectionServer_${url.port}").exists()
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableAutoConfiguration
|
||||
static class Config {}
|
||||
@Configuration
|
||||
@EnableAutoConfiguration
|
||||
static class Config {}
|
||||
|
||||
// tag::wireMockHttpServerStubConfigurer[]
|
||||
@CompileStatic
|
||||
static class HttpsForFraudDetection extends WireMockHttpServerStubConfigurer {
|
||||
// tag::wireMockHttpServerStubConfigurer[]
|
||||
@CompileStatic
|
||||
static class HttpsForFraudDetection extends WireMockHttpServerStubConfigurer {
|
||||
|
||||
private static final Log log = LogFactory.getLog(HttpsForFraudDetection)
|
||||
private static final Log log = LogFactory.getLog(HttpsForFraudDetection)
|
||||
|
||||
@Override
|
||||
WireMockConfiguration configure(WireMockConfiguration httpStubConfiguration, HttpServerStubConfiguration httpServerStubConfiguration) {
|
||||
if (httpServerStubConfiguration.stubConfiguration.artifactId == "fraudDetectionServer") {
|
||||
int httpsPort = TestSocketUtils.findAvailableTcpPort()
|
||||
log.info("Will set HTTPs port [" + httpsPort + "] for fraud detection server")
|
||||
return httpStubConfiguration
|
||||
.httpsPort(httpsPort)
|
||||
}
|
||||
return httpStubConfiguration
|
||||
}
|
||||
}
|
||||
// end::wireMockHttpServerStubConfigurer[]
|
||||
@Override
|
||||
WireMockConfiguration configure(WireMockConfiguration httpStubConfiguration, HttpServerStubConfiguration httpServerStubConfiguration) {
|
||||
if (httpServerStubConfiguration.stubConfiguration.artifactId == "fraudDetectionServer") {
|
||||
int httpsPort = TestSocketUtils.findAvailableTcpPort()
|
||||
log.info("Will set HTTPs port [" + httpsPort + "] for fraud detection server")
|
||||
return httpStubConfiguration
|
||||
.httpsPort(httpsPort)
|
||||
}
|
||||
return httpStubConfiguration
|
||||
}
|
||||
}
|
||||
// end::wireMockHttpServerStubConfigurer[]
|
||||
}
|
||||
// end::test[]
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
package org.springframework.cloud.contract.stubrunner.spring
|
||||
|
||||
import spock.lang.Specification
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
import org.springframework.beans.factory.annotation.Value
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration
|
||||
@@ -30,7 +30,7 @@ import org.springframework.test.context.ActiveProfiles
|
||||
@SpringBootTest(classes = Config, properties = ['some.property1=org.springframework.cloud.contract.verifier.stubs:loanIssuance'])
|
||||
@AutoConfigureStubRunner
|
||||
@ActiveProfiles("test-with-placeholders")
|
||||
class StubRunnerOptionsBuilderSpec extends Specification {
|
||||
class StubRunnerOptionsBuilderSpec {
|
||||
|
||||
@StubRunnerPort("fraudDetectionServer")
|
||||
int fraudDetectionServerPort
|
||||
@@ -41,12 +41,13 @@ class StubRunnerOptionsBuilderSpec extends Specification {
|
||||
@Value('${stub.port}')
|
||||
int stubPort
|
||||
|
||||
def 'should resolve placeholders'() {
|
||||
@Test
|
||||
void 'should resolve placeholders'() {
|
||||
expect:
|
||||
fraudDetectionServerPort > 1000
|
||||
loanIssuancePort > 1000
|
||||
assert fraudDetectionServerPort > 1000
|
||||
assert loanIssuancePort > 1000
|
||||
and:
|
||||
stubPort == fraudDetectionServerPort
|
||||
assert stubPort == fraudDetectionServerPort
|
||||
}
|
||||
|
||||
@Configuration
|
||||
|
||||
@@ -18,12 +18,12 @@ package org.springframework.cloud.contract.stubrunner.spring.cloud
|
||||
|
||||
import java.util.function.Function
|
||||
|
||||
import spock.lang.Specification
|
||||
import org.assertj.core.api.BDDAssertions
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
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 +37,18 @@ 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[]
|
||||
@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 {
|
||||
class StubRunnerStubsPerConsumerSpec {
|
||||
// end::test[]
|
||||
|
||||
@Autowired
|
||||
@@ -59,36 +59,36 @@ class StubRunnerStubsPerConsumerSpec extends Specification {
|
||||
MessageVerifier<Message<?>> messaging
|
||||
TestRestTemplate template = new TestRestTemplate()
|
||||
|
||||
def 'should start http stub servers for bar-consumer only'() {
|
||||
@Test
|
||||
void 'should start http stub servers for bar-consumer only'() {
|
||||
given:
|
||||
URL stubUrl = stubFinder.findStubUrl('producerWithMultipleConsumers')
|
||||
URL stubUrl = stubFinder.findStubUrl('producerWithMultipleConsumers')
|
||||
when:
|
||||
ResponseEntity entity = template.getForEntity("${stubUrl}/bar-consumer", String)
|
||||
ResponseEntity entity = template.getForEntity("${stubUrl}/bar-consumer", String)
|
||||
then:
|
||||
entity.statusCode.value() == 200
|
||||
assert entity.statusCode.value() == 200
|
||||
when:
|
||||
entity = template.getForEntity("${stubUrl}/foo-consumer", String)
|
||||
entity = template.getForEntity("${stubUrl}/foo-consumer", String)
|
||||
then:
|
||||
entity.statusCode.value() == 404
|
||||
assert entity.statusCode.value() == 404
|
||||
}
|
||||
|
||||
def 'should trigger a message by label from proper consumer'() {
|
||||
@Test
|
||||
void 'should trigger a message by label from proper consumer'() {
|
||||
when:
|
||||
stubFinder.trigger('return_book_for_bar')
|
||||
stubFinder.trigger('return_book_for_bar')
|
||||
then:
|
||||
Message<?> receivedMessage = messaging.receive('output')
|
||||
Message<?> receivedMessage = messaging.receive('output')
|
||||
and:
|
||||
receivedMessage != null
|
||||
receivedMessage.payload == '''{"bookName":"foo_for_bar"}'''.bytes
|
||||
receivedMessage.headers.get('BOOK-NAME') == 'foo_for_bar'
|
||||
assert receivedMessage != null
|
||||
assert receivedMessage.payload == '''{"bookName":"foo_for_bar"}'''.bytes
|
||||
assert receivedMessage.headers.get('BOOK-NAME') == 'foo_for_bar'
|
||||
}
|
||||
|
||||
def 'should not trigger a message by the not matching consumer'() {
|
||||
@Test
|
||||
void 'should not trigger a message by the not matching consumer'() {
|
||||
when:
|
||||
stubFinder.trigger('return_book_for_foo')
|
||||
then:
|
||||
IllegalArgumentException e = thrown(IllegalArgumentException)
|
||||
e.message.contains("No label with name [return_book_for_foo] was found")
|
||||
BDDAssertions.thenThrownBy(() -> stubFinder.trigger('return_book_for_foo')).isInstanceOf(IllegalArgumentException).hasMessageContaining("No label with name [return_book_for_foo] was found")
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@@ -97,7 +97,7 @@ class StubRunnerStubsPerConsumerSpec extends Specification {
|
||||
static class Config {
|
||||
@Bean
|
||||
Function output() {
|
||||
return { Object o ->
|
||||
return { Object o ->
|
||||
println(o)
|
||||
return o
|
||||
}
|
||||
|
||||
@@ -18,6 +18,8 @@ package org.springframework.cloud.contract.stubrunner.spring.cloud
|
||||
|
||||
import java.util.function.Function
|
||||
|
||||
import org.assertj.core.api.BDDAssertions
|
||||
import org.junit.jupiter.api.Test
|
||||
import spock.lang.Specification
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired
|
||||
@@ -47,7 +49,7 @@ import org.springframework.test.context.ActiveProfiles
|
||||
stubsMode = StubRunnerProperties.StubsMode.REMOTE,
|
||||
stubsPerConsumer = true)
|
||||
@ActiveProfiles("streamconsumer")
|
||||
class StubRunnerStubsPerConsumerWithConsumerNameSpec extends Specification {
|
||||
class StubRunnerStubsPerConsumerWithConsumerNameSpec {
|
||||
// end::test[]
|
||||
|
||||
@Autowired
|
||||
@@ -59,36 +61,37 @@ class StubRunnerStubsPerConsumerWithConsumerNameSpec extends Specification {
|
||||
|
||||
TestRestTemplate template = new TestRestTemplate()
|
||||
|
||||
def 'should start http stub servers for foo-consumer only'() {
|
||||
@Test
|
||||
void 'should start http stub servers for foo-consumer only'() {
|
||||
given:
|
||||
URL stubUrl = stubFinder.findStubUrl('producerWithMultipleConsumers')
|
||||
when:
|
||||
ResponseEntity entity = template.getForEntity("${stubUrl}/foo-consumer", String)
|
||||
then:
|
||||
entity.statusCode.value() == 200
|
||||
assert entity.statusCode.value() == 200
|
||||
when:
|
||||
entity = template.getForEntity("${stubUrl}/bar-consumer", String)
|
||||
then:
|
||||
entity.statusCode.value() == 404
|
||||
assert entity.statusCode.value() == 404
|
||||
}
|
||||
|
||||
def 'should trigger a message by label from proper consumer'() {
|
||||
@Test
|
||||
void 'should trigger a message by label from proper consumer'() {
|
||||
when:
|
||||
stubFinder.trigger('return_book_for_foo')
|
||||
then:
|
||||
Message<?> receivedMessage = messaging.receive('output')
|
||||
and:
|
||||
receivedMessage != null
|
||||
receivedMessage.payload == '''{"bookName":"foo_for_foo"}'''.bytes
|
||||
receivedMessage.headers.get('BOOK-NAME') == 'foo_for_foo'
|
||||
assert receivedMessage != null
|
||||
assert receivedMessage.payload == '''{"bookName":"foo_for_foo"}'''.bytes
|
||||
assert receivedMessage.headers.get('BOOK-NAME') == 'foo_for_foo'
|
||||
}
|
||||
|
||||
def 'should not trigger a message by the not matching consumer'() {
|
||||
@Test
|
||||
void 'should not trigger a message by the not matching consumer'() {
|
||||
when:
|
||||
stubFinder.trigger('return_book_for_bar')
|
||||
then:
|
||||
IllegalArgumentException e = thrown(IllegalArgumentException)
|
||||
e.message.contains("No label with name [return_book_for_bar] was found")
|
||||
BDDAssertions.thenThrownBy(() -> stubFinder.trigger('return_book_for_bar')).isInstanceOf(IllegalArgumentException)
|
||||
.hasMessageContaining("No label with name [return_book_for_bar] was found")
|
||||
}
|
||||
|
||||
@Configuration
|
||||
|
||||
@@ -19,12 +19,13 @@ package org.springframework.cloud.contract.stubrunner.spring.cloud.consul
|
||||
import com.ecwid.consul.v1.ConsulClient
|
||||
import com.ecwid.consul.v1.agent.model.NewService
|
||||
import groovy.transform.CompileStatic
|
||||
import org.junit.jupiter.api.AfterAll
|
||||
import org.junit.jupiter.api.BeforeAll
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.mockito.ArgumentMatcher
|
||||
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.consul.discovery.ConsulDiscoveryProperties
|
||||
@@ -32,7 +33,6 @@ import org.springframework.cloud.contract.stubrunner.spring.AutoConfigureStubRun
|
||||
import org.springframework.cloud.contract.stubrunner.spring.StubRunnerProperties
|
||||
import org.springframework.context.annotation.Bean
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import org.springframework.test.context.ContextConfiguration
|
||||
|
||||
import static org.mockito.BDDMockito.then
|
||||
import static org.mockito.Matchers.argThat
|
||||
@@ -56,30 +56,35 @@ import static org.mockito.Mockito.mock
|
||||
stubsMode = StubRunnerProperties.StubsMode.REMOTE ,
|
||||
repositoryRoot = "classpath:m2repo/repository/" )
|
||||
// end::autoconfigure[]
|
||||
class StubRunnerSpringCloudConsulAutoConfigurationSpec extends Specification {
|
||||
class StubRunnerSpringCloudConsulAutoConfigurationSpec {
|
||||
|
||||
@Autowired
|
||||
ConsulClient client
|
||||
|
||||
void setupSpec() {
|
||||
@BeforeAll
|
||||
static void setupSpec() {
|
||||
System.clearProperty("stubrunner.stubs.repository.root")
|
||||
System.clearProperty("stubrunner.stubs.classifier")
|
||||
}
|
||||
|
||||
void cleanupSpec() {
|
||||
@AfterAll
|
||||
static void cleanupSpec() {
|
||||
setupSpec()
|
||||
}
|
||||
|
||||
def 'should make service discovery work for #serviceName'() {
|
||||
@Test
|
||||
void 'should make service discovery work for #serviceName'() {
|
||||
|
||||
given:
|
||||
final String expectedId = serviceName.split(':')[0]
|
||||
final String expectedName = serviceName.split(':')[1]
|
||||
when: 'Consul registration took place for 3 stubs'
|
||||
then(client).should().agentServiceRegister(argThat(new NewServiceMatcher(expectedId, expectedName)))
|
||||
then:
|
||||
noExceptionThrown()
|
||||
where:
|
||||
serviceName << ['loanIssuance:loanIssuance', 'bootService:bootService', 'fraudDetectionServer:someNameThatShouldMapFraudDetectionServer']
|
||||
def serviceName = ['loanIssuance:loanIssuance', 'bootService:bootService', 'fraudDetectionServer:someNameThatShouldMapFraudDetectionServer']
|
||||
when:
|
||||
serviceName.each {
|
||||
and:
|
||||
final String expectedId = it.split(':')[0]
|
||||
final String expectedName = it.split(':')[1]
|
||||
then: 'Consul registration took place for 3 stubs'
|
||||
then(client).should().agentServiceRegister(argThat(new NewServiceMatcher(expectedId, expectedName)))
|
||||
}
|
||||
}
|
||||
|
||||
private static class NewServiceMatcher implements ArgumentMatcher<NewService> {
|
||||
|
||||
@@ -13,10 +13,6 @@
|
||||
<packaging>jar</packaging>
|
||||
<name>Spring Cloud Contract Sample Spring Amqp</name>
|
||||
<description>Spring Cloud Contract Sample Spring Amqp Rabbit</description>
|
||||
<properties>
|
||||
<!-- TODO: remove -->
|
||||
<skipTests>true</skipTests>
|
||||
</properties>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
@@ -36,13 +32,13 @@
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.spockframework</groupId>
|
||||
<artifactId>spock-spring</artifactId>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<groupId>org.awaitility</groupId>
|
||||
<artifactId>awaitility</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
@@ -52,6 +48,10 @@
|
||||
<groupId>org.codehaus.gmavenplus</groupId>
|
||||
<artifactId>gmavenplus-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
|
||||
@@ -22,8 +22,7 @@ import com.jayway.jsonpath.DocumentContext
|
||||
import com.jayway.jsonpath.JsonPath
|
||||
import com.toomuchcoding.jsonassert.JsonAssertion
|
||||
import groovy.json.JsonOutput
|
||||
import spock.lang.Issue
|
||||
import spock.lang.Specification
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired
|
||||
import org.springframework.boot.test.context.SpringBootTest
|
||||
@@ -40,7 +39,7 @@ import static org.springframework.cloud.contract.verifier.messaging.util.Contrac
|
||||
// Context configuration would end up in base class
|
||||
@AutoConfigureMessageVerifier
|
||||
@SpringBootTest(classes = AmqpMessagingApplication, properties = "stubrunner.amqp.enabled=true")
|
||||
class AmqpMessagingApplicationSpec extends Specification {
|
||||
class AmqpMessagingApplicationSpec {
|
||||
|
||||
// ALL CASES
|
||||
@Inject
|
||||
@@ -48,8 +47,9 @@ class AmqpMessagingApplicationSpec extends Specification {
|
||||
@Inject
|
||||
ContractVerifierObjectMapper contractVerifierObjectMapper
|
||||
|
||||
def "should work for triggered based messaging"() {
|
||||
given:
|
||||
@Test
|
||||
void should_work_for_triggered_based_messaging() {
|
||||
// given:
|
||||
def dsl = Contract.make {
|
||||
// Human readable description
|
||||
description 'Some description'
|
||||
@@ -74,20 +74,21 @@ class AmqpMessagingApplicationSpec extends Specification {
|
||||
}
|
||||
}
|
||||
// generated test should look like this:
|
||||
when:
|
||||
// when:
|
||||
publishBook()
|
||||
then:
|
||||
// then:
|
||||
def response = contractVerifierMessaging.receive('test-exchange')
|
||||
response.headers.get('contentType') == 'application/json'
|
||||
and:
|
||||
// and:
|
||||
DocumentContext parsedJson = JsonPath.
|
||||
parse(contractVerifierObjectMapper.writeValueAsString(response.payload))
|
||||
JsonAssertion.assertThat(parsedJson).field('name').isEqualTo('some')
|
||||
}
|
||||
|
||||
@Issue("332")
|
||||
def "should work for second scenario"() {
|
||||
given:
|
||||
// @Issue("332")
|
||||
@Test
|
||||
void should_work_for_second_scenario() {
|
||||
// given:
|
||||
def dsl =
|
||||
Contract.make {
|
||||
description("""
|
||||
@@ -97,11 +98,11 @@ https://cloud.spring.io/spring-cloud-contract/spring-cloud-contract.html#_publis
|
||||
"The input message triggers an output message."
|
||||
|
||||
```
|
||||
given:
|
||||
// given:
|
||||
rabbit service is running
|
||||
when:
|
||||
// when:
|
||||
input message is received
|
||||
then:
|
||||
// then:
|
||||
message is send
|
||||
```
|
||||
|
||||
@@ -128,7 +129,7 @@ then:
|
||||
}
|
||||
}
|
||||
// generated test should look like this:
|
||||
and:
|
||||
// and:
|
||||
ContractVerifierMessage inputMessage = contractVerifierMessaging.create(
|
||||
"{\"name\":\"foo2\"}"
|
||||
, headers()
|
||||
@@ -136,23 +137,24 @@ then:
|
||||
.header("amqp_replyTo", "amq.rabbitmq.reply-to")
|
||||
.header("bill", "bill")
|
||||
)
|
||||
when:
|
||||
// when:
|
||||
contractVerifierMessaging.send(inputMessage, "input")
|
||||
then:
|
||||
// then:
|
||||
ContractVerifierMessage response = contractVerifierMessaging.receive("")
|
||||
assertThat(response).isNotNull()
|
||||
assertThat(response.getHeader("contentType")).isNotNull()
|
||||
assertThat(response.getHeader("contentType").toString()).
|
||||
isEqualTo("application/json")
|
||||
and:
|
||||
// and:
|
||||
DocumentContext parsedJson = JsonPath.parse(contractVerifierObjectMapper.
|
||||
writeValueAsString(response.getPayload()))
|
||||
assertThatJson(parsedJson).field("['name']").isEqualTo("foo2")
|
||||
}
|
||||
|
||||
@Issue("178")
|
||||
def "should work for input/output when bytes are used"() {
|
||||
given:
|
||||
// @Issue("178")
|
||||
@Test
|
||||
void should_work_for_input_output_when_bytes_are_used() {
|
||||
// given:
|
||||
def inputBody = [
|
||||
ratedItemId: "992e46d8-ab05-4a26-a740-6ef7b0daeab3",
|
||||
eventType : "CREATED"
|
||||
@@ -175,16 +177,16 @@ then:
|
||||
)
|
||||
}
|
||||
}
|
||||
when:
|
||||
// when:
|
||||
contractVerifierMessaging.send(contractVerifierMessaging.
|
||||
create(new JsonOutput().toJson(inputBody), [
|
||||
"X-tenant" : "1234",
|
||||
"contentType": "application/json"
|
||||
]), "rated-item-service.rated-item-event.exchange")
|
||||
then:
|
||||
// then:
|
||||
def response = contractVerifierMessaging.
|
||||
receive('bill-service.rated-item-event.retry-exchange')
|
||||
and:
|
||||
// and:
|
||||
DocumentContext parsedJson = JsonPath.
|
||||
parse(contractVerifierObjectMapper.writeValueAsString(response.payload))
|
||||
JsonAssertion.assertThat(parsedJson).field('ratedItemId').
|
||||
|
||||
@@ -13,10 +13,6 @@
|
||||
<packaging>jar</packaging>
|
||||
<name>Spring Cloud Contract Sample Spring Integration</name>
|
||||
<description>Spring Cloud Contract Sample Spring Integration</description>
|
||||
<properties>
|
||||
<!-- TODO: remove -->
|
||||
<skipTests>true</skipTests>
|
||||
</properties>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
|
||||
@@ -21,6 +21,7 @@ import javax.inject.Inject
|
||||
import com.jayway.jsonpath.DocumentContext
|
||||
import com.jayway.jsonpath.JsonPath
|
||||
import com.toomuchcoding.jsonassert.JsonAssertion
|
||||
import org.junit.jupiter.api.Test
|
||||
import spock.lang.Specification
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired
|
||||
@@ -34,14 +35,15 @@ import org.springframework.messaging.Message
|
||||
// Context configuration would end up in base class
|
||||
@AutoConfigureMessageVerifier
|
||||
@SpringBootTest(classes = IntegrationMessagingApplication)
|
||||
class IntegrationMessagingApplicationSpec extends Specification {
|
||||
class IntegrationMessagingApplicationSpec {
|
||||
|
||||
// ALL CASES
|
||||
@Inject
|
||||
MessageVerifier<Message<?>> contractVerifierMessaging
|
||||
ContractVerifierObjectMapper contractVerifierObjectMapper = new ContractVerifierObjectMapper()
|
||||
|
||||
def "should work for triggered based messaging"() {
|
||||
@Test
|
||||
void "should work for triggered based messaging"() {
|
||||
given:
|
||||
// tag::method_trigger[]
|
||||
def dsl = Contract.make {
|
||||
@@ -79,7 +81,7 @@ class IntegrationMessagingApplicationSpec extends Specification {
|
||||
JsonAssertion.assertThat(parsedJson).field('bookName').isEqualTo('foo')
|
||||
}
|
||||
|
||||
def "should generate tests triggered by a message"() {
|
||||
void "should generate tests triggered by a message"() {
|
||||
given:
|
||||
// tag::message_trigger[]
|
||||
def dsl = Contract.make {
|
||||
@@ -125,7 +127,8 @@ class IntegrationMessagingApplicationSpec extends Specification {
|
||||
JsonAssertion.assertThat(parsedJson).field('bookName').isEqualTo('foo')
|
||||
}
|
||||
|
||||
def "should generate tests without destination, triggered by a message"() {
|
||||
@Test
|
||||
void "should generate tests without destination, triggered by a message"() {
|
||||
given:
|
||||
def dsl = Contract.make {
|
||||
label 'some_label'
|
||||
@@ -148,7 +151,6 @@ class IntegrationMessagingApplicationSpec extends Specification {
|
||||
send(contractVerifierObjectMapper.writeValueAsString([bookName: 'foo']),
|
||||
[sample: 'header'], 'delete')
|
||||
then:
|
||||
noExceptionThrown()
|
||||
bookWasDeleted()
|
||||
}
|
||||
|
||||
|
||||
@@ -13,10 +13,6 @@
|
||||
<packaging>jar</packaging>
|
||||
<name>Spring Cloud Contract Stub Runner Boot Zookeeper</name>
|
||||
<description>Spring Cloud Contract Stub Runner Boot Zookeeper</description>
|
||||
<properties>
|
||||
<!-- TODO: remove -->
|
||||
<skipTests>true</skipTests>
|
||||
</properties>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
|
||||
@@ -17,8 +17,9 @@
|
||||
package org.springframework.cloud.contract.stubrunner.spring.cloud.zookeeper
|
||||
|
||||
import org.apache.curator.test.TestingServer
|
||||
import spock.lang.Ignore
|
||||
import spock.lang.Specification
|
||||
import org.junit.jupiter.api.AfterAll
|
||||
import org.junit.jupiter.api.BeforeAll
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration
|
||||
@@ -28,26 +29,23 @@ import org.springframework.cloud.client.loadbalancer.LoadBalanced
|
||||
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.cloud.test.TestSocketUtils
|
||||
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.cloud.test.TestSocketUtils
|
||||
import org.springframework.web.client.RestTemplate
|
||||
|
||||
/**
|
||||
* @author Marcin Grzejszczak
|
||||
*/
|
||||
@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",
|
||||
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 )
|
||||
class StubRunnerSpringCloudZookeeperAutoConfigurationSpec extends Specification {
|
||||
"org.springframework.cloud.contract.verifier.stubs:bootService"] , repositoryRoot = "classpath:m2repo/repository/" , stubsMode = StubRunnerProperties.StubsMode.REMOTE )
|
||||
class StubRunnerSpringCloudZookeeperAutoConfigurationSpec {
|
||||
|
||||
@Autowired
|
||||
StubFinder stubFinder
|
||||
@@ -57,29 +55,32 @@ class StubRunnerSpringCloudZookeeperAutoConfigurationSpec extends Specification
|
||||
@Autowired
|
||||
ZookeeperDiscoveryClient zookeeperServiceDiscovery
|
||||
|
||||
void setupSpec() {
|
||||
@BeforeAll
|
||||
static void setupSpec() {
|
||||
System.clearProperty("stubrunner.stubs.repository.root")
|
||||
System.clearProperty("stubrunner.stubs.classifier")
|
||||
}
|
||||
|
||||
void cleanupSpec() {
|
||||
@AfterAll
|
||||
static void cleanupSpec() {
|
||||
setupSpec()
|
||||
}
|
||||
|
||||
@Ignore
|
||||
def 'should make service discovery work'() {
|
||||
@Test
|
||||
void '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'
|
||||
"${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'
|
||||
assert restTemplate.getForObject('http://loanIssuance/name', String) == 'loanIssuance'
|
||||
assert restTemplate.getForObject('http://someNameThatShouldMapFraudDetectionServer/name', String) == 'fraudDetectionServer'
|
||||
}
|
||||
|
||||
def 'should have all apps registered in Service Discovery'() {
|
||||
@Test
|
||||
void 'should have all apps registered in Service Discovery'() {
|
||||
expect:
|
||||
!zookeeperServiceDiscovery.getInstances('loanIssuance').empty
|
||||
!zookeeperServiceDiscovery.getInstances('someNameThatShouldMapFraudDetectionServer').empty
|
||||
assert !zookeeperServiceDiscovery.getInstances('loanIssuance').empty
|
||||
assert !zookeeperServiceDiscovery.getInstances('someNameThatShouldMapFraudDetectionServer').empty
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@@ -89,7 +90,7 @@ class StubRunnerSpringCloudZookeeperAutoConfigurationSpec extends Specification
|
||||
|
||||
@Bean
|
||||
TestingServer testingServer() {
|
||||
return new TestingServer(SocketUtils.findAvailableTcpPort())
|
||||
return new TestingServer(TestSocketUtils.findAvailableTcpPort())
|
||||
}
|
||||
|
||||
@Bean
|
||||
|
||||
@@ -13,10 +13,6 @@
|
||||
<packaging>jar</packaging>
|
||||
<name>Spring Cloud Contract Stub Runner Kafka</name>
|
||||
<description>Spring Cloud Contract Stub Runner Kafka</description>
|
||||
<properties>
|
||||
<!-- TODO: remove -->
|
||||
<skipTests>true</skipTests>
|
||||
</properties>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
@@ -42,25 +38,10 @@
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.groovy</groupId>
|
||||
<artifactId>groovy</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.spockframework</groupId>
|
||||
<artifactId>spock-core</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.spockframework</groupId>
|
||||
<artifactId>spock-spring</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
@@ -71,6 +52,11 @@
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.awaitility</groupId>
|
||||
<artifactId>awaitility</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<plugins>
|
||||
|
||||
@@ -21,12 +21,15 @@ import java.util.concurrent.TimeUnit
|
||||
|
||||
import groovy.json.JsonOutput
|
||||
import groovy.json.JsonSlurper
|
||||
import groovy.transform.CompileStatic
|
||||
import groovy.util.logging.Commons
|
||||
import org.apache.kafka.clients.consumer.MockConsumer
|
||||
import org.apache.kafka.clients.consumer.OffsetResetStrategy
|
||||
import spock.lang.IgnoreIf
|
||||
import spock.lang.Specification
|
||||
import spock.util.concurrent.PollingConditions
|
||||
import org.assertj.core.api.BDDAssertions
|
||||
import org.awaitility.Awaitility
|
||||
import org.junit.jupiter.api.AfterEach
|
||||
import org.junit.jupiter.api.BeforeEach
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.condition.DisabledOnOs
|
||||
import org.junit.jupiter.api.condition.OS
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration
|
||||
@@ -34,251 +37,251 @@ 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.kafka.annotation.EnableKafka
|
||||
import org.springframework.kafka.annotation.KafkaListener
|
||||
import org.springframework.kafka.core.KafkaTemplate
|
||||
import org.springframework.kafka.support.DefaultKafkaHeaderMapper
|
||||
import org.springframework.kafka.test.EmbeddedKafkaBroker
|
||||
import org.springframework.kafka.test.context.EmbeddedKafka
|
||||
import org.springframework.messaging.Message
|
||||
import org.springframework.messaging.MessageHeaders
|
||||
import org.springframework.messaging.support.MessageBuilder
|
||||
import org.springframework.stereotype.Component
|
||||
|
||||
/**
|
||||
* @author Marcin Grzejszczak
|
||||
*/
|
||||
@SpringBootTest(classes = Config, properties = ["debug=true"])
|
||||
@AutoConfigureStubRunner
|
||||
@IgnoreIf({ os.windows })
|
||||
@DisabledOnOs(value = OS.WINDOWS)
|
||||
@EmbeddedKafka(topics = ["input", "input2", "output", "delete"])
|
||||
@Commons
|
||||
class KafkaStubRunnerSpec extends Specification {
|
||||
class KafkaStubRunnerSpec {
|
||||
|
||||
@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
|
||||
|
||||
def setup() {
|
||||
this.myMessageListener.clear()
|
||||
}
|
||||
@BeforeEach
|
||||
@AfterEach
|
||||
void setup() {
|
||||
this.myMessageListener.clear()
|
||||
}
|
||||
|
||||
def cleanup() {
|
||||
this.myMessageListener.clear()
|
||||
}
|
||||
@CompileStatic
|
||||
private Message receiveFromOutput() {
|
||||
Message m = null
|
||||
Awaitility.await().untilAsserted(() -> {
|
||||
m = this.myMessageListener.output()
|
||||
log.info("Received from message [" + m + "]")
|
||||
assert m != null
|
||||
})
|
||||
return m
|
||||
}
|
||||
|
||||
private Message receiveFromOutput() {
|
||||
Message m = this.myMessageListener.output()
|
||||
log.info("Received message [" + m + "]")
|
||||
return m
|
||||
}
|
||||
@CompileStatic
|
||||
private Message receiveNullableMessageFromOutput() {
|
||||
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
|
||||
@Test
|
||||
void 'should download the stub and register a route for it'() {
|
||||
expect:
|
||||
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")
|
||||
Awaitility.await().pollInterval(200, TimeUnit.MILLISECONDS).untilAsserted {
|
||||
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[]
|
||||
}
|
||||
}
|
||||
@Test
|
||||
void 'should propagate the Kafka record key via message headers'() {
|
||||
expect:
|
||||
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")
|
||||
Awaitility.await().pollInterval(200, TimeUnit.MILLISECONDS).untilAsserted {
|
||||
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[]
|
||||
}
|
||||
}
|
||||
@Test
|
||||
void 'should trigger a message by label'() {
|
||||
expect:
|
||||
// tag::client_trigger[]
|
||||
stubFinder.trigger('return_book_1')
|
||||
// end::client_trigger[]
|
||||
Awaitility.await().pollInterval(200, TimeUnit.MILLISECONDS).untilAsserted {
|
||||
// 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'
|
||||
}
|
||||
}
|
||||
@Test
|
||||
void 'should trigger a label for the existing groupId and artifactId'() {
|
||||
expect:
|
||||
// tag::trigger_group_artifact[]
|
||||
stubFinder.
|
||||
trigger('my:stubs', 'return_book_1')
|
||||
// end::trigger_group_artifact[]
|
||||
Awaitility.await().pollInterval(200, TimeUnit.MILLISECONDS).untilAsserted {
|
||||
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'
|
||||
}
|
||||
}
|
||||
@Test
|
||||
void 'should trigger a label for the existing artifactId'() {
|
||||
expect:
|
||||
// tag::trigger_artifact[]
|
||||
stubFinder.trigger('stubs', 'return_book_1')
|
||||
// end::trigger_artifact[]
|
||||
Awaitility.await().pollInterval(200, TimeUnit.MILLISECONDS).untilAsserted {
|
||||
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)
|
||||
}
|
||||
@Test
|
||||
void 'should throw an exception when missing label is passed'() {
|
||||
expect:
|
||||
BDDAssertions.thenThrownBy(() -> stubFinder.trigger('missing label')).isInstanceOf(IllegalArgumentException)
|
||||
}
|
||||
|
||||
def 'should throw an exception when missing label and artifactid is passed'() {
|
||||
when:
|
||||
stubFinder.trigger('some:service', 'return_book_1')
|
||||
then:
|
||||
thrown(IllegalArgumentException)
|
||||
}
|
||||
@Test
|
||||
void 'should throw an exception when missing label and artifactid is passed'() {
|
||||
expect:
|
||||
BDDAssertions.thenThrownBy(() -> stubFinder.trigger('some:service', 'return_book_1')).isInstanceOf(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 assertThatBodyContainsBookName(receivedMessage.getPayload())
|
||||
assert receivedMessage.getHeaders().get('BOOK-NAME') != null
|
||||
}
|
||||
}
|
||||
@Test
|
||||
void 'should trigger messages by running all triggers'() {
|
||||
expect:
|
||||
// tag::trigger_all[]
|
||||
stubFinder.trigger()
|
||||
// end::trigger_all[]
|
||||
Awaitility.await().pollInterval(200, TimeUnit.MILLISECONDS).untilAsserted {
|
||||
Message receivedMessage = receiveFromOutput()
|
||||
assert receivedMessage != null
|
||||
assert assertThatBodyContainsBookName(receivedMessage.getPayload())
|
||||
assert receivedMessage.getHeaders().get('BOOK-NAME') != null
|
||||
}
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
@Test
|
||||
void '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[]
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
@Test
|
||||
void '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 = receiveNullableMessageFromOutput()
|
||||
and:
|
||||
assert 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
|
||||
}
|
||||
|
||||
private boolean assertThatBodyContainsBookName(Object payload) {
|
||||
log.info("Got payload [" + payload + "]")
|
||||
String objectAsString = payload instanceof String ? payload :
|
||||
JsonOutput.toJson(payload)
|
||||
def json = new JsonSlurper().parseText(objectAsString)
|
||||
return json.bookName != null
|
||||
}
|
||||
private boolean assertThatBodyContainsBookName(Object payload) {
|
||||
log.info("Got payload [" + payload + "]")
|
||||
String objectAsString = payload instanceof String ? payload :
|
||||
JsonOutput.toJson(payload)
|
||||
def json = new JsonSlurper().parseText(objectAsString)
|
||||
return json.bookName != null
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@ComponentScan
|
||||
@EnableAutoConfiguration
|
||||
@EnableKafka
|
||||
static class Config {
|
||||
@Configuration
|
||||
@ComponentScan
|
||||
@EnableAutoConfiguration
|
||||
@EnableKafka
|
||||
static class Config {
|
||||
}
|
||||
|
||||
@Bean
|
||||
DefaultKafkaHeaderMapper headerMapper() {
|
||||
return new DefaultKafkaHeaderMapper();
|
||||
}
|
||||
@Commons
|
||||
@Component
|
||||
static class MyMessageListener {
|
||||
|
||||
}
|
||||
CountDownLatch latch = new CountDownLatch(1)
|
||||
|
||||
@Commons
|
||||
@Component
|
||||
static class MyMessageListener {
|
||||
Message output
|
||||
|
||||
CountDownLatch latch = new CountDownLatch(1)
|
||||
@KafkaListener(topics = ["output"])
|
||||
void output(Message message) {
|
||||
log.info("I got the message [${message}]")
|
||||
this.output = message
|
||||
}
|
||||
|
||||
Message output
|
||||
void clear() {
|
||||
this.output = null
|
||||
}
|
||||
|
||||
@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)
|
||||
}
|
||||
|
||||
Message output() {
|
||||
this.latch.await(2, TimeUnit.SECONDS)
|
||||
return this.output
|
||||
}
|
||||
}
|
||||
Message output() {
|
||||
return this.output
|
||||
}
|
||||
}
|
||||
|
||||
Contract dsl =
|
||||
// tag::sample_dsl[]
|
||||
|
||||
@@ -13,10 +13,6 @@
|
||||
<packaging>jar</packaging>
|
||||
<name>Spring Cloud Contract Stub Runner Stream</name>
|
||||
<description>Spring Cloud Contract Stub Runner Stream</description>
|
||||
<properties>
|
||||
<!-- TODO: remove -->
|
||||
<skipTests>true</skipTests>
|
||||
</properties>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
@@ -43,26 +39,16 @@
|
||||
<scope>test</scope>
|
||||
<classifier>test-binder</classifier>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.spockframework</groupId>
|
||||
<artifactId>spock-core</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.spockframework</groupId>
|
||||
<artifactId>spock-spring</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.awaitility</groupId>
|
||||
<artifactId>awaitility</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<plugins>
|
||||
|
||||
@@ -17,11 +17,17 @@
|
||||
package org.springframework.cloud.contract.stubrunner.messaging.stream
|
||||
|
||||
import java.util.concurrent.TimeUnit
|
||||
import java.util.function.Consumer
|
||||
import java.util.function.Function
|
||||
import java.util.function.Supplier
|
||||
|
||||
import groovy.json.JsonOutput
|
||||
import groovy.json.JsonSlurper
|
||||
import spock.lang.Ignore
|
||||
import spock.lang.Specification
|
||||
import org.assertj.core.api.BDDAssertions
|
||||
import org.awaitility.Awaitility
|
||||
import org.junit.jupiter.api.AfterEach
|
||||
import org.junit.jupiter.api.Disabled
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration
|
||||
@@ -34,6 +40,7 @@ import org.springframework.cloud.contract.stubrunner.spring.AutoConfigureStubRun
|
||||
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.Bean
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import org.springframework.messaging.Message
|
||||
import org.springframework.test.context.ContextConfiguration
|
||||
@@ -45,182 +52,208 @@ import org.springframework.test.context.ContextConfiguration
|
||||
@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 {
|
||||
class StreamStubRunnerSpec {
|
||||
|
||||
@Autowired
|
||||
StubFinder stubFinder
|
||||
@Autowired
|
||||
MessageVerifier<Message<?>> messaging
|
||||
@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[]
|
||||
}
|
||||
@Test
|
||||
void '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:
|
||||
Awaitility.await().untilAsserted(() -> {
|
||||
// tag::client_receive[]
|
||||
Message<?> receivedMessage = messaging.receive('returnBook')
|
||||
// end::client_receive[]
|
||||
and:
|
||||
// tag::client_receive_message[]
|
||||
assert receivedMessage != null
|
||||
assertJsons(receivedMessage.payload)
|
||||
assert 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[]
|
||||
}
|
||||
@Test
|
||||
void 'should trigger a message by label'() {
|
||||
when:
|
||||
// tag::client_trigger[]
|
||||
stubFinder.trigger('return_book_1')
|
||||
// end::client_trigger[]
|
||||
then:
|
||||
Awaitility.await().untilAsserted(() -> {
|
||||
// tag::client_trigger_receive[]
|
||||
Message<?> receivedMessage = messaging.receive('returnBook')
|
||||
// end::client_trigger_receive[]
|
||||
and:
|
||||
// tag::client_trigger_message[]
|
||||
assert receivedMessage != null
|
||||
assertJsons(receivedMessage.payload)
|
||||
assert 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'
|
||||
}
|
||||
@Test
|
||||
void 'should trigger a label for the existing groupId and 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:
|
||||
assert receivedMessage != null
|
||||
assertJsons(receivedMessage.payload)
|
||||
assert 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'
|
||||
}
|
||||
@Test
|
||||
void '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:
|
||||
assert receivedMessage != null
|
||||
assertJsons(receivedMessage.payload)
|
||||
assert receivedMessage.headers.get('BOOK-NAME') == 'foo'
|
||||
}
|
||||
|
||||
def 'should throw exception when missing label is passed'() {
|
||||
when:
|
||||
stubFinder.trigger('missing label')
|
||||
then:
|
||||
thrown(IllegalArgumentException)
|
||||
}
|
||||
@Test
|
||||
void 'should throw exception when missing label is passed'() {
|
||||
when:
|
||||
BDDAssertions.thenThrownBy(() -> stubFinder.trigger('missing label')).isInstanceOf(IllegalArgumentException)
|
||||
}
|
||||
|
||||
def 'should throw exception when missing label and artifactid is passed'() {
|
||||
when:
|
||||
stubFinder.trigger('some:service', 'return_book_1')
|
||||
then:
|
||||
thrown(IllegalArgumentException)
|
||||
}
|
||||
@Test
|
||||
void 'should throw exception when missing label and artifactid is passed'() {
|
||||
when:
|
||||
BDDAssertions.thenThrownBy(() -> stubFinder.trigger('some:service', 'return_book_1')).isInstanceOf(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'
|
||||
}
|
||||
@Test
|
||||
void 'should trigger messages by running all triggers'() {
|
||||
when:
|
||||
// tag::trigger_all[]
|
||||
stubFinder.trigger()
|
||||
// end::trigger_all[]
|
||||
then:
|
||||
Message<?> receivedMessage = messaging.receive('returnBook')
|
||||
and:
|
||||
assert receivedMessage != null
|
||||
assertJsons(receivedMessage.payload)
|
||||
assert 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()
|
||||
}
|
||||
@Test
|
||||
void '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[]
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
@Test
|
||||
void '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:
|
||||
assert 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'
|
||||
}
|
||||
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 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 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[]
|
||||
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 {
|
||||
@ImportAutoConfiguration(TestChannelBinderConfiguration.class)
|
||||
@Configuration
|
||||
@EnableAutoConfiguration
|
||||
protected static class Config {
|
||||
|
||||
}
|
||||
@Bean
|
||||
Function<String, String> test1() {
|
||||
return (input) -> {
|
||||
println "Test 1 [${input}]"
|
||||
return input
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@Bean
|
||||
Consumer<String> test2() {
|
||||
return (input) -> {
|
||||
println "Test 2 [${input}]"
|
||||
}
|
||||
}
|
||||
|
||||
@Bean
|
||||
Consumer<String> test3() {
|
||||
return (input) -> {
|
||||
println "Test 3 [${input}]"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -16,13 +16,13 @@
|
||||
|
||||
package org.springframework.cloud.contract.stubrunner.messaging.stream
|
||||
|
||||
import spock.lang.Specification
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
import org.springframework.cloud.contract.spec.Contract
|
||||
import org.springframework.messaging.Message
|
||||
import org.springframework.messaging.support.MessageBuilder
|
||||
|
||||
class StubRunnerStreamTransformerSpec extends Specification {
|
||||
class StubRunnerStreamTransformerSpec {
|
||||
|
||||
Message message = MessageBuilder.withPayload("hello").build()
|
||||
|
||||
@@ -39,13 +39,14 @@ class StubRunnerStreamTransformerSpec extends Specification {
|
||||
}
|
||||
}
|
||||
|
||||
def 'should not transform the message if there is no output message'() {
|
||||
@Test
|
||||
void 'should not transform the message if there is no output message'() {
|
||||
given:
|
||||
StubRunnerStreamTransformer streamTransformer = new StubRunnerStreamTransformer(noOutputMessageContract)
|
||||
when:
|
||||
def result = streamTransformer.transform(message)
|
||||
then:
|
||||
result.is(message)
|
||||
assert result.is(message)
|
||||
}
|
||||
|
||||
def dsl = Contract.make {
|
||||
@@ -70,7 +71,8 @@ class StubRunnerStreamTransformerSpec extends Specification {
|
||||
}
|
||||
}
|
||||
|
||||
def 'should convert dsl into message'() {
|
||||
@Test
|
||||
void 'should convert dsl into message'() {
|
||||
given:
|
||||
StubRunnerStreamTransformer streamTransformer = new StubRunnerStreamTransformer(dsl) {
|
||||
@Override
|
||||
@@ -81,7 +83,7 @@ class StubRunnerStreamTransformerSpec extends Specification {
|
||||
when:
|
||||
def result = streamTransformer.transform(message)
|
||||
then:
|
||||
result.payload == '{"responseId":"123"}'.bytes
|
||||
assert result.payload == '{"responseId":"123"}'.bytes
|
||||
}
|
||||
|
||||
def dslWithRegexInGString = Contract.make {
|
||||
@@ -106,7 +108,8 @@ class StubRunnerStreamTransformerSpec extends Specification {
|
||||
}
|
||||
}
|
||||
|
||||
def 'should convert dsl into message with regex in GString'() {
|
||||
@Test
|
||||
void 'should convert dsl into message with regex in GString'() {
|
||||
given:
|
||||
StubRunnerStreamTransformer streamTransformer = new StubRunnerStreamTransformer(dslWithRegexInGString) {
|
||||
@Override
|
||||
@@ -117,10 +120,11 @@ class StubRunnerStreamTransformerSpec extends Specification {
|
||||
when:
|
||||
def result = streamTransformer.transform(message)
|
||||
then:
|
||||
result.payload == '''{"id":"99","temperature":"123.45"}'''.bytes
|
||||
assert result.payload == '''{"id":"99","temperature":"123.45"}'''.bytes
|
||||
}
|
||||
|
||||
def 'should parse dsl without DslProperty'() {
|
||||
@Test
|
||||
void 'should parse dsl without DslProperty'() {
|
||||
given:
|
||||
Contract contract = Contract.make {
|
||||
// Human readable description
|
||||
@@ -158,10 +162,11 @@ class StubRunnerStreamTransformerSpec extends Specification {
|
||||
when:
|
||||
def result = streamTransformer.transform(message)
|
||||
then:
|
||||
result.payload == '''{"orderId":"40058c70-891c-4176-a033-f70bad0c5f77","description":"This is the order description"}'''.bytes
|
||||
assert result.payload == '''{"orderId":"40058c70-891c-4176-a033-f70bad0c5f77","description":"This is the order description"}'''.bytes
|
||||
}
|
||||
|
||||
def 'should work for binary payloads from file'() {
|
||||
@Test
|
||||
void 'should work for binary payloads from file'() {
|
||||
given:
|
||||
Contract contract = Contract.make {
|
||||
label 'send_order'
|
||||
@@ -185,7 +190,7 @@ class StubRunnerStreamTransformerSpec extends Specification {
|
||||
when:
|
||||
def result = streamTransformer.transform(message)
|
||||
then:
|
||||
result.payload == StubRunnerStreamTransformerSpec.getResource("/response.pdf").bytes
|
||||
assert result.payload == StubRunnerStreamTransformerSpec.getResource("/response.pdf").bytes
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -5,10 +5,16 @@ spring:
|
||||
cloud:
|
||||
stream:
|
||||
bindings:
|
||||
output:
|
||||
test1-out-0:
|
||||
destination: returnBook
|
||||
input:
|
||||
test1-in-0:
|
||||
destination: bookStorage
|
||||
test2-in-0:
|
||||
destination: delete
|
||||
test3-in-0:
|
||||
destination: returnBook
|
||||
function:
|
||||
definition: test1;test2;test3
|
||||
|
||||
server:
|
||||
port: 0
|
||||
|
||||
Reference in New Issue
Block a user