Skip to content

Commit

Permalink
feat: Support system properties or environment variables for consumer…
Browse files Browse the repository at this point in the history
… and provider annotation with JUnit4 provider tests #528 #1616
  • Loading branch information
rholshausen committed Nov 25, 2022
1 parent 28ff522 commit 68dd07f
Show file tree
Hide file tree
Showing 6 changed files with 100 additions and 36 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -159,10 +159,10 @@ class PactDslRequestWithPathSpec extends Specification {
when:
def pact = consumerPactBuilder
.uponReceiving("a request")
.path("/api/myrequest")
.method("POST")
.queryMatchingDatetime("startDateTime", "yyyy-MM-dd'T'hh:mm:ss'Z'")
.uponReceiving('a request')
.path('/api/request')
.method('POST')
.queryMatchingDatetime('startDateTime', "yyyy-MM-dd'T'hh:mm:ss'Z'")
.willRespondWith()
.status(200)
.toPact()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@ package au.com.dius.pact.provider.junit

import au.com.dius.pact.core.model.Interaction
import au.com.dius.pact.core.model.Pact
import au.com.dius.pact.core.support.expressions.DataType
import au.com.dius.pact.core.support.expressions.ExpressionParser
import au.com.dius.pact.core.support.expressions.SystemPropertyResolver
import au.com.dius.pact.core.support.expressions.ValueResolver
import au.com.dius.pact.core.support.json.JsonException
import au.com.dius.pact.provider.ProviderUtils
import au.com.dius.pact.provider.ProviderUtils.findAnnotation
Expand Down Expand Up @@ -57,7 +60,7 @@ import java.io.IOException
open class PactRunner<I>(private val clazz: Class<*>) : ParentRunner<InteractionRunner<I>>(clazz) where I : Interaction {

private val children = mutableListOf<InteractionRunner<I>>()
private var valueResolver = SystemPropertyResolver
private var valueResolver: ValueResolver = SystemPropertyResolver
private var initialized = false

private fun initialize() {
Expand All @@ -68,16 +71,8 @@ open class PactRunner<I>(private val clazz: Class<*>) : ParentRunner<Interaction
if (clazz.getAnnotation(Ignore::class.java) != null) {
logger.info("Ignore annotation detected, exiting")
} else {
val providerInfo = findAnnotation(clazz, Provider::class.java) ?: throw InitializationError(
"Provider name should be specified by using ${Provider::class.java.simpleName} annotation")
logger.debug { "Found annotation $providerInfo" }
val serviceName = providerInfo.value

val consumerInfo = findAnnotation(clazz, Consumer::class.java)
if (consumerInfo != null) {
logger.debug { "Found annotation $consumerInfo" }
}
val consumerName = consumerInfo?.value
val (providerInfo, serviceName) = lookupProviderInfo()
val (consumerInfo, consumerName) = lookupConsumerInfo()

val testClass = TestClass(clazz)
val ignoreNoPactsToVerify = findAnnotation(clazz, IgnoreNoPactsToVerify::class.java)
Expand Down Expand Up @@ -122,6 +117,31 @@ open class PactRunner<I>(private val clazz: Class<*>) : ParentRunner<Interaction
initialized = true
}

private fun lookupConsumerInfo(): Pair<Consumer?, String?> {
val consumerInfo = findAnnotation(clazz, Consumer::class.java)
return if (consumerInfo != null) {
logger.debug { "Found annotation $consumerInfo" }
val consumerName = ExpressionParser.parseExpression(consumerInfo.value, DataType.STRING, valueResolver)?.toString()
Pair(consumerInfo, consumerName)
} else {
Pair(null, null)
}
}

private fun lookupProviderInfo(): Pair<Provider, String> {
val providerInfo = findAnnotation(clazz, Provider::class.java) ?: throw InitializationError(
"Provider name should be specified by using ${Provider::class.java.simpleName} annotation"
)
logger.debug { "Found annotation $providerInfo" }
val serviceName = ExpressionParser.parseExpression(providerInfo.value, DataType.STRING, valueResolver)?.toString()
if (serviceName.isNullOrEmpty()) {
throw InitializationError(
"Provider name specified by ${Provider::class.java.simpleName} annotation is null or empty"
)
}
return Pair(providerInfo, serviceName)
}

private fun checkIgnoreIoException(ignoreIoErrors: String, e: Exception) = if (ignoreIoErrors == "true") {
logger.warn { "\n" + WARNING_ON_IGNORED_IOERROR.trimIndent() }
logger.debug(e) { "Failed to load pact files" }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package au.com.dius.pact.provider.junit
import au.com.dius.pact.core.model.Pact
import au.com.dius.pact.core.model.RequestResponsePact
import au.com.dius.pact.core.model.UrlSource
import au.com.dius.pact.provider.junitsupport.Consumer
import au.com.dius.pact.provider.junitsupport.IgnoreNoPactsToVerify
import au.com.dius.pact.provider.junitsupport.Provider
import au.com.dius.pact.provider.junitsupport.loader.PactFolder
Expand All @@ -14,7 +15,9 @@ import au.com.dius.pact.provider.junitsupport.target.Target
import au.com.dius.pact.provider.junitsupport.target.TestTarget
import org.junit.runner.notification.RunNotifier
import org.junit.runners.model.InitializationError
import spock.lang.Issue
import spock.lang.Specification
import spock.util.environment.RestoreSystemProperties

@SuppressWarnings('UnusedObject')
class PactRunnerSpec extends Specification {
Expand Down Expand Up @@ -116,6 +119,21 @@ class PactRunnerSpec extends Specification {
Target target
}

@Provider('${provider.name}')
@PactFolder('pacts')
class ProviderFromSystemPropTestClass {
@TestTarget
Target target
}

@Provider('myAwesomeService')
@Consumer('${consumer.name}')
@PactFolder('pacts')
class ConsumerFromSystemPropTestClass {
@TestTarget
Target target
}

def 'PactRunner throws an exception if there is no @Provider annotation on the test class'() {
when:
new PactRunner(PactRunnerSpec).run(new RunNotifier())
Expand Down Expand Up @@ -219,4 +237,31 @@ class PactRunnerSpec extends Specification {
!runner.children.empty
}

@Issue('#528')
@RestoreSystemProperties
def 'PactRunner supports getting the provider name from a system property or environment variable'() {
given:
System.setProperty('provider.name', 'myAwesomeService')

when:
def runner = new PactRunner(ProviderFromSystemPropTestClass)
runner.run(new RunNotifier())

then:
!runner.children.empty
}

@Issue('#528')
@RestoreSystemProperties
def 'PactRunner supports getting the consumer name from a system property or environment variable'() {
given:
System.setProperty('consumer.name', 'anotherService')

when:
def runner = new PactRunner(ConsumerFromSystemPropTestClass)
runner.run(new RunNotifier())

then:
!runner.children.empty
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ import java.lang.annotation.Inherited
import kotlin.annotation.Retention

/**
* Used to pass consumer name to Pact runner
* Used to pass consumer name to Pact runner. Can use expressions (in `${}` form) to get the value from Java system
* properties or environment variables.
*/
@Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.CLASS, AnnotationTarget.FILE)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package au.com.dius.pact.provider.junitsupport

import java.lang.annotation.Inherited
import kotlin.annotation.Retention

/**
* Used to pass provider name to Pact runner. Can use expressions (in `${}` form) to get the value from Java system
* properties or environment variables.
*/
@Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.CLASS, AnnotationTarget.FILE)
@Inherited
annotation class Provider(
/**
* @return provider name for pact test running
*/
val value: String = ""
)

0 comments on commit 68dd07f

Please sign in to comment.