Skip to content

Commit 49f4d90

Browse files
committed
feat: Support modifying the request metadata in the provider test before being sent to the plugin
1 parent a43c2ea commit 49f4d90

File tree

6 files changed

+180
-76
lines changed

6 files changed

+180
-76
lines changed

provider/junit5/build.gradle

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,4 +28,6 @@ dependencies {
2828
exclude group: 'org.yaml'
2929
}
3030
testImplementation 'org.yaml:snakeyaml:1.33'
31+
testImplementation 'org.mockito:mockito-core:2.28.2'
32+
testImplementation 'org.mockito:mockito-inline:2.28.2'
3133
}

provider/junit5/src/main/kotlin/au/com/dius/pact/provider/junit5/PactVerificationExtension.kt

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,12 @@ import au.com.dius.pact.provider.DefaultTestResultAccumulator
1414
import au.com.dius.pact.provider.IProviderVerifier
1515
import au.com.dius.pact.provider.ProviderInfo
1616
import au.com.dius.pact.provider.ProviderVerifier
17+
import au.com.dius.pact.provider.RequestData
18+
import au.com.dius.pact.provider.RequestDataToBeVerified
1719
import au.com.dius.pact.provider.TestResultAccumulator
1820
import au.com.dius.pact.provider.junitsupport.VerificationReports
1921
import au.com.dius.pact.provider.reporters.ReporterManager
22+
import io.pact.plugins.jvm.core.InteractionVerificationData
2023
import mu.KLogging
2124
import org.apache.hc.core5.http.ClassicHttpRequest
2225
import org.apache.hc.core5.http.HttpRequest
@@ -74,9 +77,10 @@ open class PactVerificationExtension(
7477
return when (parameterContext.parameter.type) {
7578
Pact::class.java -> true
7679
Interaction::class.java -> true
77-
ClassicHttpRequest::class.java, HttpRequest::class.java -> testContext.target is HttpTestTarget || testContext.target is HttpsTestTarget
80+
ClassicHttpRequest::class.java, HttpRequest::class.java -> testContext.target is HttpTestTarget
7881
PactVerificationContext::class.java -> true
7982
ProviderVerifier::class.java -> true
83+
RequestData::class.java -> testContext.target is PluginTestTarget
8084
else -> false
8185
}
8286
}
@@ -89,6 +93,14 @@ open class PactVerificationExtension(
8993
ClassicHttpRequest::class.java, HttpRequest::class.java -> store.get("httpRequest")
9094
PactVerificationContext::class.java -> store.get("interactionContext")
9195
ProviderVerifier::class.java -> store.get("verifier")
96+
RequestData::class.java -> {
97+
val request = store.get("request")
98+
if (request is RequestDataToBeVerified) {
99+
request
100+
} else {
101+
null
102+
}
103+
}
92104
else -> null
93105
}
94106
}

provider/junit5/src/main/kotlin/au/com/dius/pact/provider/junit5/PluginTestTarget.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import au.com.dius.pact.provider.IProviderInfo
1010
import au.com.dius.pact.provider.IProviderVerifier
1111
import au.com.dius.pact.provider.PactVerification
1212
import au.com.dius.pact.provider.ProviderResponse
13+
import au.com.dius.pact.provider.RequestDataToBeVerified
1314
import io.pact.plugins.jvm.core.CatalogueEntry
1415
import io.pact.plugins.jvm.core.CatalogueManager
1516
import io.pact.plugins.jvm.core.DefaultPluginManager
@@ -87,7 +88,7 @@ class PluginTestTarget(private val config: MutableMap<String, Any?> = mutableMap
8788
return when (val v4pact = pact.asV4Pact()) {
8889
is Ok -> when (val result = DefaultPluginManager.prepareValidationForInteraction(transportEntry, v4pact.value,
8990
interaction.asV4Interaction(), config)) {
90-
is Ok -> result.value to transportEntry
91+
is Ok -> RequestDataToBeVerified(result.value) to transportEntry
9192
is Err -> throw RuntimeException("Failed to configure the interaction for verification - ${result.error}")
9293
}
9394
is Err -> throw RuntimeException("PluginTestTarget can only be used with V4 Pacts")

provider/junit5/src/test/groovy/au/com/dius/pact/provider/junit5/PactVerificationExtensionSpec.groovy

Lines changed: 124 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@ package au.com.dius.pact.provider.junit5
22

33
import au.com.dius.pact.core.model.Consumer
44
import au.com.dius.pact.core.model.FilteredPact
5+
import au.com.dius.pact.core.model.Interaction
6+
import au.com.dius.pact.core.model.OptionalBody
7+
import au.com.dius.pact.core.model.Pact
58
import au.com.dius.pact.core.model.PactBrokerSource
69
import au.com.dius.pact.core.model.Provider
710
import au.com.dius.pact.core.model.Request
@@ -13,39 +16,79 @@ import au.com.dius.pact.core.support.expressions.ValueResolver
1316
import au.com.dius.pact.provider.IConsumerInfo
1417
import au.com.dius.pact.provider.IProviderInfo
1518
import au.com.dius.pact.provider.IProviderVerifier
19+
import au.com.dius.pact.provider.ProviderVerifier
20+
import au.com.dius.pact.provider.RequestData
21+
import au.com.dius.pact.provider.RequestDataToBeVerified
1622
import au.com.dius.pact.provider.TestResultAccumulator
23+
import org.apache.hc.core5.http.ClassicHttpRequest
24+
import org.apache.hc.core5.http.HttpRequest
1725
import org.junit.jupiter.api.extension.ExtensionContext
26+
import org.junit.jupiter.api.extension.ParameterContext
1827
import spock.lang.Issue
28+
import spock.lang.Shared
1929
import spock.lang.Specification
2030

21-
class PactVerificationExtensionSpec extends Specification {
31+
import java.lang.reflect.Parameter
2232

23-
def 'updateTestResult uses the original pact when pact is filtered '() {
24-
given:
25-
PactVerificationContext context
26-
ExtensionContext.Store store = Stub {
33+
import static org.mockito.Mockito.mock
34+
import static org.mockito.Mockito.when
35+
36+
@SuppressWarnings('UnnecessaryGetter')
37+
class PactVerificationExtensionSpec extends Specification {
38+
@Shared PactVerificationContext context
39+
PactVerificationExtension extension
40+
@Shared ExtensionContext.Store store
41+
@Shared ExtensionContext extContext
42+
@Shared Map<String, Object> contextMap
43+
ValueResolver mockValueResolver
44+
@Shared RequestResponseInteraction interaction1, interaction2
45+
@Shared RequestResponsePact pact
46+
PactBrokerSource pactSource
47+
@Shared ClassicHttpRequest classicHttpRequest
48+
@Shared ProviderVerifier verifier
49+
@Shared RequestDataToBeVerified data
50+
51+
def setupSpec() {
52+
verifier = Mock(ProviderVerifier)
53+
store = Stub {
2754
get(_) >> { args ->
2855
if (args[0] == 'interactionContext') {
2956
context
57+
} else {
58+
contextMap[args[0]]
3059
}
3160
}
61+
put(_, _) >> { args -> contextMap[args[0]] = args[1] }
3262
}
33-
ExtensionContext extContext = Stub {
63+
64+
extContext = Stub {
3465
getStore(_) >> store
3566
}
36-
def mockValueResolver = Mock(ValueResolver)
67+
interaction1 = new RequestResponseInteraction('interaction1', [], new Request(), new Response())
68+
interaction2 = new RequestResponseInteraction('interaction2', [], new Request(), new Response())
69+
pact = new RequestResponsePact(new Provider(), new Consumer(), [interaction1, interaction2])
70+
classicHttpRequest = Mock(ClassicHttpRequest)
71+
context = new PactVerificationContext(store, extContext, Stub(TestTarget), Stub(IProviderVerifier),
72+
Stub(ValueResolver), Stub(IProviderInfo), Stub(IConsumerInfo), interaction1, pact, [])
73+
data = new RequestDataToBeVerified(OptionalBody.empty(), [:])
74+
}
75+
76+
def setup() {
77+
mockValueResolver = Mock(ValueResolver)
78+
pactSource = new PactBrokerSource('localhost', '80', 'http')
79+
contextMap = [
80+
httpRequest: classicHttpRequest,
81+
verifier: verifier
82+
]
83+
}
3784

38-
def interaction1 = new RequestResponseInteraction('interaction1', [], new Request(), new Response())
39-
def interaction2 = new RequestResponseInteraction('interaction2', [], new Request(), new Response())
40-
def pact = new RequestResponsePact(new Provider(), new Consumer(), [interaction1, interaction2])
85+
def 'updateTestResult uses the original pact when pact is filtered '() {
86+
given:
4187
def filteredPact = new FilteredPact(pact, { it.description == 'interaction1' })
4288
PactBrokerSource pactSource = new PactBrokerSource('localhost', '80', 'http')
4389

44-
context = new PactVerificationContext(store, extContext, Stub(TestTarget), Stub(IProviderVerifier),
45-
Stub(ValueResolver), Stub(IProviderInfo), Stub(IConsumerInfo), interaction1, pact, [])
46-
47-
PactVerificationExtension extension = new PactVerificationExtension(filteredPact, pactSource, interaction1,
48-
'service', 'consumer', mockValueResolver)
90+
extension = new PactVerificationExtension(filteredPact, pactSource, interaction1, 'service', 'consumer',
91+
mockValueResolver)
4992
extension.testResultAccumulator = Mock(TestResultAccumulator)
5093

5194
when:
@@ -58,28 +101,7 @@ class PactVerificationExtensionSpec extends Specification {
58101

59102
def 'updateTestResult uses the pact itself when pact is not filtered '() {
60103
given:
61-
PactVerificationContext context
62-
ExtensionContext.Store store = Stub {
63-
get(_) >> { args ->
64-
if (args[0] == 'interactionContext') {
65-
context
66-
}
67-
}
68-
}
69-
ExtensionContext extContext = Stub {
70-
getStore(_) >> store
71-
}
72-
def mockValueResolver = Mock(ValueResolver)
73-
74-
def interaction1 = new RequestResponseInteraction('interaction1', [], new Request(), new Response())
75-
def interaction2 = new RequestResponseInteraction('interaction2', [], new Request(), new Response())
76-
def pact = new RequestResponsePact(new Provider(), new Consumer(), [interaction1, interaction2])
77-
PactBrokerSource pactSource = new PactBrokerSource('localhost', '80', 'http')
78-
79-
context = new PactVerificationContext(store, extContext, Stub(TestTarget), Stub(IProviderVerifier),
80-
Stub(ValueResolver), Stub(IProviderInfo), Stub(IConsumerInfo), interaction1, pact, [])
81-
82-
PactVerificationExtension extension = new PactVerificationExtension(pact, pactSource, interaction1,
104+
extension = new PactVerificationExtension(pact, pactSource, interaction1,
83105
'service', 'consumer', mockValueResolver)
84106
extension.testResultAccumulator = Mock(TestResultAccumulator)
85107

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

93115
def 'if updateTestResult fails, throw an exception'() {
94116
given:
95-
PactVerificationContext context
96-
ExtensionContext.Store store = Stub {
97-
get(_) >> { args ->
98-
if (args[0] == 'interactionContext') {
99-
context
100-
}
101-
}
102-
}
103-
ExtensionContext extContext = Stub {
104-
getStore(_) >> store
105-
}
106-
def mockValueResolver = Mock(ValueResolver)
117+
pact = new RequestResponsePact(new Provider(), new Consumer(), [interaction1, interaction2], [:], pactSource)
107118

108-
def interaction1 = new RequestResponseInteraction('interaction1')
109-
def interaction2 = new RequestResponseInteraction('interaction2')
110-
PactBrokerSource pactSource = new PactBrokerSource('localhost', '80', 'http')
111-
def pact = new RequestResponsePact(new Provider(), new Consumer(), [interaction1, interaction2 ], [:], pactSource)
112-
113-
context = new PactVerificationContext(store, extContext, Stub(TestTarget), Stub(IProviderVerifier),
114-
Stub(ValueResolver), Stub(IProviderInfo), Stub(IConsumerInfo), interaction1, pact, [])
115-
116-
PactVerificationExtension extension = new PactVerificationExtension(pact, pactSource, interaction1,
119+
extension = new PactVerificationExtension(pact, pactSource, interaction1,
117120
'service', 'consumer', mockValueResolver)
118121
extension.testResultAccumulator = Mock(TestResultAccumulator)
119122

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

143-
def interaction1 = new RequestResponseInteraction('interaction1')
144-
PactBrokerSource pactSource = new PactBrokerSource('localhost', '80', 'http')
145-
def pact = new RequestResponsePact(new Provider(), new Consumer(), [interaction1], [:], pactSource)
146-
147-
PactVerificationExtension extension = new PactVerificationExtension(pact, pactSource, interaction1,
138+
extension = new PactVerificationExtension(pact, pactSource, interaction1,
148139
'service', 'consumer', mockValueResolver)
149140

150141
when:
151142
extension.beforeEach(extContext)
152143

153144
then:
154-
map['interactionContext'].valueResolver == mockValueResolver
145+
contextMap['interactionContext'].valueResolver == mockValueResolver
146+
}
147+
148+
def 'supports parameter test'() {
149+
given:
150+
context.target = target
151+
extension = new PactVerificationExtension(pact, pactSource, interaction1, 'service', 'consumer',
152+
mockValueResolver)
153+
Parameter parameter = mock(Parameter)
154+
when(parameter.getType()).thenReturn(parameterType)
155+
ParameterContext parameterContext = Stub {
156+
getParameter() >> parameter
157+
}
158+
159+
expect:
160+
extension.supportsParameter(parameterContext, extContext) == result
161+
162+
where:
163+
164+
parameterType | target | result
165+
Pact | new HttpTestTarget() | true
166+
Interaction | new HttpTestTarget() | true
167+
ClassicHttpRequest | new HttpTestTarget() | true
168+
ClassicHttpRequest | new HttpsTestTarget() | true
169+
ClassicHttpRequest | new MessageTestTarget() | false
170+
HttpRequest | new HttpTestTarget() | true
171+
HttpRequest | new HttpsTestTarget() | true
172+
HttpRequest | new MessageTestTarget() | false
173+
PactVerificationContext | new HttpTestTarget() | true
174+
ProviderVerifier | new HttpTestTarget() | true
175+
String | new HttpTestTarget() | false
176+
RequestData | new HttpTestTarget() | false
177+
RequestData | new PluginTestTarget() | true
178+
}
179+
180+
def 'resolve parameter test'() {
181+
given:
182+
extension = new PactVerificationExtension(pact, pactSource, interaction1, 'service', 'consumer',
183+
mockValueResolver)
184+
Parameter parameter = mock(Parameter)
185+
when(parameter.getType()).thenReturn(parameterType)
186+
ParameterContext parameterContext = Stub {
187+
getParameter() >> parameter
188+
}
189+
190+
contextMap['request'] = data
191+
192+
expect:
193+
extension.resolveParameter(parameterContext, extContext) == result
194+
195+
where:
196+
197+
parameterType | result
198+
Pact | pact
199+
Interaction | interaction1
200+
ClassicHttpRequest | classicHttpRequest
201+
HttpRequest | classicHttpRequest
202+
PactVerificationContext | context
203+
ProviderVerifier | verifier
204+
String | null
205+
RequestData | data
155206
}
156207
}

provider/src/main/kotlin/au/com/dius/pact/provider/ProviderVerifier.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -963,7 +963,7 @@ open class ProviderVerifier @JvmOverloads constructor (
963963
logger.debug { "Verifying interaction => $request" }
964964
return when (val result = DefaultPluginManager.verifyInteraction(
965965
client as CatalogueEntry,
966-
request as InteractionVerificationData,
966+
(request as RequestDataToBeVerified).asInteractionVerificationData(),
967967
userConfig,
968968
pact,
969969
interaction
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package au.com.dius.pact.provider
2+
3+
import au.com.dius.pact.core.model.OptionalBody
4+
import io.pact.plugins.jvm.core.InteractionVerificationData
5+
6+
/**
7+
* Request data that is going to be used by the plugin to create the request to be verified
8+
*/
9+
interface RequestData {
10+
/**
11+
* Data for the request of the interaction
12+
*/
13+
val requestData: OptionalBody
14+
15+
/**
16+
* Metadata associated with the request
17+
*/
18+
val metadata: Map<String, Any?>
19+
}
20+
21+
/**
22+
* Data used by a plugin to create a request to be verified
23+
*/
24+
data class RequestDataToBeVerified(
25+
/**
26+
* Data for the request of the interaction
27+
*/
28+
override val requestData: OptionalBody,
29+
30+
/**
31+
* Metadata associated with the request
32+
*/
33+
override val metadata: Map<String, Any?>
34+
): RequestData {
35+
constructor(requestData: InteractionVerificationData) : this(requestData.requestData, requestData.metadata)
36+
37+
fun asInteractionVerificationData() = InteractionVerificationData(requestData, metadata)
38+
}

0 commit comments

Comments
 (0)