Skip to content

Commit

Permalink
Merge branch 'master' into v4.5.x
Browse files Browse the repository at this point in the history
  • Loading branch information
rholshausen committed Feb 15, 2023
2 parents cd8e060 + 8e12557 commit 63559b8
Show file tree
Hide file tree
Showing 8 changed files with 210 additions and 87 deletions.
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,16 @@
To generate the log, run `git log --pretty='* %h - %s (%an, %ad)' TAGNAME..HEAD` replacing TAGNAME and HEAD as appropriate.

# 4.4.6 - Maintenance Release: Supports injecting request metadata from plugins into provider tests

* 461b9e348 - feat: RequestData metadata needs to be a mutable Map (Ronald Holshausen, Wed Feb 15 16:32:48 2023 +1100)
* 49f4d908e - feat: Support modifying the request metadata in the provider test before being sent to the plugin (Ronald Holshausen, Wed Feb 15 16:29:06 2023 +1100)
* a43c2ea04 - chore: check for both cases when looking for pact do not track value (Ronald Holshausen, Tue Feb 14 12:42:52 2023 +1100)
* 21ada1b2e - fix: support metadata mismatches from results from plugins (Ronald Holshausen, Wed Feb 8 13:44:49 2023 +1100)
* 1bee97d14 - feat: add support for NotEmpty matcher in V4 DSL (Ronald Holshausen, Wed Feb 8 13:41:20 2023 +1100)
* 4ac9dcd0f - chore: Upgrade plugin driver to 0.3.1 (Ronald Holshausen, Wed Feb 8 13:40:07 2023 +1100)
* e71eb4d39 - feat: Upgrade plugin driver to 0.3.0 (supports message metadata) (Ronald Holshausen, Mon Feb 6 15:12:13 2023 +1100)
* 1a7ae6822 - bump version to 4.4.6 (Ronald Holshausen, Fri Feb 3 09:17:00 2023 +1100)

# 4.4.5 - Bugfix Release

* 8c965dca6 - fix(regression): Changes for #1641 broke the use of plugin mock servers (Ronald Holshausen, Thu Feb 2 16:17:10 2023 +1100)
Expand Down
28 changes: 17 additions & 11 deletions provider/junit5/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ For details on the provider and pact source annotations, refer to the [Pact juni

You can set the test target (the object that defines the target of the test, which should point to your provider) on the
`PactVerificationContext`, but you need to do this in a before test method (annotated with `@BeforeEach`). There are three
different test targets you can use: `HttpTestTarget`, `HttpsTestTarget` and `MessageTestTarget`.
main test targets you can use: `HttpTestTarget`, `HttpsTestTarget` and `MessageTestTarget`. There is also a `PluginTestTarget`
for use when the interactions are provided by a plugin.

For example:

Expand Down Expand Up @@ -201,7 +202,8 @@ or `setStateHandlers` methods. See [StateAnnotationsOnAdditionalClassTest](https
Sometimes you may need to add things to the requests that can't be persisted in a pact file. Examples of these would be
authentication tokens, which have a small life span. The Http and Https test targets support injecting the request that
will executed into the test template method (of type `org.apache.http.HttpRequest` for versions 4.2.x and before,
`org.apache.hc.core5.http.HttpRequest` for versions 4.3.0+).
`org.apache.hc.core5.http.HttpRequest` for versions 4.3.0+), while the plugin test target supports injecting the data
that will be used to make the request into the test template method (as an instance of `au.com.dius.pact.provider.RequestData`).
You can then add things to the request before calling the `verifyInteraction()` method.

For example to add a header:
Expand All @@ -218,16 +220,17 @@ For example to add a header:

## Objects that can be injected into the test methods

You can inject the following objects into your test methods (just like the `PactVerificationContext`). They will be null if injected before the
supported phase.
You can inject the following objects into your test methods (just like the `PactVerificationContext`). They will be
null if injected before the supported phase.

| Object | Can be injected from phase | Description |
| ------ | --------------- | ----------- |
| PactVerificationContext | @BeforeEach | The context to use to execute the interaction test |
| Pact | any | The Pact model for the test |
| Interaction | any | The Interaction model for the test |
| HttpRequest | @TestTemplate | The request that is going to be executed (only for HTTP and HTTPS targets) |
| ProviderVerifier | @TestTemplate | The verifier instance that is used to verify the interaction |
| Object | Can be injected from phase | Description |
|-------------------------|----------------------------|----------------------------------------------------------------------------|
| PactVerificationContext | @BeforeEach | The context to use to execute the interaction test |
| Pact | any | The Pact model for the test |
| Interaction | any | The Interaction model for the test |
| HttpRequest | @TestTemplate | The request that is going to be executed (only for HTTP and HTTPS targets) |
| RequestData | @TestTemplate | The request data that is going to be executed (only for the Plugin target) |
| ProviderVerifier | @TestTemplate | The verifier instance that is used to verify the interaction |

## Allowing the test to pass when no pacts are found to verify (version 4.0.7+)

Expand Down Expand Up @@ -327,6 +330,9 @@ instructions for each plugin, but the default is to unpack the plugin into a sub
(i.e., for the Protobuf plugin 0.0.0 it will be `protobuf-0.0.0`). The plugin manifest file must be present for the
plugin to be able to be loaded.

Note that the request data used to generate the request for verification can be injected into the test template method
using the `au.com.dius.pact.provider.RequestData` type. This can be used to add any required metadata to the request.

# Test Analytics

We are tracking anonymous analytics to gather important usage statistics like JVM version
Expand Down
2 changes: 2 additions & 0 deletions provider/junit5/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,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
}
}
Loading

0 comments on commit 63559b8

Please sign in to comment.