Skip to content

Commit

Permalink
feat: Support modifying the request metadata in the provider test bef…
Browse files Browse the repository at this point in the history
…ore being sent to the plugin
  • Loading branch information
rholshausen committed Feb 15, 2023
1 parent a43c2ea commit 49f4d90
Show file tree
Hide file tree
Showing 6 changed files with 180 additions and 76 deletions.
2 changes: 2 additions & 0 deletions provider/junit5/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,6 @@ dependencies {
exclude group: 'org.yaml'
}
testImplementation 'org.yaml:snakeyaml:1.33'
testImplementation 'org.mockito:mockito-core:2.28.2'
testImplementation 'org.mockito:mockito-inline:2.28.2'
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,12 @@ import au.com.dius.pact.provider.DefaultTestResultAccumulator
import au.com.dius.pact.provider.IProviderVerifier
import au.com.dius.pact.provider.ProviderInfo
import au.com.dius.pact.provider.ProviderVerifier
import au.com.dius.pact.provider.RequestData
import au.com.dius.pact.provider.RequestDataToBeVerified
import au.com.dius.pact.provider.TestResultAccumulator
import au.com.dius.pact.provider.junitsupport.VerificationReports
import au.com.dius.pact.provider.reporters.ReporterManager
import io.pact.plugins.jvm.core.InteractionVerificationData
import mu.KLogging
import org.apache.hc.core5.http.ClassicHttpRequest
import org.apache.hc.core5.http.HttpRequest
Expand Down Expand Up @@ -74,9 +77,10 @@ open class PactVerificationExtension(
return when (parameterContext.parameter.type) {
Pact::class.java -> true
Interaction::class.java -> true
ClassicHttpRequest::class.java, HttpRequest::class.java -> testContext.target is HttpTestTarget || testContext.target is HttpsTestTarget
ClassicHttpRequest::class.java, HttpRequest::class.java -> testContext.target is HttpTestTarget
PactVerificationContext::class.java -> true
ProviderVerifier::class.java -> true
RequestData::class.java -> testContext.target is PluginTestTarget
else -> false
}
}
Expand All @@ -89,6 +93,14 @@ open class PactVerificationExtension(
ClassicHttpRequest::class.java, HttpRequest::class.java -> store.get("httpRequest")
PactVerificationContext::class.java -> store.get("interactionContext")
ProviderVerifier::class.java -> store.get("verifier")
RequestData::class.java -> {
val request = store.get("request")
if (request is RequestDataToBeVerified) {
request
} else {
null
}
}
else -> null
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import au.com.dius.pact.provider.IProviderInfo
import au.com.dius.pact.provider.IProviderVerifier
import au.com.dius.pact.provider.PactVerification
import au.com.dius.pact.provider.ProviderResponse
import au.com.dius.pact.provider.RequestDataToBeVerified
import io.pact.plugins.jvm.core.CatalogueEntry
import io.pact.plugins.jvm.core.CatalogueManager
import io.pact.plugins.jvm.core.DefaultPluginManager
Expand Down Expand Up @@ -87,7 +88,7 @@ class PluginTestTarget(private val config: MutableMap<String, Any?> = mutableMap
return when (val v4pact = pact.asV4Pact()) {
is Ok -> when (val result = DefaultPluginManager.prepareValidationForInteraction(transportEntry, v4pact.value,
interaction.asV4Interaction(), config)) {
is Ok -> result.value to transportEntry
is Ok -> RequestDataToBeVerified(result.value) to transportEntry
is Err -> throw RuntimeException("Failed to configure the interaction for verification - ${result.error}")
}
is Err -> throw RuntimeException("PluginTestTarget can only be used with V4 Pacts")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ package au.com.dius.pact.provider.junit5

import au.com.dius.pact.core.model.Consumer
import au.com.dius.pact.core.model.FilteredPact
import au.com.dius.pact.core.model.Interaction
import au.com.dius.pact.core.model.OptionalBody
import au.com.dius.pact.core.model.Pact
import au.com.dius.pact.core.model.PactBrokerSource
import au.com.dius.pact.core.model.Provider
import au.com.dius.pact.core.model.Request
Expand All @@ -13,39 +16,79 @@ import au.com.dius.pact.core.support.expressions.ValueResolver
import au.com.dius.pact.provider.IConsumerInfo
import au.com.dius.pact.provider.IProviderInfo
import au.com.dius.pact.provider.IProviderVerifier
import au.com.dius.pact.provider.ProviderVerifier
import au.com.dius.pact.provider.RequestData
import au.com.dius.pact.provider.RequestDataToBeVerified
import au.com.dius.pact.provider.TestResultAccumulator
import org.apache.hc.core5.http.ClassicHttpRequest
import org.apache.hc.core5.http.HttpRequest
import org.junit.jupiter.api.extension.ExtensionContext
import org.junit.jupiter.api.extension.ParameterContext
import spock.lang.Issue
import spock.lang.Shared
import spock.lang.Specification

class PactVerificationExtensionSpec extends Specification {
import java.lang.reflect.Parameter

def 'updateTestResult uses the original pact when pact is filtered '() {
given:
PactVerificationContext context
ExtensionContext.Store store = Stub {
import static org.mockito.Mockito.mock
import static org.mockito.Mockito.when

@SuppressWarnings('UnnecessaryGetter')
class PactVerificationExtensionSpec extends Specification {
@Shared PactVerificationContext context
PactVerificationExtension extension
@Shared ExtensionContext.Store store
@Shared ExtensionContext extContext
@Shared Map<String, Object> contextMap
ValueResolver mockValueResolver
@Shared RequestResponseInteraction interaction1, interaction2
@Shared RequestResponsePact pact
PactBrokerSource pactSource
@Shared ClassicHttpRequest classicHttpRequest
@Shared ProviderVerifier verifier
@Shared RequestDataToBeVerified data

def setupSpec() {
verifier = Mock(ProviderVerifier)
store = Stub {
get(_) >> { args ->
if (args[0] == 'interactionContext') {
context
} else {
contextMap[args[0]]
}
}
put(_, _) >> { args -> contextMap[args[0]] = args[1] }
}
ExtensionContext extContext = Stub {

extContext = Stub {
getStore(_) >> store
}
def mockValueResolver = Mock(ValueResolver)
interaction1 = new RequestResponseInteraction('interaction1', [], new Request(), new Response())
interaction2 = new RequestResponseInteraction('interaction2', [], new Request(), new Response())
pact = new RequestResponsePact(new Provider(), new Consumer(), [interaction1, interaction2])
classicHttpRequest = Mock(ClassicHttpRequest)
context = new PactVerificationContext(store, extContext, Stub(TestTarget), Stub(IProviderVerifier),
Stub(ValueResolver), Stub(IProviderInfo), Stub(IConsumerInfo), interaction1, pact, [])
data = new RequestDataToBeVerified(OptionalBody.empty(), [:])
}

def setup() {
mockValueResolver = Mock(ValueResolver)
pactSource = new PactBrokerSource('localhost', '80', 'http')
contextMap = [
httpRequest: classicHttpRequest,
verifier: verifier
]
}

def interaction1 = new RequestResponseInteraction('interaction1', [], new Request(), new Response())
def interaction2 = new RequestResponseInteraction('interaction2', [], new Request(), new Response())
def pact = new RequestResponsePact(new Provider(), new Consumer(), [interaction1, interaction2])
def 'updateTestResult uses the original pact when pact is filtered '() {
given:
def filteredPact = new FilteredPact(pact, { it.description == 'interaction1' })
PactBrokerSource pactSource = new PactBrokerSource('localhost', '80', 'http')

context = new PactVerificationContext(store, extContext, Stub(TestTarget), Stub(IProviderVerifier),
Stub(ValueResolver), Stub(IProviderInfo), Stub(IConsumerInfo), interaction1, pact, [])

PactVerificationExtension extension = new PactVerificationExtension(filteredPact, pactSource, interaction1,
'service', 'consumer', mockValueResolver)
extension = new PactVerificationExtension(filteredPact, pactSource, interaction1, 'service', 'consumer',
mockValueResolver)
extension.testResultAccumulator = Mock(TestResultAccumulator)

when:
Expand All @@ -58,28 +101,7 @@ class PactVerificationExtensionSpec extends Specification {

def 'updateTestResult uses the pact itself when pact is not filtered '() {
given:
PactVerificationContext context
ExtensionContext.Store store = Stub {
get(_) >> { args ->
if (args[0] == 'interactionContext') {
context
}
}
}
ExtensionContext extContext = Stub {
getStore(_) >> store
}
def mockValueResolver = Mock(ValueResolver)

def interaction1 = new RequestResponseInteraction('interaction1', [], new Request(), new Response())
def interaction2 = new RequestResponseInteraction('interaction2', [], new Request(), new Response())
def pact = new RequestResponsePact(new Provider(), new Consumer(), [interaction1, interaction2])
PactBrokerSource pactSource = new PactBrokerSource('localhost', '80', 'http')

context = new PactVerificationContext(store, extContext, Stub(TestTarget), Stub(IProviderVerifier),
Stub(ValueResolver), Stub(IProviderInfo), Stub(IConsumerInfo), interaction1, pact, [])

PactVerificationExtension extension = new PactVerificationExtension(pact, pactSource, interaction1,
extension = new PactVerificationExtension(pact, pactSource, interaction1,
'service', 'consumer', mockValueResolver)
extension.testResultAccumulator = Mock(TestResultAccumulator)

Expand All @@ -92,28 +114,9 @@ class PactVerificationExtensionSpec extends Specification {

def 'if updateTestResult fails, throw an exception'() {
given:
PactVerificationContext context
ExtensionContext.Store store = Stub {
get(_) >> { args ->
if (args[0] == 'interactionContext') {
context
}
}
}
ExtensionContext extContext = Stub {
getStore(_) >> store
}
def mockValueResolver = Mock(ValueResolver)
pact = new RequestResponsePact(new Provider(), new Consumer(), [interaction1, interaction2], [:], pactSource)

def interaction1 = new RequestResponseInteraction('interaction1')
def interaction2 = new RequestResponseInteraction('interaction2')
PactBrokerSource pactSource = new PactBrokerSource('localhost', '80', 'http')
def pact = new RequestResponsePact(new Provider(), new Consumer(), [interaction1, interaction2 ], [:], pactSource)

context = new PactVerificationContext(store, extContext, Stub(TestTarget), Stub(IProviderVerifier),
Stub(ValueResolver), Stub(IProviderInfo), Stub(IConsumerInfo), interaction1, pact, [])

PactVerificationExtension extension = new PactVerificationExtension(pact, pactSource, interaction1,
extension = new PactVerificationExtension(pact, pactSource, interaction1,
'service', 'consumer', mockValueResolver)
extension.testResultAccumulator = Mock(TestResultAccumulator)

Expand All @@ -130,27 +133,75 @@ class PactVerificationExtensionSpec extends Specification {
@Issue('#1572')
def 'beforeEach method passes the property resolver on to the verification context'() {
given:
def map = [:]
ExtensionContext.Store store = Stub {
get(_) >> { args -> map[args[0]] }
put(_, _) >> { args -> map[args[0]] = args[1] }
}
ExtensionContext extContext = Stub {
getStore(_) >> store
}
def mockValueResolver = Mock(ValueResolver)
pact = new RequestResponsePact(new Provider(), new Consumer(), [interaction1], [:], pactSource)

def interaction1 = new RequestResponseInteraction('interaction1')
PactBrokerSource pactSource = new PactBrokerSource('localhost', '80', 'http')
def pact = new RequestResponsePact(new Provider(), new Consumer(), [interaction1], [:], pactSource)

PactVerificationExtension extension = new PactVerificationExtension(pact, pactSource, interaction1,
extension = new PactVerificationExtension(pact, pactSource, interaction1,
'service', 'consumer', mockValueResolver)

when:
extension.beforeEach(extContext)

then:
map['interactionContext'].valueResolver == mockValueResolver
contextMap['interactionContext'].valueResolver == mockValueResolver
}

def 'supports parameter test'() {
given:
context.target = target
extension = new PactVerificationExtension(pact, pactSource, interaction1, 'service', 'consumer',
mockValueResolver)
Parameter parameter = mock(Parameter)
when(parameter.getType()).thenReturn(parameterType)
ParameterContext parameterContext = Stub {
getParameter() >> parameter
}

expect:
extension.supportsParameter(parameterContext, extContext) == result

where:

parameterType | target | result
Pact | new HttpTestTarget() | true
Interaction | new HttpTestTarget() | true
ClassicHttpRequest | new HttpTestTarget() | true
ClassicHttpRequest | new HttpsTestTarget() | true
ClassicHttpRequest | new MessageTestTarget() | false
HttpRequest | new HttpTestTarget() | true
HttpRequest | new HttpsTestTarget() | true
HttpRequest | new MessageTestTarget() | false
PactVerificationContext | new HttpTestTarget() | true
ProviderVerifier | new HttpTestTarget() | true
String | new HttpTestTarget() | false
RequestData | new HttpTestTarget() | false
RequestData | new PluginTestTarget() | true
}

def 'resolve parameter test'() {
given:
extension = new PactVerificationExtension(pact, pactSource, interaction1, 'service', 'consumer',
mockValueResolver)
Parameter parameter = mock(Parameter)
when(parameter.getType()).thenReturn(parameterType)
ParameterContext parameterContext = Stub {
getParameter() >> parameter
}

contextMap['request'] = data

expect:
extension.resolveParameter(parameterContext, extContext) == result

where:

parameterType | result
Pact | pact
Interaction | interaction1
ClassicHttpRequest | classicHttpRequest
HttpRequest | classicHttpRequest
PactVerificationContext | context
ProviderVerifier | verifier
String | null
RequestData | data
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -963,7 +963,7 @@ open class ProviderVerifier @JvmOverloads constructor (
logger.debug { "Verifying interaction => $request" }
return when (val result = DefaultPluginManager.verifyInteraction(
client as CatalogueEntry,
request as InteractionVerificationData,
(request as RequestDataToBeVerified).asInteractionVerificationData(),
userConfig,
pact,
interaction
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package au.com.dius.pact.provider

import au.com.dius.pact.core.model.OptionalBody
import io.pact.plugins.jvm.core.InteractionVerificationData

/**
* Request data that is going to be used by the plugin to create the request to be verified
*/
interface RequestData {
/**
* Data for the request of the interaction
*/
val requestData: OptionalBody

/**
* Metadata associated with the request
*/
val metadata: Map<String, Any?>
}

/**
* Data used by a plugin to create a request to be verified
*/
data class RequestDataToBeVerified(
/**
* Data for the request of the interaction
*/
override val requestData: OptionalBody,

/**
* Metadata associated with the request
*/
override val metadata: Map<String, Any?>
): RequestData {
constructor(requestData: InteractionVerificationData) : this(requestData.requestData, requestData.metadata)

fun asInteractionVerificationData() = InteractionVerificationData(requestData, metadata)
}

0 comments on commit 49f4d90

Please sign in to comment.