Re-instate integration tests for stub runner messaging
This commit is contained in:
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright 2013-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.contract.stubrunner.messaging.stream
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonCreator
|
||||
import groovy.transform.CompileStatic
|
||||
import groovy.transform.EqualsAndHashCode
|
||||
|
||||
@CompileStatic
|
||||
@EqualsAndHashCode
|
||||
class BookReturned implements Serializable {
|
||||
final String bookName
|
||||
|
||||
@JsonCreator(mode = JsonCreator.Mode.PROPERTIES)
|
||||
BookReturned(String bookName) {
|
||||
this.bookName = bookName
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,227 @@
|
||||
/*
|
||||
* Copyright 2013-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.contract.stubrunner.messaging.stream
|
||||
|
||||
import groovy.json.JsonOutput
|
||||
import groovy.json.JsonSlurper
|
||||
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration
|
||||
import org.springframework.boot.test.IntegrationTest;
|
||||
import org.springframework.boot.test.context.SpringBootContextLoader
|
||||
import org.springframework.cloud.contract.spec.Contract
|
||||
import org.springframework.cloud.contract.stubrunner.StubFinder
|
||||
import org.springframework.cloud.contract.stubrunner.spring.AutoConfigureStubRunner
|
||||
import org.springframework.cloud.contract.verifier.messaging.MessageVerifier
|
||||
import org.springframework.cloud.contract.verifier.messaging.boot.AutoConfigureMessageVerifier;
|
||||
import org.springframework.cloud.stream.annotation.EnableBinding
|
||||
import org.springframework.cloud.stream.messaging.Sink
|
||||
import org.springframework.cloud.stream.messaging.Source
|
||||
import org.springframework.context.annotation.ComponentScan
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import org.springframework.messaging.Message
|
||||
import org.springframework.test.context.ContextConfiguration
|
||||
|
||||
import spock.lang.Specification
|
||||
|
||||
/**
|
||||
* @author Marcin Grzejszczak
|
||||
*/
|
||||
@ContextConfiguration(classes = Config, loader = SpringBootContextLoader)
|
||||
@IntegrationTest("debug=true")
|
||||
@AutoConfigureStubRunner
|
||||
@AutoConfigureMessageVerifier
|
||||
class StreamStubRunnerSpec extends Specification {
|
||||
|
||||
@Autowired StubFinder stubFinder
|
||||
@Autowired MessageVerifier<Message<?>> messaging
|
||||
|
||||
def setup() {
|
||||
// ensure that message were taken from the queue
|
||||
messaging.receive('returnBook', 100, TimeUnit.MILLISECONDS)
|
||||
}
|
||||
|
||||
def 'should download the stub and register a route for it'() {
|
||||
when:
|
||||
// tag::client_send[]
|
||||
messaging.send(new BookReturned('foo'), [sample: 'header'], 'bookStorage')
|
||||
// end::client_send[]
|
||||
then:
|
||||
// tag::client_receive[]
|
||||
Message<?> receivedMessage = messaging.receive('returnBook')
|
||||
// end::client_receive[]
|
||||
and:
|
||||
// tag::client_receive_message[]
|
||||
receivedMessage != null
|
||||
assertJsons(receivedMessage.payload)
|
||||
receivedMessage.headers.get('BOOK-NAME') == 'foo'
|
||||
// end::client_receive_message[]
|
||||
}
|
||||
|
||||
def 'should trigger a message by label'() {
|
||||
when:
|
||||
// tag::client_trigger[]
|
||||
stubFinder.trigger('return_book_1')
|
||||
// end::client_trigger[]
|
||||
then:
|
||||
// tag::client_trigger_receive[]
|
||||
Message<?> receivedMessage = messaging.receive('returnBook')
|
||||
// end::client_trigger_receive[]
|
||||
and:
|
||||
// tag::client_trigger_message[]
|
||||
receivedMessage != null
|
||||
assertJsons(receivedMessage.payload)
|
||||
receivedMessage.headers.get('BOOK-NAME') == 'foo'
|
||||
// end::client_trigger_message[]
|
||||
}
|
||||
|
||||
def 'should trigger a label for the existing groupId:artifactId'() {
|
||||
when:
|
||||
// tag::trigger_group_artifact[]
|
||||
stubFinder.trigger('org.springframework.cloud.contract.verifier.stubs:streamService', 'return_book_1')
|
||||
// end::trigger_group_artifact[]
|
||||
then:
|
||||
Message<?> receivedMessage = messaging.receive('returnBook')
|
||||
and:
|
||||
receivedMessage != null
|
||||
assertJsons(receivedMessage.payload)
|
||||
receivedMessage.headers.get('BOOK-NAME') == 'foo'
|
||||
}
|
||||
|
||||
def 'should trigger a label for the existing artifactId'() {
|
||||
when:
|
||||
// tag::trigger_artifact[]
|
||||
stubFinder.trigger('streamService', 'return_book_1')
|
||||
// end::trigger_artifact[]
|
||||
then:
|
||||
Message<?> receivedMessage = messaging.receive('returnBook')
|
||||
and:
|
||||
receivedMessage != null
|
||||
assertJsons(receivedMessage.payload)
|
||||
receivedMessage.headers.get('BOOK-NAME') == 'foo'
|
||||
}
|
||||
|
||||
def 'should throw exception when missing label is passed'() {
|
||||
when:
|
||||
stubFinder.trigger('missing label')
|
||||
then:
|
||||
thrown(IllegalArgumentException)
|
||||
}
|
||||
|
||||
def 'should throw exception when missing label and artifactid is passed'() {
|
||||
when:
|
||||
stubFinder.trigger('some:service', 'return_book_1')
|
||||
then:
|
||||
thrown(IllegalArgumentException)
|
||||
}
|
||||
|
||||
def 'should trigger messages by running all triggers'() {
|
||||
when:
|
||||
// tag::trigger_all[]
|
||||
stubFinder.trigger()
|
||||
// end::trigger_all[]
|
||||
then:
|
||||
Message<?> receivedMessage = messaging.receive('returnBook')
|
||||
and:
|
||||
receivedMessage != null
|
||||
assertJsons(receivedMessage.payload)
|
||||
receivedMessage.headers.get('BOOK-NAME') == 'foo'
|
||||
}
|
||||
|
||||
def 'should trigger a label with no output message'() {
|
||||
when:
|
||||
// tag::trigger_no_output[]
|
||||
messaging.send(new BookReturned('foo'), [sample: 'header'], 'delete')
|
||||
// end::trigger_no_output[]
|
||||
then:
|
||||
noExceptionThrown()
|
||||
}
|
||||
|
||||
def 'should not trigger a message that does not match input'() {
|
||||
when:
|
||||
messaging.send(new BookReturned('not_matching'), [wrong: 'header_value'], 'bookStorage')
|
||||
then:
|
||||
Message<?> receivedMessage = messaging.receive('returnBook', 100, TimeUnit.MILLISECONDS)
|
||||
and:
|
||||
receivedMessage == null
|
||||
}
|
||||
|
||||
private boolean assertJsons(Object payload) {
|
||||
String objectAsString = payload instanceof String ? payload :
|
||||
JsonOutput.toJson(payload)
|
||||
def json = new JsonSlurper().parseText(objectAsString)
|
||||
return json.bookName == 'foo'
|
||||
}
|
||||
|
||||
Contract dsl =
|
||||
// tag::sample_dsl[]
|
||||
Contract.make {
|
||||
label 'return_book_1'
|
||||
input { triggeredBy('bookReturnedTriggered()') }
|
||||
outputMessage {
|
||||
sentTo('returnBook')
|
||||
body('''{ "bookName" : "foo" }''')
|
||||
headers { header('BOOK-NAME', 'foo') }
|
||||
}
|
||||
}
|
||||
// end::sample_dsl[]
|
||||
|
||||
Contract dsl2 =
|
||||
// tag::sample_dsl_2[]
|
||||
Contract.make {
|
||||
label 'return_book_2'
|
||||
input {
|
||||
messageFrom('bookStorage')
|
||||
messageBody([
|
||||
bookName: 'foo'
|
||||
])
|
||||
messageHeaders { header('sample', 'header') }
|
||||
}
|
||||
outputMessage {
|
||||
sentTo('returnBook')
|
||||
body([
|
||||
bookName: 'foo'
|
||||
])
|
||||
headers { header('BOOK-NAME', 'foo') }
|
||||
}
|
||||
}
|
||||
// end::sample_dsl_2[]
|
||||
|
||||
Contract dsl3 =
|
||||
// tag::sample_dsl_3[]
|
||||
Contract.make {
|
||||
label 'delete_book'
|
||||
input {
|
||||
messageFrom('delete')
|
||||
messageBody([
|
||||
bookName: 'foo'
|
||||
])
|
||||
messageHeaders { header('sample', 'header') }
|
||||
assertThat('bookWasDeleted()')
|
||||
}
|
||||
}
|
||||
// end::sample_dsl_3[]
|
||||
|
||||
|
||||
@EnableBinding([Sink, Source])
|
||||
@Configuration
|
||||
@EnableAutoConfiguration
|
||||
protected static class Config {}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
package org.springframework.cloud.contract.stubrunner.messaging.stream
|
||||
|
||||
import org.springframework.cloud.contract.spec.Contract
|
||||
import org.springframework.messaging.Message
|
||||
import org.springframework.messaging.support.MessageBuilder
|
||||
import spock.lang.Specification
|
||||
|
||||
class StubRunnerStreamTransformerSpec extends Specification {
|
||||
|
||||
Message message = MessageBuilder.withPayload("hello").build()
|
||||
|
||||
def noOutputMessageContract = Contract.make {
|
||||
label 'return_book_2'
|
||||
input {
|
||||
messageFrom('bookStorage')
|
||||
messageBody([
|
||||
bookId: $(consumer(regex('[0-9]+')), producer('123'))
|
||||
])
|
||||
messageHeaders {
|
||||
header('sample', 'header')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def '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)
|
||||
}
|
||||
|
||||
def dsl = Contract.make {
|
||||
label 'return_book_2'
|
||||
input {
|
||||
messageFrom('bookStorage')
|
||||
messageBody([
|
||||
bookId: $(consumer(regex('[0-9]+')), producer('123'))
|
||||
])
|
||||
messageHeaders {
|
||||
header('sample', 'header')
|
||||
}
|
||||
}
|
||||
outputMessage {
|
||||
sentTo('returnBook')
|
||||
body([
|
||||
responseId: $(producer(regex('[0-9]+')), consumer('123'))
|
||||
])
|
||||
headers {
|
||||
header('BOOK-NAME', 'foo')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def 'should convert dsl into message'() {
|
||||
given:
|
||||
StubRunnerStreamTransformer streamTransformer = new StubRunnerStreamTransformer(dsl)
|
||||
when:
|
||||
def result = streamTransformer.transform(message)
|
||||
then:
|
||||
result.payload == '{"responseId":"123"}'
|
||||
}
|
||||
|
||||
def dslWithRegexInGString = Contract.make {
|
||||
// Human readable description
|
||||
description 'Should produce valid sensor data'
|
||||
// Label by means of which the output message can be triggered
|
||||
label 'sensor1'
|
||||
// input to the contract
|
||||
input {
|
||||
// the contract will be triggered by a method
|
||||
triggeredBy('createSensorData()')
|
||||
}
|
||||
// output message of the contract
|
||||
outputMessage {
|
||||
// destination to which the output message will be sent
|
||||
sentTo 'sensor-data'
|
||||
headers {
|
||||
header('contentType': 'application/json')
|
||||
}
|
||||
// the body of the output message
|
||||
body("""{"id":"${value(producer(regex('[0-9]+')), consumer('99'))}","temperature":"123.45"}""")
|
||||
}
|
||||
}
|
||||
|
||||
def 'should convert dsl into message with regex in GString'() {
|
||||
given:
|
||||
StubRunnerStreamTransformer streamTransformer = new StubRunnerStreamTransformer(dslWithRegexInGString)
|
||||
when:
|
||||
def result = streamTransformer.transform(message)
|
||||
then:
|
||||
result.payload == '''{"id":"99","temperature":"123.45"}'''
|
||||
}
|
||||
|
||||
def 'should parse dsl without DslProperty'() {
|
||||
given:
|
||||
Contract contract = Contract.make {
|
||||
// Human readable description
|
||||
description 'Sends an order message'
|
||||
// Label by means of which the output message can be triggered
|
||||
label 'send_order'
|
||||
// input to the contract
|
||||
input {
|
||||
// the contract will be triggered by a method
|
||||
triggeredBy('orderTrigger()')
|
||||
}
|
||||
// output message of the contract
|
||||
outputMessage {
|
||||
// destination to which the output message will be sent
|
||||
sentTo('orders')
|
||||
// any headers for the output message
|
||||
headers {
|
||||
header('contentType': 'application/json')
|
||||
}
|
||||
// the body of the output message
|
||||
body(
|
||||
orderId: value(
|
||||
consumer('40058c70-891c-4176-a033-f70bad0c5f77'),
|
||||
producer(regex('([0-9|a-f]*-*)*'))),
|
||||
description: "This is the order description"
|
||||
)
|
||||
}
|
||||
}
|
||||
StubRunnerStreamTransformer streamTransformer = new StubRunnerStreamTransformer(contract)
|
||||
when:
|
||||
def result = streamTransformer.transform(message)
|
||||
then:
|
||||
result.payload == '''{"orderId":"40058c70-891c-4176-a033-f70bad0c5f77","description":"This is the order description"}'''
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
stubrunner.stubs.repositoryRoot: classpath:m2repo/repository/
|
||||
stubrunner.stubs.ids: org.springframework.cloud.contract.verifier.stubs:streamService:0.0.1-SNAPSHOT:stubs
|
||||
|
||||
spring:
|
||||
cloud:
|
||||
stream:
|
||||
bindings:
|
||||
output:
|
||||
destination: returnBook
|
||||
input:
|
||||
destination: bookStorage
|
||||
@@ -0,0 +1,28 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
~ Copyright 2013-2016 the original author or authors.
|
||||
~
|
||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||
~ you may not use this file except in compliance with the License.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<metadata>
|
||||
<groupId>org.springframework.cloud.contract.verifier.stubs</groupId>
|
||||
<artifactId>streamService</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<versioning>
|
||||
<snapshot>
|
||||
<localCopy>true</localCopy>
|
||||
</snapshot>
|
||||
<lastUpdated>20160409062112</lastUpdated>
|
||||
</versioning>
|
||||
</metadata>
|
||||
Binary file not shown.
@@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
~ Copyright 2013-2016 the original author or authors.
|
||||
~
|
||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||
~ you may not use this file except in compliance with the License.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>org.springframework.cloud.contract.verifier.stubs</groupId>
|
||||
<artifactId>streamService</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<packaging>pom</packaging>
|
||||
</project>
|
||||
@@ -0,0 +1,28 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
~ Copyright 2013-2016 the original author or authors.
|
||||
~
|
||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||
~ you may not use this file except in compliance with the License.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<metadata>
|
||||
<groupId>org.springframework.cloud.contract.verifier.stubs</groupId>
|
||||
<artifactId>streamService</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<versioning>
|
||||
<versions>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
</versions>
|
||||
<lastUpdated>20160409062112</lastUpdated>
|
||||
</versioning>
|
||||
</metadata>
|
||||
@@ -0,0 +1,28 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
~ Copyright 2013-2016 the original author or authors.
|
||||
~
|
||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||
~ you may not use this file except in compliance with the License.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<metadata>
|
||||
<groupId>org.springframework.cloud.contract.verifier.stubs</groupId>
|
||||
<artifactId>streamService</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<versioning>
|
||||
<versions>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
</versions>
|
||||
<lastUpdated>20160409062112</lastUpdated>
|
||||
</versioning>
|
||||
</metadata>
|
||||
Reference in New Issue
Block a user