Re-instate integration tests for stub runner messaging

This commit is contained in:
Dave Syer
2016-07-19 15:56:32 +01:00
parent 4c900f46fe
commit 07befc839f
42 changed files with 2203 additions and 3 deletions

View File

@@ -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
}
}

View File

@@ -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 {}
}

View File

@@ -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"}'''
}
}

View File

@@ -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

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>