Fixed the docs

This commit is contained in:
Marcin Grzejszczak
2021-07-15 14:14:04 +02:00
parent 47e88db6b4
commit 633d16630a
3 changed files with 514 additions and 5 deletions

View File

@@ -374,7 +374,7 @@ group and artifact ID, as the following example shows:
====
[source,java,indent=0]
----
include::{stubrunner_core_path}/src/test/groovy/org/springframework/cloud/contract/stubrunner/junit/StubRunnerRuleJUnitTest.java[tags=classrule]
include::{stubrunner_core_path}/src/test/groovy/org/springframework/cloud/contract/stubrunner/junit4/StubRunnerRuleJUnitTest.java[tags=classrule]
----
====
@@ -410,13 +410,13 @@ The following examples provide more detail about using Stub Runner:
[source,groovy,indent=0,subs="verbatim,attributes",role="primary"]
.Spock
----
include::{stubrunner_core_path}/src/test/groovy/org/springframework/cloud/contract/stubrunner/junit/StubRunnerRuleSpec.groovy[tags=classrule]
include::{stubrunner_core_path}/src/test/groovy/org/springframework/cloud/contract/stubrunner/junit4/StubRunnerRuleSpec.groovy[tags=classrule]
----
[source,java,indent=0,subs="verbatim,attributes",role="secondary"]
.Junit 4
----
include::{stubrunner_core_path}/src/test/groovy/org/springframework/cloud/contract/stubrunner/junit/StubRunnerRuleJUnitTest.java[tags=test]
include::{stubrunner_core_path}/src/test/groovy/org/springframework/cloud/contract/stubrunner/junit4/StubRunnerRuleJUnitTest.java[tags=test]
----
[source,java,indent=0,subs="verbatim,attributes",role="secondary"]
@@ -456,7 +456,7 @@ and then pass the port for the last downloaded stub. The following example shows
====
[source,java,indent=0]
----
include::{stubrunner_core_path}/src/test/groovy/org/springframework/cloud/contract/stubrunner/junit/StubRunnerRuleCustomPortJUnitTest.java[tags=classrule_with_port]
include::{stubrunner_core_path}/src/test/groovy/org/springframework/cloud/contract/stubrunner/junit4/StubRunnerRuleCustomPortJUnitTest.java[tags=classrule_with_port]
----
====
@@ -465,7 +465,7 @@ For the preceding example, the following test is valid:
====
[source,java,indent=0]
----
include::{stubrunner_core_path}/src/test/groovy/org/springframework/cloud/contract/stubrunner/junit/StubRunnerRuleCustomPortJUnitTest.java[tags=test_with_port]
include::{stubrunner_core_path}/src/test/groovy/org/springframework/cloud/contract/stubrunner/junit4/StubRunnerRuleCustomPortJUnitTest.java[tags=test_with_port]
----
====

View File

@@ -0,0 +1,283 @@
/*
* Copyright 2013-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.contract.stubrunner.messaging.camel
import groovy.json.JsonOutput
import groovy.json.JsonSlurper
import org.apache.activemq.spring.ActiveMQConnectionFactory
import org.apache.camel.CamelContext
import org.apache.camel.ConsumerTemplate
import org.apache.camel.Exchange
import org.apache.camel.ProducerTemplate
import org.apache.camel.component.activemq.ActiveMQComponent
import org.apache.camel.component.jms.JmsConfiguration
import org.apache.camel.impl.engine.DefaultShutdownStrategy
import spock.lang.Ignore
import spock.lang.Specification
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.beans.factory.annotation.Value
import org.springframework.boot.autoconfigure.EnableAutoConfiguration
import org.springframework.boot.test.context.SpringBootContextLoader
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.cloud.contract.spec.Contract
import org.springframework.cloud.contract.stubrunner.StubFinder
import org.springframework.cloud.contract.stubrunner.spring.AutoConfigureStubRunner
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.ComponentScan
import org.springframework.context.annotation.Configuration
import org.springframework.test.annotation.DirtiesContext
import org.springframework.test.context.ContextConfiguration
/**
* @author Marcin Grzejszczak
*/
@ContextConfiguration(classes = Config, loader = SpringBootContextLoader)
@SpringBootTest(properties = "debug=true")
@AutoConfigureStubRunner
@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)
@Ignore
class CamelStubRunnerSpec extends Specification {
//TODO: fix extremely slow test that causes pipeline to fail and then unignore
@Autowired
StubFinder stubFinder
@Autowired
CamelContext camelContext
ConsumerTemplate consumerTemplate
ProducerTemplate producerTemplate
def setup() {
consumerTemplate = camelContext.createConsumerTemplate()
producerTemplate = camelContext.createProducerTemplate()
}
def cleanup() {
// ensure that message were taken from the queue
consumerTemplate.receive('jms:output', 100)
consumerTemplate.receive('jms:input', 100)
this.producerTemplate.stop()
this.consumerTemplate.stop()
def strategy = new DefaultShutdownStrategy(this.camelContext)
strategy.timeout = 1
this.camelContext.shutdownStrategy = strategy
}
def 'should not trigger a message that does not match input'() {
when:
producerTemplate.
sendBodyAndHeaders('jms:input', new BookReturned('notmatching'), [wrong: 'header_value'])
then:
Exchange receivedMessage = consumerTemplate.receive('jms:output', 100)
and:
receivedMessage == null
}
def 'should download the stub and register a route for it'() {
when:
// tag::client_send[]
producerTemplate.
sendBodyAndHeaders('jms:input', new BookReturned('foo'), [sample: 'header'])
// end::client_send[]
then:
// tag::client_receive[]
Exchange receivedMessage = consumerTemplate.receive('jms:output', 5000)
// end::client_receive[]
and:
// tag::client_receive_message[]
receivedMessage != null
assertThatBodyContainsBookNameFoo(receivedMessage.in.body)
receivedMessage.in.headers.get('BOOK-NAME') == 'foo'
// end::client_receive_message[]
}
def 'should trigger a message by label'() {
when:
// tag::client_trigger[]
stubFinder.trigger('return_book_1')
// end::client_trigger[]
then:
// tag::client_trigger_receive[]
Exchange receivedMessage = consumerTemplate.receive('jms:output', 5000)
// end::client_trigger_receive[]
and:
// tag::client_trigger_message[]
receivedMessage != null
assertThatBodyContainsBookNameFoo(receivedMessage.in.body)
receivedMessage.in.headers.get('BOOK-NAME') == 'foo'
// end::client_trigger_message[]
}
def 'should trigger a label for the existing groupId:artifactId'() {
when:
// tag::trigger_group_artifact[]
stubFinder.
trigger('org.springframework.cloud.contract.verifier.stubs:camelService', 'return_book_1')
// end::trigger_group_artifact[]
then:
Exchange receivedMessage = consumerTemplate.receive('jms:output', 5000)
and:
receivedMessage != null
assertThatBodyContainsBookNameFoo(receivedMessage.in.body)
receivedMessage.in.headers.get('BOOK-NAME') == 'foo'
}
def 'should trigger a label for the existing artifactId'() {
when:
// tag::trigger_artifact[]
stubFinder.trigger('camelService', 'return_book_1')
// end::trigger_artifact[]
then:
Exchange receivedMessage = consumerTemplate.receive('jms:output', 5000)
and:
receivedMessage != null
assertThatBodyContainsBookNameFoo(receivedMessage.in.body)
receivedMessage.in.headers.get('BOOK-NAME') == 'foo'
}
def 'should throw an exception when missing label is passed'() {
when:
stubFinder.trigger('missing label')
then:
thrown(IllegalArgumentException)
}
def 'should throw an exception when missing label and artifactid is passed'() {
when:
stubFinder.trigger('some:service', 'return_book_1')
then:
thrown(IllegalArgumentException)
}
def 'should trigger messages by running all triggers'() {
when:
// tag::trigger_all[]
stubFinder.trigger()
// end::trigger_all[]
then:
Exchange receivedMessage = consumerTemplate.receive('jms:output', 5000)
and:
receivedMessage != null
assertThatBodyContainsBookNameFoo(receivedMessage.in.body)
receivedMessage.in.headers.get('BOOK-NAME') == 'foo'
}
def 'should trigger a label with no output message'() {
when:
// tag::trigger_no_output[]
producerTemplate.
sendBodyAndHeaders('jms:delete', new BookReturned('foo'), [sample: 'header'])
// end::trigger_no_output[]
then:
noExceptionThrown()
}
private boolean assertThatBodyContainsBookNameFoo(Object payload) {
String objectAsString = payload instanceof String ? payload :
JsonOutput.toJson(payload)
def json = new JsonSlurper().parseText(objectAsString)
return json.bookName == 'foo'
}
@Configuration
@ComponentScan
@EnableAutoConfiguration
static class Config {
@Bean
ActiveMQConnectionFactory activeMQConnectionFactory(@Value('${activemq.url:vm://localhost?broker.persistent=false}') String url) {
ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory(brokerURL: url)
try {
factory.trustAllPackages = true
}
catch (Throwable e) {
}
return factory
}
@Bean
JmsConfiguration jmsConfiguration(ActiveMQConnectionFactory activeMQConnectionFactory) {
return new JmsConfiguration(connectionFactory: activeMQConnectionFactory)
}
@Bean
ActiveMQComponent activeMQComponent(JmsConfiguration jmsConfiguration) {
return new ActiveMQComponent(configuration: jmsConfiguration)
}
}
Contract dsl =
// tag::sample_dsl[]
Contract.make {
label 'return_book_1'
input {
triggeredBy('bookReturnedTriggered()')
}
outputMessage {
sentTo('jms:output')
body('''{ "bookName" : "foo" }''')
headers {
header('BOOK-NAME', 'foo')
}
}
}
// end::sample_dsl[]
Contract dsl2 =
// tag::sample_dsl_2[]
Contract.make {
label 'return_book_2'
input {
messageFrom('jms:input')
messageBody([
bookName: 'foo'
])
messageHeaders {
header('sample', 'header')
}
}
outputMessage {
sentTo('jms:output')
body([
bookName: 'foo'
])
headers {
header('BOOK-NAME', 'foo')
}
}
}
// end::sample_dsl_2[]
Contract dsl3 =
// tag::sample_dsl_3[]
Contract.make {
label 'delete_book'
input {
messageFrom('jms:delete')
messageBody([
bookName: 'foo'
])
messageHeaders {
header('sample', 'header')
}
assertThat('bookWasDeleted()')
}
}
// end::sample_dsl_3[]
}

View File

@@ -0,0 +1,226 @@
/*
* Copyright 2013-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.contract.stubrunner.messaging.stream
import java.util.concurrent.TimeUnit
import groovy.json.JsonOutput
import groovy.json.JsonSlurper
import spock.lang.Ignore
import spock.lang.Specification
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.autoconfigure.EnableAutoConfiguration
import org.springframework.boot.autoconfigure.ImportAutoConfiguration
import org.springframework.boot.test.context.SpringBootContextLoader
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.cloud.contract.spec.Contract
import org.springframework.cloud.contract.stubrunner.StubFinder
import org.springframework.cloud.contract.stubrunner.spring.AutoConfigureStubRunner
import org.springframework.cloud.contract.verifier.messaging.MessageVerifier
import org.springframework.cloud.contract.verifier.messaging.boot.AutoConfigureMessageVerifier
import org.springframework.cloud.stream.binder.test.TestChannelBinderConfiguration
import org.springframework.context.annotation.Configuration
import org.springframework.messaging.Message
import org.springframework.test.context.ContextConfiguration
/**
* @author Marcin Grzejszczak
*/
@ContextConfiguration(classes = Config, loader = SpringBootContextLoader)
@SpringBootTest(properties = "debug=true")
@AutoConfigureStubRunner
@AutoConfigureMessageVerifier
//@IgnoreIf({ os.windows })
@Ignore("Wait until the feature of runtime message sending and polling is available")
class StreamStubRunnerSpec extends Specification {
@Autowired
StubFinder stubFinder
@Autowired
MessageVerifier<Message<?>> messaging
def 'should download the stub and register a route for it'() {
when:
// tag::client_send[]
messaging.send(new BookReturned('foo'), [sample: 'header'], 'bookStorage')
// end::client_send[]
then:
// tag::client_receive[]
Message<?> receivedMessage = messaging.receive('returnBook')
// end::client_receive[]
and:
// tag::client_receive_message[]
receivedMessage != null
assertJsons(receivedMessage.payload)
receivedMessage.headers.get('BOOK-NAME') == 'foo'
// end::client_receive_message[]
}
def 'should trigger a message by label'() {
when:
// tag::client_trigger[]
stubFinder.trigger('return_book_1')
// end::client_trigger[]
then:
// tag::client_trigger_receive[]
Message<?> receivedMessage = messaging.receive('returnBook')
// end::client_trigger_receive[]
and:
// tag::client_trigger_message[]
receivedMessage != null
assertJsons(receivedMessage.payload)
receivedMessage.headers.get('BOOK-NAME') == 'foo'
// end::client_trigger_message[]
}
def 'should trigger a label for the existing groupId:artifactId'() {
when:
// tag::trigger_group_artifact[]
stubFinder.trigger('org.springframework.cloud.contract.verifier.stubs:streamService', 'return_book_1')
// end::trigger_group_artifact[]
then:
Message<?> receivedMessage = messaging.receive('returnBook')
and:
receivedMessage != null
assertJsons(receivedMessage.payload)
receivedMessage.headers.get('BOOK-NAME') == 'foo'
}
def 'should trigger a label for the existing artifactId'() {
when:
// tag::trigger_artifact[]
stubFinder.trigger('streamService', 'return_book_1')
// end::trigger_artifact[]
then:
Message<?> receivedMessage = messaging.receive('returnBook')
and:
receivedMessage != null
assertJsons(receivedMessage.payload)
receivedMessage.headers.get('BOOK-NAME') == 'foo'
}
def 'should throw exception when missing label is passed'() {
when:
stubFinder.trigger('missing label')
then:
thrown(IllegalArgumentException)
}
def 'should throw exception when missing label and artifactid is passed'() {
when:
stubFinder.trigger('some:service', 'return_book_1')
then:
thrown(IllegalArgumentException)
}
def 'should trigger messages by running all triggers'() {
when:
// tag::trigger_all[]
stubFinder.trigger()
// end::trigger_all[]
then:
Message<?> receivedMessage = messaging.receive('returnBook')
and:
receivedMessage != null
assertJsons(receivedMessage.payload)
receivedMessage.headers.get('BOOK-NAME') == 'foo'
}
def 'should trigger a label with no output message'() {
when:
// tag::trigger_no_output[]
messaging.send(new BookReturned('foo'), [sample: 'header'], 'delete')
// end::trigger_no_output[]
then:
noExceptionThrown()
}
def 'should not trigger a message that does not match input'() {
when:
messaging.send(new BookReturned('not_matching'), [wrong: 'header_value'], 'bookStorage')
then:
Message<?> receivedMessage = messaging.receive('returnBook', 100, TimeUnit.MILLISECONDS)
and:
receivedMessage == null
}
private boolean assertJsons(Object payload) {
String objectAsString = payload instanceof String ? payload :
payload instanceof byte[] ? new String(payload)
: JsonOutput.toJson(payload)
def json = new JsonSlurper().parseText(objectAsString)
return json.bookName == 'foo'
}
Contract dsl =
// tag::sample_dsl[]
Contract.make {
label 'return_book_1'
input { triggeredBy('bookReturnedTriggered()') }
outputMessage {
sentTo('returnBook')
body('''{ "bookName" : "foo" }''')
headers { header('BOOK-NAME', 'foo') }
}
}
// end::sample_dsl[]
Contract dsl2 =
// tag::sample_dsl_2[]
Contract.make {
label 'return_book_2'
input {
messageFrom('bookStorage')
messageBody([
bookName: 'foo'
])
messageHeaders { header('sample', 'header') }
}
outputMessage {
sentTo('returnBook')
body([
bookName: 'foo'
])
headers { header('BOOK-NAME', 'foo') }
}
}
// end::sample_dsl_2[]
Contract dsl3 =
// tag::sample_dsl_3[]
Contract.make {
label 'delete_book'
input {
messageFrom('delete')
messageBody([
bookName: 'foo'
])
messageHeaders { header('sample', 'header') }
assertThat('bookWasDeleted()')
}
}
// end::sample_dsl_3[]
@ImportAutoConfiguration(TestChannelBinderConfiguration.class)
@Configuration
@EnableAutoConfiguration
protected static class Config {
}
}