From 9a8d12133dae2e7f30383f9ac8d667bfee6f50f4 Mon Sep 17 00:00:00 2001 From: Ronald Holshausen Date: Sat, 16 Dec 2023 19:05:46 +1100 Subject: [PATCH] feat: Add tests for supportingmultiple test targets with JUnit 5 #1708 --- .../junit5/PactVerificationContext.kt | 5 +- .../provider/junit5/HttpTestTargetSpec.groovy | 21 +++++ .../junit5/MessageTestTargetSpec.groovy | 21 +++++ .../junit5/PactVerificationContextSpec.groovy | 78 +++++++++++++++++++ .../PactVerificationExtensionSpec.groovy | 50 ++++++++++++ .../junit5/PluginTestTargetSpec.groovy | 21 +++++ .../junit5/CombinedHttpAndMessageTest.java | 65 ++++++++++++++++ .../resources/pacts/v4-combined-pact.json | 61 +++++++++++++++ .../spring/junit5/WebFluxBasedTestTarget.kt | 8 +- .../spring/junit5/WebFluxTargetSpec.groovy | 15 ++++ .../junit5/WebTestClientTargetSpec.groovy | 15 ++++ .../spring/spring6/WebFluxTargetSpec.groovy | 15 ++++ .../spring6/WebTestClientTargetSpec.groovy | 16 ++++ 13 files changed, 388 insertions(+), 3 deletions(-) create mode 100644 provider/junit5/src/test/groovy/au/com/dius/pact/provider/junit5/HttpTestTargetSpec.groovy create mode 100644 provider/junit5/src/test/groovy/au/com/dius/pact/provider/junit5/MessageTestTargetSpec.groovy create mode 100644 provider/junit5/src/test/groovy/au/com/dius/pact/provider/junit5/PluginTestTargetSpec.groovy create mode 100644 provider/junit5/src/test/java/au/com/dius/pact/provider/junit5/CombinedHttpAndMessageTest.java create mode 100644 provider/junit5/src/test/resources/pacts/v4-combined-pact.json diff --git a/provider/junit5/src/main/kotlin/au/com/dius/pact/provider/junit5/PactVerificationContext.kt b/provider/junit5/src/main/kotlin/au/com/dius/pact/provider/junit5/PactVerificationContext.kt index 2653e8995d..4ce8c3be30 100644 --- a/provider/junit5/src/main/kotlin/au/com/dius/pact/provider/junit5/PactVerificationContext.kt +++ b/provider/junit5/src/main/kotlin/au/com/dius/pact/provider/junit5/PactVerificationContext.kt @@ -8,7 +8,9 @@ import au.com.dius.pact.core.model.RequestResponseInteraction import au.com.dius.pact.core.model.UnknownPactSource import au.com.dius.pact.core.model.V4Interaction import au.com.dius.pact.core.model.generators.GeneratorTestMode -import au.com.dius.pact.core.support.* +import au.com.dius.pact.core.support.MetricEvent +import au.com.dius.pact.core.support.Metrics +import au.com.dius.pact.core.support.Result import au.com.dius.pact.core.support.expressions.SystemPropertyResolver import au.com.dius.pact.core.support.expressions.ValueResolver import au.com.dius.pact.provider.IConsumerInfo @@ -20,7 +22,6 @@ import au.com.dius.pact.provider.ProviderVerifier import au.com.dius.pact.provider.VerificationFailureType import au.com.dius.pact.provider.VerificationResult import au.com.dius.pact.provider.junitsupport.TestDescription -import io.pact.plugins.jvm.core.PluginConfiguration import org.junit.jupiter.api.extension.ExtensionContext import kotlin.collections.isNotEmpty diff --git a/provider/junit5/src/test/groovy/au/com/dius/pact/provider/junit5/HttpTestTargetSpec.groovy b/provider/junit5/src/test/groovy/au/com/dius/pact/provider/junit5/HttpTestTargetSpec.groovy new file mode 100644 index 0000000000..b5100c78d4 --- /dev/null +++ b/provider/junit5/src/test/groovy/au/com/dius/pact/provider/junit5/HttpTestTargetSpec.groovy @@ -0,0 +1,21 @@ +package au.com.dius.pact.provider.junit5 + +import au.com.dius.pact.core.model.RequestResponseInteraction +import au.com.dius.pact.core.model.V4Interaction +import au.com.dius.pact.core.model.messaging.Message +import spock.lang.Specification + +class HttpTestTargetSpec extends Specification { + def 'supports any HTTP interaction'() { + expect: + new HttpTestTarget().supportsInteraction(interaction) == result + + where: + interaction | result + new RequestResponseInteraction('test') | true + new Message('test') | false + new V4Interaction.AsynchronousMessage('test') | false + new V4Interaction.SynchronousMessages('test') | false + new V4Interaction.SynchronousHttp('test') | true + } +} diff --git a/provider/junit5/src/test/groovy/au/com/dius/pact/provider/junit5/MessageTestTargetSpec.groovy b/provider/junit5/src/test/groovy/au/com/dius/pact/provider/junit5/MessageTestTargetSpec.groovy new file mode 100644 index 0000000000..026f5c8a89 --- /dev/null +++ b/provider/junit5/src/test/groovy/au/com/dius/pact/provider/junit5/MessageTestTargetSpec.groovy @@ -0,0 +1,21 @@ +package au.com.dius.pact.provider.junit5 + +import au.com.dius.pact.core.model.RequestResponseInteraction +import au.com.dius.pact.core.model.V4Interaction +import au.com.dius.pact.core.model.messaging.Message +import spock.lang.Specification + +class MessageTestTargetSpec extends Specification { + def 'supports any message interaction'() { + expect: + new MessageTestTarget().supportsInteraction(interaction) == result + + where: + interaction | result + new RequestResponseInteraction('test') | false + new Message('test') | true + new V4Interaction.AsynchronousMessage('test') | true + new V4Interaction.SynchronousMessages('test') | true + new V4Interaction.SynchronousHttp('test') | false + } +} diff --git a/provider/junit5/src/test/groovy/au/com/dius/pact/provider/junit5/PactVerificationContextSpec.groovy b/provider/junit5/src/test/groovy/au/com/dius/pact/provider/junit5/PactVerificationContextSpec.groovy index 327bb54417..553cbc6121 100644 --- a/provider/junit5/src/test/groovy/au/com/dius/pact/provider/junit5/PactVerificationContextSpec.groovy +++ b/provider/junit5/src/test/groovy/au/com/dius/pact/provider/junit5/PactVerificationContextSpec.groovy @@ -150,4 +150,82 @@ class PactVerificationContextSpec extends Specification { 1 * verifier.verifyResponseByInvokingProviderMethods(provider, consumer, interaction, interaction.description, [:], true, _) >> new VerificationResult.Ok() } + + def 'currentTarget - returns the current target if it supports the interaction'() { + given: + def expectedTarget = new HttpTestTarget() + ExtensionContext.Store store = Stub() + ExtensionContext extContext = Stub() + IProviderVerifier verifier = Mock() + ValueResolver valueResolver = Stub() + IProviderInfo provider = Stub() + IConsumerInfo consumer = Stub() + Interaction interaction = new RequestResponseInteraction('test') + def pact = new RequestResponsePact(new Provider(), new Consumer(), [interaction]) + List testResults = [] + + def context = new PactVerificationContext(store, extContext, expectedTarget, verifier, valueResolver, + provider, consumer, interaction, pact, testResults) + + when: + def result = context.currentTarget() + + then: + result == expectedTarget + } + + @SuppressWarnings('LineLength') + def 'currentTarget - searches for a target in the additional ones if the current target does not support the interaction'() { + given: + def expectedTarget = new HttpTestTarget() + ExtensionContext.Store store = Stub() + ExtensionContext extContext = Stub() + IProviderVerifier verifier = Mock() + ValueResolver valueResolver = Stub() + IProviderInfo provider = Stub() + IConsumerInfo consumer = Stub() + Interaction interaction = new RequestResponseInteraction('test') + def pact = new RequestResponsePact(new Provider(), new Consumer(), [interaction]) + List testResults = [] + TestTarget otherTarget = Mock { + supportsInteraction(_) >> false + } + + def context = new PactVerificationContext(store, extContext, new MessageTestTarget(), verifier, valueResolver, + provider, consumer, interaction, pact, testResults) + context.addAdditionalTarget(otherTarget) + context.addAdditionalTarget(expectedTarget) + + when: + def result = context.currentTarget() + + then: + result == expectedTarget + } + + def 'currentTarget - returns null if no target can be found that supports the interaction'() { + given: + ExtensionContext.Store store = Stub() + ExtensionContext extContext = Stub() + IProviderVerifier verifier = Mock() + ValueResolver valueResolver = Stub() + IProviderInfo provider = Stub() + IConsumerInfo consumer = Stub() + Interaction interaction = new RequestResponseInteraction('test') + def pact = new RequestResponsePact(new Provider(), new Consumer(), [interaction]) + List testResults = [] + TestTarget otherTarget = Mock { + supportsInteraction(_) >> false + } + + def context = new PactVerificationContext(store, extContext, new MessageTestTarget(), verifier, valueResolver, + provider, consumer, interaction, pact, testResults) + context.addAdditionalTarget(otherTarget) + + when: + def result = context.currentTarget() + + then: + result == null + } } diff --git a/provider/junit5/src/test/groovy/au/com/dius/pact/provider/junit5/PactVerificationExtensionSpec.groovy b/provider/junit5/src/test/groovy/au/com/dius/pact/provider/junit5/PactVerificationExtensionSpec.groovy index 8afe23d5d2..d6271f5901 100644 --- a/provider/junit5/src/test/groovy/au/com/dius/pact/provider/junit5/PactVerificationExtensionSpec.groovy +++ b/provider/junit5/src/test/groovy/au/com/dius/pact/provider/junit5/PactVerificationExtensionSpec.groovy @@ -207,6 +207,41 @@ class PactVerificationExtensionSpec extends Specification { RequestData | new PluginTestTarget() | true } + def 'supports parameter test with mutiple test targets'() { + given: + def interaction = new V4Interaction.SynchronousHttp(null, 'interaction2') + context = new PactVerificationContext(store, extContext, target, Stub(IProviderVerifier), + Stub(ValueResolver), Stub(IProviderInfo), Stub(IConsumerInfo), interaction, pact, []) + context.addAdditionalTarget(new MessageTestTarget()) + + extension = new PactVerificationExtension(pact, pactSource, interaction, 'service', 'consumer', + mockValueResolver) + Parameter parameter = mock(Parameter) + when(parameter.getType()).thenReturn(parameterType) + ParameterContext parameterContext = Stub { + getParameter() >> parameter + } + + expect: + extension.supportsParameter(parameterContext, extContext) == true + + where: + + parameterType | target + Pact | new HttpTestTarget() + Interaction | new HttpTestTarget() + ClassicHttpRequest | new HttpTestTarget() + ClassicHttpRequest | new HttpsTestTarget() + ClassicHttpRequest | new MessageTestTarget() + HttpRequest | new HttpTestTarget() + HttpRequest | new HttpsTestTarget() + HttpRequest | new MessageTestTarget() + PactVerificationContext | new HttpTestTarget() + ProviderVerifier | new HttpTestTarget() + RequestData | new HttpTestTarget() + RequestData | new PluginTestTarget() + } + def 'resolve parameter test'() { given: extension = new PactVerificationExtension(pact, pactSource, interaction1, 'service', 'consumer', @@ -234,4 +269,19 @@ class PactVerificationExtensionSpec extends Specification { String | null RequestData | data } + + def 'beforeTestExecution - throws an exception if there is no valid test target for the interaction'() { + given: + context = new PactVerificationContext(store, extContext, new MessageTestTarget(), Stub(IProviderVerifier), + Stub(ValueResolver), Stub(IProviderInfo), Stub(IConsumerInfo), interaction1, pact, []) + extension = new PactVerificationExtension(pact, pactSource, interaction1, 'service', 'consumer', + mockValueResolver) + + when: + extension.beforeTestExecution(extContext) + + then: + def ex = thrown(UnsupportedOperationException) + ex.message == 'No test target has been configured for RequestResponseInteraction interactions' + } } diff --git a/provider/junit5/src/test/groovy/au/com/dius/pact/provider/junit5/PluginTestTargetSpec.groovy b/provider/junit5/src/test/groovy/au/com/dius/pact/provider/junit5/PluginTestTargetSpec.groovy new file mode 100644 index 0000000000..637d98909e --- /dev/null +++ b/provider/junit5/src/test/groovy/au/com/dius/pact/provider/junit5/PluginTestTargetSpec.groovy @@ -0,0 +1,21 @@ +package au.com.dius.pact.provider.junit5 + +import au.com.dius.pact.core.model.V4Interaction +import spock.lang.Specification +import au.com.dius.pact.core.model.RequestResponseInteraction +import au.com.dius.pact.core.model.messaging.Message + +class PluginTestTargetSpec extends Specification { + def 'supports any V4 interaction'() { + expect: + new PluginTestTarget().supportsInteraction(interaction) == result + + where: + interaction | result + new RequestResponseInteraction('test') | false + new Message('test') | false + new V4Interaction.AsynchronousMessage('test') | true + new V4Interaction.SynchronousMessages('test') | true + new V4Interaction.SynchronousHttp('test') | true + } +} diff --git a/provider/junit5/src/test/java/au/com/dius/pact/provider/junit5/CombinedHttpAndMessageTest.java b/provider/junit5/src/test/java/au/com/dius/pact/provider/junit5/CombinedHttpAndMessageTest.java new file mode 100644 index 0000000000..68859ab724 --- /dev/null +++ b/provider/junit5/src/test/java/au/com/dius/pact/provider/junit5/CombinedHttpAndMessageTest.java @@ -0,0 +1,65 @@ +package au.com.dius.pact.provider.junit5; + +import au.com.dius.pact.provider.MessageAndMetadata; +import au.com.dius.pact.provider.PactVerifyProvider; +import au.com.dius.pact.provider.junitsupport.Provider; +import au.com.dius.pact.provider.junitsupport.State; +import au.com.dius.pact.provider.junitsupport.loader.PactFolder; +import com.github.tomakehurst.wiremock.WireMockServer; +import org.apache.hc.core5.http.HttpRequest; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.TestTemplate; +import org.junit.jupiter.api.extension.ExtendWith; +import ru.lanwen.wiremock.ext.WiremockResolver; +import ru.lanwen.wiremock.ext.WiremockUriResolver; + +import java.net.MalformedURLException; +import java.net.URL; +import java.util.Map; + +import static com.github.tomakehurst.wiremock.client.WireMock.*; +import static java.lang.String.format; + +@Provider("test_provider_combined") +@PactFolder("pacts") +@ExtendWith({ + WiremockResolver.class, + WiremockUriResolver.class +}) +public class CombinedHttpAndMessageTest { + @TestTemplate + @ExtendWith(PactVerificationInvocationContextProvider.class) + void testTemplate(HttpRequest request, PactVerificationContext context) { + if (request != null) { + request.addHeader("X-ContractTest", "true"); + } + + context.verifyInteraction(); + } + + @BeforeEach + void before(PactVerificationContext context, + @WiremockResolver.Wiremock WireMockServer server, + @WiremockUriResolver.WiremockUri String uri) throws MalformedURLException { + context.setTarget(HttpTestTarget.fromUrl(new URL(uri))); + context.addAdditionalTarget(new MessageTestTarget()); + + server.stubFor( + get(urlPathEqualTo("/data")) + .withHeader("X-ContractTest", equalTo("true")) + .willReturn(aResponse() + .withStatus(200) + .withHeader("content-type", "application/json") + .withBody("{}") + ) + ); + } + + @State("message exists") + public void messageExits() { } + + @PactVerifyProvider("Test Message") + public MessageAndMetadata message() { + return new MessageAndMetadata("{\"a\": \"1234-1234\"}".getBytes(), Map.of("destination", "a/b/c")); + } +} diff --git a/provider/junit5/src/test/resources/pacts/v4-combined-pact.json b/provider/junit5/src/test/resources/pacts/v4-combined-pact.json new file mode 100644 index 0000000000..ff323e1346 --- /dev/null +++ b/provider/junit5/src/test/resources/pacts/v4-combined-pact.json @@ -0,0 +1,61 @@ +{ + "provider": { + "name": "test_provider_combined" + }, + "consumer": { + "name": "test_consumer" + }, + "interactions": [ + { + "type": "Synchronous/HTTP", + "key": "001", + "description": "test http interaction", + "request": { + "method": "GET", + "path": "/data" + }, + "response": { + "status": 200, + "body": { + "contentType": "application/json", + "encoded": false, + "content": { + + } + } + } + }, { + "type": "Asynchronous/Messages", + "key": "m_001", + "metadata": { + "contentType": "application/json", + "destination": "a/b/c" + }, + "providerStates": [ + { + "name": "message exists" + } + ], + "contents": { + "contentType": "application/json", + "encoded": false, + "content": { + "a": "1234-1234" + } + }, + "generators": { + "content": { + "a": { + "type": "Uuid" + } + } + }, + "description": "Test Message" + } + ], + "metadata": { + "pactSpecification": { + "version": "4.0" + } + } +} diff --git a/provider/junit5spring/src/main/kotlin/au/com/dius/pact/provider/spring/junit5/WebFluxBasedTestTarget.kt b/provider/junit5spring/src/main/kotlin/au/com/dius/pact/provider/spring/junit5/WebFluxBasedTestTarget.kt index fdc59bbd44..15fb534e91 100644 --- a/provider/junit5spring/src/main/kotlin/au/com/dius/pact/provider/spring/junit5/WebFluxBasedTestTarget.kt +++ b/provider/junit5spring/src/main/kotlin/au/com/dius/pact/provider/spring/junit5/WebFluxBasedTestTarget.kt @@ -1,6 +1,12 @@ package au.com.dius.pact.provider.spring.junit5 -import au.com.dius.pact.core.model.* +import au.com.dius.pact.core.model.ContentType +import au.com.dius.pact.core.model.IRequest +import au.com.dius.pact.core.model.OptionalBody +import au.com.dius.pact.core.model.Pact +import au.com.dius.pact.core.model.PactSource +import au.com.dius.pact.core.model.Interaction +import au.com.dius.pact.core.model.SynchronousRequestResponse import au.com.dius.pact.provider.IProviderVerifier import au.com.dius.pact.provider.ProviderInfo import au.com.dius.pact.provider.ProviderResponse diff --git a/provider/junit5spring/src/test/groovy/au/com/dius/pact/provider/spring/junit5/WebFluxTargetSpec.groovy b/provider/junit5spring/src/test/groovy/au/com/dius/pact/provider/spring/junit5/WebFluxTargetSpec.groovy index 10ea18777e..819494a4c0 100644 --- a/provider/junit5spring/src/test/groovy/au/com/dius/pact/provider/spring/junit5/WebFluxTargetSpec.groovy +++ b/provider/junit5spring/src/test/groovy/au/com/dius/pact/provider/spring/junit5/WebFluxTargetSpec.groovy @@ -4,6 +4,8 @@ import au.com.dius.pact.core.model.OptionalBody import au.com.dius.pact.core.model.Pact import au.com.dius.pact.core.model.Request import au.com.dius.pact.core.model.RequestResponseInteraction +import au.com.dius.pact.core.model.V4Interaction +import au.com.dius.pact.core.model.messaging.Message import org.springframework.http.MediaType import org.springframework.test.web.reactive.server.WebTestClient import org.springframework.web.reactive.function.BodyInserters @@ -83,4 +85,17 @@ class WebFluxTargetSpec extends Specification { response.contentType.toString() == 'application/json' response.body.valueAsString() == '{"id":1234}' } + + def 'supports any HTTP interaction'() { + expect: + new WebFluxTarget(routerFunction).supportsInteraction(interaction) == result + + where: + interaction | result + new RequestResponseInteraction('test') | true + new Message('test') | false + new V4Interaction.AsynchronousMessage('test') | false + new V4Interaction.SynchronousMessages('test') | false + new V4Interaction.SynchronousHttp('test') | true + } } diff --git a/provider/junit5spring/src/test/groovy/au/com/dius/pact/provider/spring/junit5/WebTestClientTargetSpec.groovy b/provider/junit5spring/src/test/groovy/au/com/dius/pact/provider/spring/junit5/WebTestClientTargetSpec.groovy index dfe12fee2f..12e3bedaee 100644 --- a/provider/junit5spring/src/test/groovy/au/com/dius/pact/provider/spring/junit5/WebTestClientTargetSpec.groovy +++ b/provider/junit5spring/src/test/groovy/au/com/dius/pact/provider/spring/junit5/WebTestClientTargetSpec.groovy @@ -4,6 +4,8 @@ import au.com.dius.pact.core.model.OptionalBody import au.com.dius.pact.core.model.Pact import au.com.dius.pact.core.model.Request import au.com.dius.pact.core.model.RequestResponseInteraction +import au.com.dius.pact.core.model.V4Interaction +import au.com.dius.pact.core.model.messaging.Message import org.springframework.http.MediaType import org.springframework.test.web.reactive.server.WebTestClient import org.springframework.web.reactive.function.BodyInserters @@ -85,4 +87,17 @@ class WebTestClientTargetSpec extends Specification { response.contentType.toString() == 'application/json' response.body.valueAsString() == '{"id":1234}' } + + def 'supports any HTTP interaction'() { + expect: + new WebTestClientTarget(bindToRouterFunction(routerFunction).build()).supportsInteraction(interaction) == result + + where: + interaction | result + new RequestResponseInteraction('test') | true + new Message('test') | false + new V4Interaction.AsynchronousMessage('test') | false + new V4Interaction.SynchronousMessages('test') | false + new V4Interaction.SynchronousHttp('test') | true + } } diff --git a/provider/spring6/src/test/groovy/au/com/dius/pact/provider/spring/spring6/WebFluxTargetSpec.groovy b/provider/spring6/src/test/groovy/au/com/dius/pact/provider/spring/spring6/WebFluxTargetSpec.groovy index 42848ef9f9..f55ac62a85 100644 --- a/provider/spring6/src/test/groovy/au/com/dius/pact/provider/spring/spring6/WebFluxTargetSpec.groovy +++ b/provider/spring6/src/test/groovy/au/com/dius/pact/provider/spring/spring6/WebFluxTargetSpec.groovy @@ -4,6 +4,8 @@ import au.com.dius.pact.core.model.OptionalBody import au.com.dius.pact.core.model.Pact import au.com.dius.pact.core.model.Request import au.com.dius.pact.core.model.RequestResponseInteraction +import au.com.dius.pact.core.model.V4Interaction +import au.com.dius.pact.core.model.messaging.Message import org.springframework.http.MediaType import org.springframework.test.web.reactive.server.WebTestClient import org.springframework.web.reactive.function.BodyInserters @@ -83,4 +85,17 @@ class WebFluxTargetSpec extends Specification { response.contentType.toString() == 'application/json' response.body.valueAsString() == '{"id":1234}' } + + def 'supports any HTTP interaction'() { + expect: + new WebFluxSpring6Target(routerFunction).supportsInteraction(interaction) == result + + where: + interaction | result + new RequestResponseInteraction('test') | true + new Message('test') | false + new V4Interaction.AsynchronousMessage('test') | false + new V4Interaction.SynchronousMessages('test') | false + new V4Interaction.SynchronousHttp('test') | true + } } diff --git a/provider/spring6/src/test/groovy/au/com/dius/pact/provider/spring/spring6/WebTestClientTargetSpec.groovy b/provider/spring6/src/test/groovy/au/com/dius/pact/provider/spring/spring6/WebTestClientTargetSpec.groovy index 5c859d732d..722837cb17 100644 --- a/provider/spring6/src/test/groovy/au/com/dius/pact/provider/spring/spring6/WebTestClientTargetSpec.groovy +++ b/provider/spring6/src/test/groovy/au/com/dius/pact/provider/spring/spring6/WebTestClientTargetSpec.groovy @@ -4,6 +4,8 @@ import au.com.dius.pact.core.model.OptionalBody import au.com.dius.pact.core.model.Pact import au.com.dius.pact.core.model.Request import au.com.dius.pact.core.model.RequestResponseInteraction +import au.com.dius.pact.core.model.V4Interaction +import au.com.dius.pact.core.model.messaging.Message import org.springframework.http.MediaType import org.springframework.test.web.reactive.server.WebTestClient import org.springframework.web.reactive.function.BodyInserters @@ -88,4 +90,18 @@ class WebTestClientTargetSpec extends Specification { response.contentType.toString() == 'application/json' response.body.valueAsString() == '{"id":1234}' } + + def 'supports any HTTP interaction'() { + expect: + new WebTestClientSpring6Target(bindToRouterFunction(routerFunction).build()) + .supportsInteraction(interaction) == result + + where: + interaction | result + new RequestResponseInteraction('test') | true + new Message('test') | false + new V4Interaction.AsynchronousMessage('test') | false + new V4Interaction.SynchronousMessages('test') | false + new V4Interaction.SynchronousHttp('test') | true + } }