diff --git a/consumer/junit5/src/main/kotlin/au/com/dius/pact/consumer/junit5/PactConsumerTestExt.kt b/consumer/junit5/src/main/kotlin/au/com/dius/pact/consumer/junit5/PactConsumerTestExt.kt index 2fd5f97d3..6a8f27553 100644 --- a/consumer/junit5/src/main/kotlin/au/com/dius/pact/consumer/junit5/PactConsumerTestExt.kt +++ b/consumer/junit5/src/main/kotlin/au/com/dius/pact/consumer/junit5/PactConsumerTestExt.kt @@ -127,11 +127,11 @@ class PactConsumerTestExt : Extension, BeforeTestExecutionCallback, BeforeAllCal } private fun resolveParameterForProvider( - providerInfo: Pair, + providerInfo: Pair>, extensionContext: ExtensionContext, type: Class<*> ): Any { - val pact = lookupPact(providerInfo.first, providerInfo.second, extensionContext) + val pact = setupPactForTest(providerInfo.first, providerInfo.second, extensionContext) return if (type.isAssignableFrom(MockServer::class.java) && mockServerConfigured(extensionContext)) { setupMockServerForProvider(providerInfo.first, providerInfo.second, extensionContext) } else when (providerInfo.first.providerType) { @@ -212,7 +212,7 @@ class PactConsumerTestExt : Extension, BeforeTestExecutionCallback, BeforeAllCal override fun beforeTestExecution(context: ExtensionContext) { if (!ignoredTest(context)) { - for ((providerInfo, pactMethod) in lookupProviderInfo(context)) { + for ((providerInfo, pactMethods) in lookupProviderInfo(context)) { logger.debug { "providerInfo = $providerInfo" } if (mockServerConfigured(context) || @@ -220,7 +220,7 @@ class PactConsumerTestExt : Extension, BeforeTestExecutionCallback, BeforeAllCal providerInfo.providerType == ProviderType.SYNCH || providerInfo.providerType == ProviderType.UNSPECIFIED ) { - val mockServer = setupMockServerForProvider(providerInfo, pactMethod, context) + val mockServer = setupMockServerForProvider(providerInfo, pactMethods, context) mockServer.start() mockServer.waitForServer() } @@ -235,7 +235,7 @@ class PactConsumerTestExt : Extension, BeforeTestExecutionCallback, BeforeAllCal private fun setupMockServerForProvider( providerInfo: ProviderInfo, - pactMethod: String, + pactMethods: List, context: ExtensionContext ): AbstractBaseMockServer { val store = context.getStore(NAMESPACE) @@ -245,13 +245,57 @@ class PactConsumerTestExt : Extension, BeforeTestExecutionCallback, BeforeAllCal else -> { val config = mockServerConfigFromAnnotation(context, providerInfo).merge(providerInfo.mockServerConfig()) store.put("mockServerConfig:${providerInfo.providerName}", config) - val mockServer = mockServer(lookupPact(providerInfo, pactMethod, context), config) + val mockServer = mockServer(setupPactForTest(providerInfo, pactMethods, context), config) store.put(key, JUnit5MockServerSupport(mockServer)) mockServer } } } + private fun setupPactForTest( + providerInfo: ProviderInfo, + pactMethods: List, + context: ExtensionContext + ): BasePact { + val store = context.getStore(NAMESPACE) + val key = "pact:${providerInfo.providerName}" + return when { + store[key] != null -> store[key] as BasePact + else -> { + val pact = if (pactMethods.isEmpty()) { + lookupPact(providerInfo, "", context) + } else { + val head = pactMethods.first() + val tail = pactMethods.drop(1) + val initial = lookupPact(providerInfo, head, context) + tail.fold(initial) { acc, method -> + val pact = lookupPact(providerInfo, method, context) + + if (pact.provider != acc.provider) { + // Should not really get here, as the Pacts should have been sorted by provider + throw IllegalArgumentException("You are using different Pacts with different providers for the same test" + + " ('${acc.provider}') and '${pact.provider}'). A separate test (and ideally a separate test class)" + + " should be used for each provider.") + } + + if (pact.consumer != acc.consumer) { + logger.warn { + "WARNING: You are using different Pacts with different consumers for the same test " + + "('${acc.consumer}') and '${pact.consumer}'). The second consumer will be ignored and dropped from " + + "the Pact and the interactions merged. If this is not your intention, you need to create a " + + "separate test for each consumer." + } + } + + acc.mergeInteractions(pact.interactions) as BasePact + } + } + store.put(key, pact) + pact + } + } + } + private fun mockServerConfigured(extensionContext: ExtensionContext): Boolean { val mockServerConfig = AnnotationSupport.findAnnotation(extensionContext.requiredTestClass, MockServerConfig::class.java) @@ -314,38 +358,14 @@ class PactConsumerTestExt : Extension, BeforeTestExecutionCallback, BeforeAllCal } } - fun lookupProviderInfo(context: ExtensionContext): List> { + fun lookupProviderInfo(context: ExtensionContext): List>> { logger.trace { "lookupProviderInfo($context)" } val store = context.getStore(NAMESPACE) val providerInfo = when { - store["providers"] != null -> store["providers"] as List> + store["providers"] != null -> store["providers"] as List>> else -> { - val methodAnnotation = if (context.testMethod.isPresent && - AnnotationSupport.isAnnotated(context.testMethod.get(), PactTestFor::class.java)) { - val testMethod = context.testMethod.get() - logger.debug { "Found @PactTestFor annotation on test method $testMethod" } - AnnotationSupport.findAnnotation(testMethod, PactTestFor::class.java).get() - } else { - null - } - - val classAnnotation = if (AnnotationSupport.isAnnotated(context.requiredTestClass, PactTestFor::class.java)) { - logger.debug { "Found @PactTestFor annotation on test ${context.requiredTestClass}" } - AnnotationSupport.findAnnotation(context.requiredTestClass, PactTestFor::class.java).get() - } else if (AnnotationSupport.isAnnotated(context.requiredTestClass, Nested::class.java)) { - logger.debug { - "Found @Nested annotation on test class ${context.requiredTestClass}, will search the enclosing classes" - } - val searchResult = Annotations.searchForAnnotation(context.requiredTestClass.kotlin, PactTestFor::class) - if (searchResult != null) { - logger.debug { "Found @PactTestFor annotation on outer $searchResult" } - searchResult.findAnnotation() - } else { - null - } - } else { - null - } + val methodAnnotation = pactTestForTestMethod(context) + val classAnnotation = pactTestForClass(context) val providerInfo = when { classAnnotation != null && methodAnnotation != null -> @@ -363,42 +383,34 @@ class PactConsumerTestExt : Extension, BeforeTestExecutionCallback, BeforeAllCal providerInfo != null -> { when { methodAnnotation != null -> if (methodAnnotation.pactMethods.isNotEmpty()) { - methodAnnotation.pactMethods.map { - val providerName = providerNameFromPactMethod(it, context) - val provider = if (providerName.isNotEmpty()) - providerInfo.copy(providerName = providerName) - else providerInfo - val mockServerConfig = mockServerConfigFromAnnotation(context, provider) - provider.withMockServerConfig(mockServerConfig) to it - } + buildProviderList(methodAnnotation, context, providerInfo) } else { val mockServerConfig = mockServerConfigFromAnnotation(context, providerInfo) val provider = providerInfo.withMockServerConfig(mockServerConfig) - listOf(provider to methodAnnotation.pactMethod) + val pactMethods = if (methodAnnotation.pactMethod.isNotEmpty()) + listOf(methodAnnotation.pactMethod) + else emptyList() + listOf(provider to pactMethods) } classAnnotation != null -> if (classAnnotation.pactMethods.isNotEmpty()) { - classAnnotation.pactMethods.map { - val providerName = providerNameFromPactMethod(it, context) - val provider = if (providerName.isNotEmpty()) - providerInfo.copy(providerName = providerName) - else providerInfo - val mockServerConfig = mockServerConfigFromAnnotation(context, provider) - provider.withMockServerConfig(mockServerConfig) to it - } + buildProviderList(classAnnotation, context, providerInfo) } else { val mockServerConfig = mockServerConfigFromAnnotation(context, providerInfo) val provider = providerInfo.withMockServerConfig(mockServerConfig) - listOf(provider to classAnnotation.pactMethod) + val pactMethods = if (classAnnotation.pactMethod.isNotEmpty()) + listOf(classAnnotation.pactMethod) + else emptyList() + listOf(provider to pactMethods) } else -> { logger.warn { "No @PactTestFor annotation found on test class, using defaults" } - listOf(ProviderInfo() to "") + listOf(ProviderInfo() to listOf()) } } } else -> { logger.warn { "No @PactTestFor annotation found on test class, using defaults" } - listOf(ProviderInfo() to "") + listOf(ProviderInfo() to listOf()) } } @@ -411,6 +423,59 @@ class PactConsumerTestExt : Extension, BeforeTestExecutionCallback, BeforeAllCal return providerInfo } + private fun buildProviderList( + annotation: PactTestFor, + context: ExtensionContext, + providerInfo: ProviderInfo + ): List>> { + val target = mutableMapOf>() + return annotation.pactMethods.fold(target) { acc, method -> + val providerName = providerNameFromPactMethod(method, context) + val provider = if (providerName.isNotEmpty()) + providerInfo.copy(providerName = providerName) + else providerInfo + + val key = acc.keys.firstOrNull { it.providerName == provider.providerName } + if (key != null) { + acc[key]!!.add(method) + } else { + val mockServerConfig = mockServerConfigFromAnnotation(context, provider) + provider.withMockServerConfig(mockServerConfig) + acc[provider] = mutableListOf(method) + } + acc + }.toList() + } + + private fun pactTestForClass(context: ExtensionContext) = + if (AnnotationSupport.isAnnotated(context.requiredTestClass, PactTestFor::class.java)) { + logger.debug { "Found @PactTestFor annotation on test ${context.requiredTestClass}" } + AnnotationSupport.findAnnotation(context.requiredTestClass, PactTestFor::class.java).get() + } else if (AnnotationSupport.isAnnotated(context.requiredTestClass, Nested::class.java)) { + logger.debug { + "Found @Nested annotation on test class ${context.requiredTestClass}, will search the enclosing classes" + } + val searchResult = Annotations.searchForAnnotation(context.requiredTestClass.kotlin, PactTestFor::class) + if (searchResult != null) { + logger.debug { "Found @PactTestFor annotation on outer $searchResult" } + searchResult.findAnnotation() + } else { + null + } + } else { + null + } + + private fun pactTestForTestMethod(context: ExtensionContext) = + if (context.testMethod.isPresent && + AnnotationSupport.isAnnotated(context.testMethod.get(), PactTestFor::class.java)) { + val testMethod = context.testMethod.get() + logger.debug { "Found @PactTestFor annotation on test method $testMethod" } + AnnotationSupport.findAnnotation(testMethod, PactTestFor::class.java).get() + } else { + null + } + private fun providerNameFromPactMethod(methodName: String, context: ExtensionContext): String { val method = pactMethodAnnotation(null, context, methodName) return method!!.getAnnotation(Pact::class.java).provider @@ -422,97 +487,93 @@ class PactConsumerTestExt : Extension, BeforeTestExecutionCallback, BeforeAllCal context: ExtensionContext ): BasePact { val store = context.getStore(NAMESPACE) - if (store["pact:${providerInfo.providerName}"] == null) { - val providerName = providerInfo.providerName.ifEmpty { "default" } - val method = pactMethodAnnotation(providerName, context, pactMethod) - - val providerType = providerInfo.providerType ?: ProviderType.SYNCH - if (method == null) { - throw UnsupportedOperationException("No method annotated with @Pact was found on test class " + - context.requiredTestClass.simpleName + " for provider '${providerInfo.providerName}'") - } else if (providerType == ProviderType.SYNCH && !JUnitTestSupport.conformsToSignature(method, providerInfo.pactVersion ?: PactSpecVersion.V4)) { - throw UnsupportedOperationException("Method ${method.name} does not conform to required method signature " + - "'public [RequestResponsePact|V4Pact] xxx(PactBuilder builder)'") - } else if (providerType == ProviderType.ASYNCH && !JUnitTestSupport.conformsToMessagePactSignature(method, providerInfo.pactVersion ?: PactSpecVersion.V4)) { - throw UnsupportedOperationException("Method ${method.name} does not conform to required method signature " + - "'public [MessagePact|V4Pact] xxx(PactBuilder builder)'") - } else if (providerType == ProviderType.SYNCH_MESSAGE && !JUnitTestSupport.conformsToSynchMessagePactSignature(method, providerInfo.pactVersion ?: PactSpecVersion.V4)) { - throw UnsupportedOperationException("Method ${method.name} does not conform to required method signature " + - "'public V4Pact xxx(PactBuilder builder)'") - } - - val pactAnnotation = AnnotationSupport.findAnnotation(method, Pact::class.java).get() - val pactConsumer = ep.parseExpression(pactAnnotation.consumer, DataType.STRING)?.toString() ?: pactAnnotation.consumer - logger.debug { - "Invoking method '${method.name}' to get Pact for the test " + - "'${context.testMethod.map { it.name }.orElse("unknown")}'" - } - - val provider = ep.parseExpression(pactAnnotation.provider, DataType.STRING)?.toString() - val providerNameToUse = if (provider.isNullOrEmpty()) providerName else provider - val pact = when (providerType) { - ProviderType.SYNCH, ProviderType.UNSPECIFIED -> { - if (method.parameterTypes[0].isAssignableFrom(Class.forName("au.com.dius.pact.consumer.dsl.PactDslWithProvider"))) { - val consumerPactBuilder = ConsumerPactBuilder.consumer(pactConsumer) - if (providerInfo.pactVersion != null) { - consumerPactBuilder.pactSpecVersion(providerInfo.pactVersion) - } - ReflectionSupport.invokeMethod(method, context.requiredTestInstance, - consumerPactBuilder.hasPactWith(providerNameToUse)) as BasePact - } else { - val pactBuilder = PactBuilder(pactConsumer, providerNameToUse) - if (providerInfo.pactVersion != null) { - pactBuilder.pactSpecVersion(providerInfo.pactVersion) - } - ReflectionSupport.invokeMethod(method, context.requiredTestInstance, pactBuilder) as BasePact + val providerName = providerInfo.providerName.ifEmpty { "default" } + val method = pactMethodAnnotation(providerName, context, pactMethod) + + val providerType = providerInfo.providerType ?: ProviderType.SYNCH + if (method == null) { + throw UnsupportedOperationException("No method annotated with @Pact was found on test class " + + context.requiredTestClass.simpleName + " for provider '${providerInfo.providerName}'") + } else if (providerType == ProviderType.SYNCH && !JUnitTestSupport.conformsToSignature(method, providerInfo.pactVersion ?: PactSpecVersion.V4)) { + throw UnsupportedOperationException("Method ${method.name} does not conform to required method signature " + + "'public [RequestResponsePact|V4Pact] xxx(PactBuilder builder)'") + } else if (providerType == ProviderType.ASYNCH && !JUnitTestSupport.conformsToMessagePactSignature(method, providerInfo.pactVersion ?: PactSpecVersion.V4)) { + throw UnsupportedOperationException("Method ${method.name} does not conform to required method signature " + + "'public [MessagePact|V4Pact] xxx(PactBuilder builder)'") + } else if (providerType == ProviderType.SYNCH_MESSAGE && !JUnitTestSupport.conformsToSynchMessagePactSignature(method, providerInfo.pactVersion ?: PactSpecVersion.V4)) { + throw UnsupportedOperationException("Method ${method.name} does not conform to required method signature " + + "'public V4Pact xxx(PactBuilder builder)'") + } + + val pactAnnotation = AnnotationSupport.findAnnotation(method, Pact::class.java).get() + val pactConsumer = ep.parseExpression(pactAnnotation.consumer, DataType.STRING)?.toString() ?: pactAnnotation.consumer + logger.debug { + "Invoking method '${method.name}' to get Pact for the test " + + "'${context.testMethod.map { it.name }.orElse("unknown")}'" + } + + val provider = ep.parseExpression(pactAnnotation.provider, DataType.STRING)?.toString() + val providerNameToUse = if (provider.isNullOrEmpty()) providerName else provider + val pact = when (providerType) { + ProviderType.SYNCH, ProviderType.UNSPECIFIED -> { + if (method.parameterTypes[0].isAssignableFrom(Class.forName("au.com.dius.pact.consumer.dsl.PactDslWithProvider"))) { + val consumerPactBuilder = ConsumerPactBuilder.consumer(pactConsumer) + if (providerInfo.pactVersion != null) { + consumerPactBuilder.pactSpecVersion(providerInfo.pactVersion) } - } - ProviderType.ASYNCH -> { - if (method.parameterTypes[0].isAssignableFrom(Class.forName("au.com.dius.pact.consumer.MessagePactBuilder"))) { - ReflectionSupport.invokeMethod( - method, context.requiredTestInstance, - MessagePactBuilder(providerInfo.pactVersion ?: PactSpecVersion.V3) - .consumer(pactConsumer).hasPactWith(providerNameToUse) - ) as BasePact - } else { - val pactBuilder = PactBuilder(pactConsumer, providerNameToUse) - if (providerInfo.pactVersion != null) { - pactBuilder.pactSpecVersion(providerInfo.pactVersion) - } - ReflectionSupport.invokeMethod(method, context.requiredTestInstance, pactBuilder) as BasePact + ReflectionSupport.invokeMethod(method, context.requiredTestInstance, + consumerPactBuilder.hasPactWith(providerNameToUse)) as BasePact + } else { + val pactBuilder = PactBuilder(pactConsumer, providerNameToUse) + if (providerInfo.pactVersion != null) { + pactBuilder.pactSpecVersion(providerInfo.pactVersion) } + ReflectionSupport.invokeMethod(method, context.requiredTestInstance, pactBuilder) as BasePact } - ProviderType.SYNCH_MESSAGE -> { - if (method.parameterTypes[0].isAssignableFrom(Class.forName("au.com.dius.pact.consumer.dsl.SynchronousMessagePactBuilder"))) { - ReflectionSupport.invokeMethod( - method, context.requiredTestInstance, - SynchronousMessagePactBuilder(providerInfo.pactVersion ?: PactSpecVersion.V4) - .consumer(pactConsumer).hasPactWith(providerNameToUse) - ) as BasePact - } else { - val pactBuilder = PactBuilder(pactConsumer, providerNameToUse) - if (providerInfo.pactVersion != null) { - pactBuilder.pactSpecVersion(providerInfo.pactVersion) - } - ReflectionSupport.invokeMethod(method, context.requiredTestInstance, pactBuilder) as BasePact + } + ProviderType.ASYNCH -> { + if (method.parameterTypes[0].isAssignableFrom(Class.forName("au.com.dius.pact.consumer.MessagePactBuilder"))) { + ReflectionSupport.invokeMethod( + method, context.requiredTestInstance, + MessagePactBuilder(providerInfo.pactVersion ?: PactSpecVersion.V3) + .consumer(pactConsumer).hasPactWith(providerNameToUse) + ) as BasePact + } else { + val pactBuilder = PactBuilder(pactConsumer, providerNameToUse) + if (providerInfo.pactVersion != null) { + pactBuilder.pactSpecVersion(providerInfo.pactVersion) } + ReflectionSupport.invokeMethod(method, context.requiredTestInstance, pactBuilder) as BasePact } } - - if (providerInfo.pactVersion != null && providerInfo.pactVersion >= PactSpecVersion.V4) { - pact.asV4Pact().unwrap().interactions.forEach { i -> - i.comments["testname"] = Json.toJson(context.testClass.map { it.name + "." }.orElse("") + - context.displayName) + ProviderType.SYNCH_MESSAGE -> { + if (method.parameterTypes[0].isAssignableFrom(Class.forName("au.com.dius.pact.consumer.dsl.SynchronousMessagePactBuilder"))) { + ReflectionSupport.invokeMethod( + method, context.requiredTestInstance, + SynchronousMessagePactBuilder(providerInfo.pactVersion ?: PactSpecVersion.V4) + .consumer(pactConsumer).hasPactWith(providerNameToUse) + ) as BasePact + } else { + val pactBuilder = PactBuilder(pactConsumer, providerNameToUse) + if (providerInfo.pactVersion != null) { + pactBuilder.pactSpecVersion(providerInfo.pactVersion) + } + ReflectionSupport.invokeMethod(method, context.requiredTestInstance, pactBuilder) as BasePact } } + } - val executedFragments = store["executedFragments"] as MutableSet - executedFragments.add(method) - store.put("pact:${providerInfo.providerName}", pact) - return pact - } else { - return store["pact:${providerInfo.providerName}"] as BasePact + if (providerInfo.pactVersion != null && providerInfo.pactVersion >= PactSpecVersion.V4) { + pact.asV4Pact().unwrap().interactions.forEach { i -> + i.comments["testname"] = Json.toJson(context.testClass.map { it.name + "." }.orElse("") + + context.displayName) + } } + + val executedFragments = store["executedFragments"] as MutableSet + executedFragments.add(method) + + return pact } private fun pactMethodAnnotation(providerName: String?, context: ExtensionContext, pactMethod: String): Method? { diff --git a/consumer/junit5/src/test/groovy/au/com/dius/pact/consumer/junit5/PactConsumerTestExtSpec.groovy b/consumer/junit5/src/test/groovy/au/com/dius/pact/consumer/junit5/PactConsumerTestExtSpec.groovy index c7311fa51..2e1804a27 100644 --- a/consumer/junit5/src/test/groovy/au/com/dius/pact/consumer/junit5/PactConsumerTestExtSpec.groovy +++ b/consumer/junit5/src/test/groovy/au/com/dius/pact/consumer/junit5/PactConsumerTestExtSpec.groovy @@ -26,7 +26,7 @@ import spock.util.environment.RestoreSystemProperties import java.lang.reflect.Method @SuppressWarnings(['EmptyMethod', 'UnusedMethodParameter', 'UnnecessaryGetter', - 'UnnecessaryParenthesesForMethodCallWithClosure', 'LineLength']) + 'UnnecessaryParenthesesForMethodCallWithClosure', 'LineLength', 'ExplicitHashSetInstantiation']) @PactTestFor(providerName = 'PactConsumerTestExtSpecProvider', pactVersion = PactSpecVersion.V3) class PactConsumerTestExtSpec extends Specification { @@ -58,6 +58,7 @@ class PactConsumerTestExtSpec extends Specification { getTestMethod() >> { Optional.ofNullable(testMethod) } getExecutionException() >> Optional.empty() getStore(_) >> mockStore + getRequiredTestInstance() >> { requiredTestClass.newInstance() } } } @@ -71,7 +72,7 @@ class PactConsumerTestExtSpec extends Specification { def store = [get: { arg -> if (arg == 'providers') { - [new Pair(providerInfo, 'test')] + [new Pair(providerInfo, ['test'])] } else if (model.isAssignableFrom(V4Pact)) { model.newInstance(new Consumer(), new Provider(), []) } else { @@ -110,7 +111,7 @@ class PactConsumerTestExtSpec extends Specification { mockStoreData['mockServer:provider'] = new JUnit5MockServerSupport(mockServer) mockStoreData['mockServerConfig:provider'] = new MockProviderConfig() - mockStoreData['providers'] = [new Pair(new ProviderInfo('provider'), 'test')] + mockStoreData['providers'] = [new Pair(new ProviderInfo('provider'), ['test'])] def provider = new Provider('provider') def consumer = new Consumer('consumer') @@ -141,7 +142,7 @@ class PactConsumerTestExtSpec extends Specification { providerInfo.size() == 1 providerInfo.first().first.providerName == 'PactConsumerTestExtSpecProvider' providerInfo.first().first.pactVersion == PactSpecVersion.V3 - providerInfo.first().second == '' + providerInfo.first().second.empty } static class TestClass { @@ -160,7 +161,7 @@ class PactConsumerTestExtSpec extends Specification { providerInfo.size() == 1 providerInfo.first().first.providerName == 'PactConsumerTestExtSpecMethodProvider' providerInfo.first().first.pactVersion == PactSpecVersion.V1 - providerInfo.first().second == '' + providerInfo.first().second.empty } @PactTestFor(providerName = 'PactConsumerTestExtSpecClassProvider', pactVersion = PactSpecVersion.V3) @@ -181,7 +182,7 @@ class PactConsumerTestExtSpec extends Specification { providerInfo.size() == 1 providerInfo.first().first.providerName == 'PactConsumerTestExtSpecMethodProvider' providerInfo.first().first.pactVersion == PactSpecVersion.V3 - providerInfo.first().second == '' + providerInfo.first().second.empty } @PactTestFor(providerName = 'PactConsumerTestExtSpecClassProvider', pactVersion = PactSpecVersion.V3) @@ -201,7 +202,7 @@ class PactConsumerTestExtSpec extends Specification { providerInfo.first().first.pactVersion == PactSpecVersion.V3 providerInfo.first().first.https providerInfo.first().first.port == '1234' - providerInfo.first().second == '' + providerInfo.first().second.empty } @PactTestFor(providerName = 'PactConsumerTestExtSpecClassProvider', pactVersion = PactSpecVersion.V3) @@ -225,7 +226,7 @@ class PactConsumerTestExtSpec extends Specification { providerInfo.first().first.pactVersion == PactSpecVersion.V3 providerInfo.first().first.https providerInfo.first().first.port == '1235' - providerInfo.first().second == '' + providerInfo.first().second.empty } @PactTestFor(providerName = 'TestClassEmptyProviderOnMethod', pactVersion = PactSpecVersion.V3) @@ -249,7 +250,43 @@ class PactConsumerTestExtSpec extends Specification { providerInfo.size() == 1 providerInfo.first().first.providerName == 'TestClassEmptyProviderOnMethod' providerInfo.first().first.pactVersion == PactSpecVersion.V3 - providerInfo.first().second == 'pactMethod' + providerInfo.first().second == ['pactMethod'] + } + + @PactTestFor(providerName = 'TestClassMultiplePactMethods') + static class TestClassMultiplePactMethods { + @Pact + RequestResponsePact pactMethod1(PactDslWithProvider builder) { + builder + .uponReceiving('interaction 1') + .path('/one') + .toPact() + } + + @Pact + RequestResponsePact pactMethod2(PactDslWithProvider builder) { + builder + .uponReceiving('interaction 2') + .path('/two') + .toPact() + } + + @PactTestFor(pactMethods = [ 'pactMethod1', 'pactMethod2' ]) + def pactTestForMethod() { } + } + + def 'lookupProviderInfo - with multiple pact methods for the same provider'() { + given: + testMethod = TestClassMultiplePactMethods.getMethod('pactTestForMethod') + requiredTestClass = TestClassMultiplePactMethods + + when: + def providerInfo = testExt.lookupProviderInfo(mockContext) + + then: + providerInfo.size() == 1 + providerInfo.first().first.providerName == 'TestClassMultiplePactMethods' + providerInfo.first().second == ['pactMethod1', 'pactMethod2'] } def 'mockServerConfigured - returns false when there are no MockServerConfig annotations'() { @@ -399,4 +436,61 @@ class PactConsumerTestExtSpec extends Specification { expect: testExt.ignoredTest(mockContext) } + + class TestSetupPactFor { + @Pact(consumer = 'consumer1') + RequestResponsePact pactMethod1(PactDslWithProvider builder) { + builder + .uponReceiving('interaction 1') + .path('/one') + .willRespondWith() + .toPact() + } + + @Pact(consumer = 'consumer1') + RequestResponsePact pactMethod2(PactDslWithProvider builder) { + builder + .uponReceiving('interaction 2') + .path('/two') + .willRespondWith() + .toPact() + } + + @PactTestFor + def testMethod() { } + } + + def 'setupPactForTest - pact methods is empty'() { + given: + requiredTestClass = TestSetupPactFor + testMethod = TestSetupPactFor.getMethod('testMethod') + def provider = new ProviderInfo('setupPactForTest', '', '', PactSpecVersion.V3) + mockStoreData['executedFragments'] = new HashSet() + + expect: + testExt.setupPactForTest(provider, [], mockContext).interactions*.description == ['interaction 1'] + } + + def 'setupPactForTest - pact methods has one entry'() { + given: + requiredTestClass = TestSetupPactFor + testMethod = TestSetupPactFor.getMethod('testMethod') + def provider = new ProviderInfo('setupPactForTest', '', '', PactSpecVersion.V3) + mockStoreData['executedFragments'] = new HashSet() + + expect: + testExt.setupPactForTest(provider, ['pactMethod2'], mockContext).interactions*.description == ['interaction 2'] + } + + def 'setupPactForTest - pact methods has more than one entry'() { + given: + requiredTestClass = TestSetupPactFor + testMethod = TestSetupPactFor.getMethod('testMethod') + def provider = new ProviderInfo('setupPactForTest', '', '', PactSpecVersion.V3) + mockStoreData['executedFragments'] = new HashSet() + + expect: + testExt.setupPactForTest(provider, ['pactMethod1', 'pactMethod2'], mockContext).interactions*.description == + ['interaction 1', 'interaction 2'] + } } diff --git a/consumer/junit5/src/test/groovy/au/com/dius/pact/consumer/junit5/PactConsumerTestExtTest.groovy b/consumer/junit5/src/test/groovy/au/com/dius/pact/consumer/junit5/PactConsumerTestExtTest.groovy index 35c120cc2..9224fda43 100644 --- a/consumer/junit5/src/test/groovy/au/com/dius/pact/consumer/junit5/PactConsumerTestExtTest.groovy +++ b/consumer/junit5/src/test/groovy/au/com/dius/pact/consumer/junit5/PactConsumerTestExtTest.groovy @@ -6,23 +6,17 @@ import au.com.dius.pact.core.model.PactSpecVersion import au.com.dius.pact.core.model.Provider import au.com.dius.pact.core.model.RequestResponsePact import au.com.dius.pact.core.model.annotations.Pact -import org.apache.commons.lang3.JavaVersion -import org.apache.commons.lang3.SystemUtils import org.hamcrest.Matchers import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.Test import org.junit.jupiter.api.extension.ExtensionContext -import spock.lang.Requires import java.lang.reflect.Method import static org.hamcrest.MatcherAssert.assertThat import static org.junit.jupiter.api.Assertions.assertThrows -// TODO: Groovy mocks don't work on JDK 16 -@Requires(reason = "Groovy mocks don't work on JDK 16", - value = { SystemUtils.isJavaVersionAtMost(JavaVersion.JAVA_15) }) class PactConsumerTestExtTest { private final subject = new PactConsumerTestExt() @@ -163,7 +157,7 @@ class PactConsumerTestExtTest { assertThat(providerInfo.first.hostInterface, Matchers.is('')) assertThat(providerInfo.first.port, Matchers.is('')) assertThat(providerInfo.first.pactVersion, Matchers.is(Matchers.nullValue())) - assertThat(providerInfo.second, Matchers.is('')) + assert providerInfo.second == [] } @Test @@ -181,7 +175,7 @@ class PactConsumerTestExtTest { assertThat(providerInfo.first.hostInterface, Matchers.is('localhost')) assertThat(providerInfo.first.port, Matchers.is('8080')) assertThat(providerInfo.first.pactVersion, Matchers.is(PactSpecVersion.V3)) - assertThat(providerInfo.second, Matchers.is('pactMethod')) + assert providerInfo.second == ['pactMethod'] } @Test @@ -199,7 +193,7 @@ class PactConsumerTestExtTest { assertThat(providerInfo.first.hostInterface, Matchers.is('localhost')) assertThat(providerInfo.first.port, Matchers.is('8080')) assertThat(providerInfo.first.pactVersion, Matchers.is(PactSpecVersion.V3)) - assertThat(providerInfo.second, Matchers.is('pactMethod')) + assert providerInfo.second == ['pactMethod'] } @Test @@ -219,7 +213,7 @@ class PactConsumerTestExtTest { assertThat(providerInfo.first.hostInterface, Matchers.is('testServer')) assertThat(providerInfo.first.port, Matchers.is('1234')) assertThat(providerInfo.first.pactVersion, Matchers.is(PactSpecVersion.V1_1)) - assertThat(providerInfo.second, Matchers.is('pactMethod')) + assert providerInfo.second == ['pactMethod'] } @Test diff --git a/consumer/junit5/src/test/java/au/com/dius/pact/consumer/junit5/Issue1457MultiMethodsTest.java b/consumer/junit5/src/test/java/au/com/dius/pact/consumer/junit5/Issue1457MultiMethodsTest.java new file mode 100644 index 000000000..38442e184 --- /dev/null +++ b/consumer/junit5/src/test/java/au/com/dius/pact/consumer/junit5/Issue1457MultiMethodsTest.java @@ -0,0 +1,54 @@ +package au.com.dius.pact.consumer.junit5; + +import au.com.dius.pact.consumer.MockServer; +import au.com.dius.pact.consumer.dsl.PactDslWithProvider; +import au.com.dius.pact.core.model.PactSpecVersion; +import au.com.dius.pact.core.model.RequestResponsePact; +import au.com.dius.pact.core.model.annotations.Pact; +import org.apache.hc.client5.http.fluent.Request; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.is; + +@ExtendWith(PactConsumerTestExt.class) +@PactTestFor(providerName = "Issue1457", pactVersion = PactSpecVersion.V3) +public class Issue1457MultiMethodsTest { + @Pact(consumer = "Issue1457Consumer") + public RequestResponsePact countryDetails(PactDslWithProvider builder) { + return builder + .uponReceiving("A request to get USA code") + .path("/code/USA") + .willRespondWith() + .status(200) + .body("United States", "text/plain") + .toPact(); + } + + @Pact(consumer = "Issue1457Consumer") + public RequestResponsePact countryDetails2(PactDslWithProvider builder) { + return builder + .uponReceiving("A request to get other code") + .path("/code/other") + .willRespondWith() + .status(200) + .body("Other", "text/plain") + .toPact(); + } + + @Test + @PactTestFor(pactMethods = {"countryDetails", "countryDetails2"}) + @DisplayName("validate country details") + public void getCountryDetails(MockServer mockServer) throws Exception { + String response = Request.get(mockServer.getUrl() + "/code/USA") + .execute().returnContent().asString(); + assertThat(response, is(equalTo("United States"))); + + response = Request.get(mockServer.getUrl() + "/code/other") + .execute().returnContent().asString(); + assertThat(response, is(equalTo("Other"))); + } +}