Skip to content

Commit

Permalink
fix: support consumer version selector methods on Kotlin test classes #…
Browse files Browse the repository at this point in the history
  • Loading branch information
uglyog committed Aug 11, 2022
1 parent d56079c commit cfc6e38
Show file tree
Hide file tree
Showing 7 changed files with 232 additions and 26 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package au.com.dius.pact.provider.junit5;

import au.com.dius.pact.provider.junitsupport.IgnoreNoPactsToVerify;
import au.com.dius.pact.provider.junitsupport.Provider;
import au.com.dius.pact.provider.junitsupport.loader.PactBroker;
import au.com.dius.pact.provider.junitsupport.loader.PactBrokerConsumerVersionSelectors;
import au.com.dius.pact.provider.junitsupport.loader.SelectorBuilder;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.TestTemplate;
import org.junit.jupiter.api.extension.ExtendWith;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;

@Provider("Animal Profile Service")
@PactBroker(url = "http://broker.host")
@IgnoreNoPactsToVerify(ignoreIoErrors = "true")
class ConsumerVersionSelectorJavaTest {
static boolean called = false;

@PactBrokerConsumerVersionSelectors
public static SelectorBuilder consumerVersionSelectors() {
called = true;
return new SelectorBuilder().branch("current");
}

@TestTemplate
@ExtendWith(PactVerificationInvocationContextProvider.class)
void pactVerificationTestTemplate(PactVerificationContext context) {
if (context != null) {
context.verifyInteraction();
}
}

@AfterAll
static void after() {
assertThat("consumerVersionSelectors() was not called", called, is(true));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
package au.com.dius.pact.provider.junit5

import au.com.dius.pact.provider.junitsupport.IgnoreNoPactsToVerify
import au.com.dius.pact.provider.junitsupport.Provider
import au.com.dius.pact.provider.junitsupport.loader.PactBroker
import au.com.dius.pact.provider.junitsupport.loader.PactBrokerConsumerVersionSelectors
import au.com.dius.pact.provider.junitsupport.loader.SelectorBuilder
import org.hamcrest.MatcherAssert
import org.hamcrest.Matchers
import org.junit.jupiter.api.AfterAll
import org.junit.jupiter.api.TestTemplate
import org.junit.jupiter.api.extension.ExtendWith

@Provider("Animal Profile Service")
@PactBroker(url = "http://broker.host")
@IgnoreNoPactsToVerify(ignoreIoErrors = "true")
class ConsumerVersionSelectorKotlinTest {
@PactBrokerConsumerVersionSelectors
fun consumerVersionSelectors(): SelectorBuilder {
called = true
return SelectorBuilder().branch("current")
}

@TestTemplate
@ExtendWith(PactVerificationInvocationContextProvider::class)
fun pactVerificationTestTemplate(context: PactVerificationContext?) {
context?.verifyInteraction()
}

companion object {
private var called: Boolean = false

@AfterAll
fun after() {
MatcherAssert.assertThat("consumerVersionSelectors() was not called", called, Matchers.`is`(true))
}
}
}

@PactBroker(url = "http://broker.host")
@IgnoreNoPactsToVerify(ignoreIoErrors = "true")
abstract class Base {
@PactBrokerConsumerVersionSelectors
fun consumerVersionSelectors(): SelectorBuilder {
called = true
return SelectorBuilder().branch("current")
}

companion object {
var called: Boolean = false
}
}

@Provider("Animal Profile Service")
class ConsumerVersionSelectorKotlinTestWithAbstractBase: Base() {
@TestTemplate
@ExtendWith(PactVerificationInvocationContextProvider::class)
fun pactVerificationTestTemplate(context: PactVerificationContext?) {
context?.verifyInteraction()
}

companion object {
@AfterAll
fun after() {
MatcherAssert.assertThat("consumerVersionSelectors() was not called", called, Matchers.`is`(true))
}
}
}

@Provider("Animal Profile Service")
@PactBroker(url = "http://broker.host")
@IgnoreNoPactsToVerify(ignoreIoErrors = "true")
class ConsumerVersionSelectorKotlinTestWithCompanionMethod {
@TestTemplate
@ExtendWith(PactVerificationInvocationContextProvider::class)
fun pactVerificationTestTemplate(context: PactVerificationContext?) {
context?.verifyInteraction()
}

companion object {
private var called: Boolean = false

@PactBrokerConsumerVersionSelectors
fun consumerVersionSelectors(): SelectorBuilder {
called = true
return SelectorBuilder().branch("current")
}

@AfterAll
fun after() {
MatcherAssert.assertThat("consumerVersionSelectors() was not called", called, Matchers.`is`(true))
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package au.com.dius.pact.provider.spring.junit5;

import au.com.dius.pact.provider.junit5.PactVerificationContext;
import au.com.dius.pact.provider.junitsupport.IgnoreNoPactsToVerify;
import au.com.dius.pact.provider.junitsupport.Provider;
import au.com.dius.pact.provider.junitsupport.loader.PactBroker;
import au.com.dius.pact.provider.junitsupport.loader.PactBrokerConsumerVersionSelectors;
import au.com.dius.pact.provider.junitsupport.loader.SelectorBuilder;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.TestTemplate;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit.jupiter.SpringExtension;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;

@ExtendWith(SpringExtension.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@Provider("Animal Profile Service")
@PactBroker
@IgnoreNoPactsToVerify(ignoreIoErrors = "true")
class ConsumerVersionSelectorJavaTest {
static boolean called = false;

@PactBrokerConsumerVersionSelectors
public static SelectorBuilder consumerVersionSelectors() {
called = true;
return new SelectorBuilder().branch("current");
}

@TestTemplate
@ExtendWith(PactVerificationSpringProvider.class)
void pactVerificationTestTemplate(PactVerificationContext context) {
if (context != null) {
context.verifyInteraction();
}
}

@AfterAll
static void after() {
assertThat("consumerVersionSelectors() was not called", called, is(true));
}
}
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
package au.com.dius.pact.provider.spring.junit5

import au.com.dius.pact.provider.junit5.PactVerificationContext
import au.com.dius.pact.provider.junitsupport.IgnoreNoPactsToVerify
import au.com.dius.pact.provider.junitsupport.Provider
import au.com.dius.pact.provider.junitsupport.loader.PactBroker
import au.com.dius.pact.provider.junit5.PactVerificationContext
import au.com.dius.pact.provider.junitsupport.loader.PactBrokerConsumerVersionSelectors
import au.com.dius.pact.provider.junitsupport.loader.SelectorBuilder
import org.junit.jupiter.api.Disabled
import org.hamcrest.MatcherAssert
import org.hamcrest.Matchers
import org.junit.jupiter.api.AfterAll
import org.junit.jupiter.api.TestTemplate
import org.junit.jupiter.api.extension.ExtendWith
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.test.context.junit.jupiter.SpringExtension

Expand All @@ -18,17 +19,25 @@ import org.springframework.test.context.junit.jupiter.SpringExtension
@Provider("Animal Profile Service")
@PactBroker
@IgnoreNoPactsToVerify(ignoreIoErrors = "true")
@Disabled
open class ConsumerVersionSelectorTest {
companion object {
@PactBrokerConsumerVersionSelectors
@JvmStatic
fun consumerVersionSelectors() = SelectorBuilder().branch("current")
open class ConsumerVersionSelectorKotlinTest {
@PactBrokerConsumerVersionSelectors
fun consumerVersionSelectors(): SelectorBuilder {
called = true
return SelectorBuilder().branch("current")
}

@TestTemplate
@ExtendWith(PactVerificationSpringProvider::class)
fun pactVerificationTestTemplate(context: PactVerificationContext?) {
context?.verifyInteraction()
}

companion object {
private var called: Boolean = false

@AfterAll
fun after() {
MatcherAssert.assertThat("consumerVersionSelectors() was not called", called, Matchers.`is`(true))
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,12 @@ import com.github.michaelbull.result.Ok
import mu.KLogging
import org.apache.hc.core5.net.URIBuilder
import java.io.IOException
import java.lang.reflect.Method
import java.lang.reflect.Modifier
import java.net.URI
import java.net.URISyntaxException
import kotlin.reflect.KCallable
import kotlin.reflect.KClass
import kotlin.reflect.KFunction
import kotlin.reflect.KParameter
import kotlin.reflect.KVisibility
import kotlin.reflect.full.findAnnotation
import kotlin.reflect.full.companionObjectInstance
import kotlin.reflect.full.isSubtypeOf
import kotlin.reflect.full.starProjectedType
import kotlin.reflect.jvm.kotlinFunction
Expand Down Expand Up @@ -138,7 +135,7 @@ open class PactBrokerLoader(
val tags = pactBrokerTags.orEmpty().flatMap { ep.parseListExpression(it, resolver) }
val selectorsMethod = testClassHasSelectorsMethod(this.testClass)
return if (selectorsMethod != null) {
invokeSelectorsMethod(this.testInstance, selectorsMethod)
invokeSelectorsMethod(this.testInstance, this.testClass, selectorsMethod)
} else if (shouldFallBackToTags(tags, pactBrokerConsumerVersionSelectors, resolver)) {
permutations(tags, pactBrokerConsumers.flatMap { ep.parseListExpression(it, resolver) })
.map { ConsumerVersionSelectors.Selector(it.first, true, it.second) }
Expand Down Expand Up @@ -354,28 +351,49 @@ open class PactBrokerLoader(

companion object : KLogging() {
@JvmStatic
fun invokeSelectorsMethod(testInstance: Any?, selectorsMethod: KCallable<*>): List<ConsumerVersionSelectors> {
fun invokeSelectorsMethod(
testInstance: Any?,
testClass: Class<*>?,
method: Method
): List<ConsumerVersionSelectors> {
val projectedType = SelectorBuilder::class.starProjectedType
method.trySetAccessible()
val selectorsMethod = method.kotlinFunction!!
return when (selectorsMethod.parameters.size) {
0 -> if (selectorsMethod.returnType.isSubtypeOf(projectedType)) {
val builder = selectorsMethod.call() as SelectorBuilder
val builder = method.invoke(null) as SelectorBuilder
builder.build()
} else {
selectorsMethod.call() as List<ConsumerVersionSelectors>
method.invoke(null) as List<ConsumerVersionSelectors>
}
1 -> if (selectorsMethod.returnType.isSubtypeOf(projectedType)) {
val builder = selectorsMethod.call(testInstance) as SelectorBuilder
builder.build()
} else {
selectorsMethod.call(testInstance) as List<ConsumerVersionSelectors>
1 -> {
val instance = instanceForMethod(testInstance, testClass, method)
if (selectorsMethod.returnType.isSubtypeOf(projectedType)) {
val builder = method.invoke(instance) as SelectorBuilder
builder.build()
} else {
method.invoke(instance) as List<ConsumerVersionSelectors>
}
}
else -> throw java.lang.IllegalArgumentException(
"Consumer version selector method should not take any parameters and return an instance of SelectorBuilder")
}
}

private fun instanceForMethod(testInstance: Any?, testClass: Class<*>?, selectorsMethod: Method): Any? {
return if (testInstance == null) {
val declaringClass = testClass?.kotlin ?: selectorsMethod.declaringClass.kotlin
if (declaringClass.isCompanion) {
declaringClass.companionObjectInstance
} else {
declaringClass.java.newInstance()
}
} else testInstance
}

@JvmStatic
fun testClassHasSelectorsMethod(testClass: Class<*>?): KFunction<*>? {
@Suppress("ThrowsCount")
fun testClassHasSelectorsMethod(testClass: Class<*>?): Method? {
val method = testClass?.methods?.firstOrNull { method ->
method.getAnnotation(PactBrokerConsumerVersionSelectors::class.java) != null
}
Expand All @@ -402,7 +420,7 @@ open class PactBrokerLoader(
}
}

return method?.kotlinFunction
return method
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1468,7 +1468,7 @@ class PactBrokerLoaderSpec extends Specification {
@Unroll
def 'Invoke Selectors Method'() {
expect:
PactBrokerLoader.invokeSelectorsMethod(instance, PactBrokerLoader.testClassHasSelectorsMethod(clazz)) == result
PactBrokerLoader.invokeSelectorsMethod(instance, clazz, PactBrokerLoader.testClassHasSelectorsMethod(clazz)) == result

where:

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@ class KotlinClassWithSelectorMethod {
fun consumerVersionSelectors() = SelectorBuilder().environment("KotlinSelectorMethod")
}

@Suppress("UnnecessaryAbstractClass")
abstract class KotlinAbstractClassWithSelectorMethod {
@PactBrokerConsumerVersionSelectors
fun consumerVersionSelectors() = SelectorBuilder().environment("KotlinSelectorMethod")
}

@Suppress("UtilityClassWithPublicConstructor")
class KotlinClassWithStaticSelectorMethod {
companion object {
@PactBrokerConsumerVersionSelectors
Expand Down

0 comments on commit cfc6e38

Please sign in to comment.