Skip to content

Commit c13a943

Browse files
author
Ronald Holshausen
committedNov 24, 2019
feat: add support for request filters with MockMvcTarget #983
1 parent a57e758 commit c13a943

File tree

5 files changed

+86
-13
lines changed

5 files changed

+86
-13
lines changed
 

‎provider/pact-jvm-provider-spring/src/main/kotlin/au/com/dius/pact/provider/spring/MvcProviderVerifier.kt

+49-5
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,15 @@ package au.com.dius.pact.provider.spring
22

33
import au.com.dius.pact.core.model.Request
44
import au.com.dius.pact.core.model.RequestResponseInteraction
5+
import au.com.dius.pact.provider.ProviderClient
56
import au.com.dius.pact.provider.ProviderInfo
67
import au.com.dius.pact.provider.ProviderVerifier
8+
import groovy.lang.Binding
9+
import groovy.lang.Closure
10+
import groovy.lang.GroovyShell
711
import mu.KLogging
812
import org.apache.commons.lang3.StringUtils
13+
import org.hamcrest.Matchers.anything
914
import org.springframework.http.HttpHeaders
1015
import org.springframework.http.HttpMethod
1116
import org.springframework.http.MediaType
@@ -15,16 +20,20 @@ import org.springframework.test.web.servlet.MockMvc
1520
import org.springframework.test.web.servlet.MvcResult
1621
import org.springframework.test.web.servlet.RequestBuilder
1722
import org.springframework.test.web.servlet.ResultActions
23+
import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder
1824
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders
1925
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.asyncDispatch
2026
import org.springframework.test.web.servlet.result.MockMvcResultHandlers
2127
import org.springframework.test.web.servlet.result.MockMvcResultMatchers.request
2228
import org.springframework.web.util.UriComponentsBuilder
29+
import scala.Function1
2330
import java.net.URI
31+
import java.util.concurrent.Callable
32+
import java.util.function.Consumer
33+
import java.util.function.Function
2434
import javax.mail.internet.ContentDisposition
2535
import javax.mail.internet.MimeMultipart
2636
import javax.mail.util.ByteArrayDataSource
27-
import org.hamcrest.Matchers.anything
2837

2938
/**
3039
* Verifies the providers against the defined consumers using Spring MockMvc
@@ -41,7 +50,7 @@ open class MvcProviderVerifier(private val debugRequestResponse: Boolean = false
4150
try {
4251
val request = interaction.request
4352

44-
val mvcResult = executeMockMvcRequest(mockMvc, request)
53+
val mvcResult = executeMockMvcRequest(mockMvc, request, provider)
4554

4655
val expectedResponse = interaction.response
4756
val actualResponse = handleResponse(mvcResult.response)
@@ -56,7 +65,7 @@ open class MvcProviderVerifier(private val debugRequestResponse: Boolean = false
5665
}
5766
}
5867

59-
fun executeMockMvcRequest(mockMvc: MockMvc, request: Request): MvcResult {
68+
fun executeMockMvcRequest(mockMvc: MockMvc, request: Request, provider: ProviderInfo): MvcResult {
6069
val body = request.body
6170
val requestBuilder = if (body != null && body.isPresent()) {
6271
if (request.isMultipartFileUpload()) {
@@ -81,11 +90,46 @@ open class MvcProviderVerifier(private val debugRequestResponse: Boolean = false
8190
MockMvcRequestBuilders.request(HttpMethod.valueOf(request.method), requestUriString(request))
8291
.headers(mapHeaders(request, false))
8392
}
84-
return performRequest(mockMvc, requestBuilder).andDo({
93+
94+
executeRequestFilter(requestBuilder, provider)
95+
96+
return performRequest(mockMvc, requestBuilder).andDo {
8597
if (debugRequestResponse) {
8698
MockMvcResultHandlers.print().handle(it)
8799
}
88-
}).andReturn()
100+
}.andReturn()
101+
}
102+
103+
private fun executeRequestFilter(requestBuilder: MockHttpServletRequestBuilder, provider: ProviderInfo) {
104+
val requestFilter = provider.requestFilter
105+
if (requestFilter != null) {
106+
when (requestFilter) {
107+
is Closure<*> -> requestFilter.call(requestBuilder)
108+
is Function1<*, *> -> (requestFilter as Function1<MockHttpServletRequestBuilder, *>).apply(requestBuilder)
109+
is org.apache.commons.collections4.Closure<*> ->
110+
(requestFilter as org.apache.commons.collections4.Closure<Any>).execute(requestBuilder)
111+
else -> {
112+
if (ProviderClient.isFunctionalInterface(requestFilter)) {
113+
invokeJavaFunctionalInterface(requestFilter, requestBuilder)
114+
} else {
115+
val binding = Binding()
116+
binding.setVariable(ProviderClient.REQUEST, requestBuilder)
117+
val shell = GroovyShell(binding)
118+
shell.evaluate(requestFilter as String)
119+
}
120+
}
121+
}
122+
}
123+
}
124+
125+
private fun invokeJavaFunctionalInterface(functionalInterface: Any, requestBuilder: MockHttpServletRequestBuilder) {
126+
when (functionalInterface) {
127+
is Consumer<*> -> (functionalInterface as Consumer<MockHttpServletRequestBuilder>).accept(requestBuilder)
128+
is Function<*, *> -> (functionalInterface as Function<MockHttpServletRequestBuilder, Any?>).apply(requestBuilder)
129+
is Callable<*> -> (functionalInterface as Callable<MockHttpServletRequestBuilder>).call()
130+
else -> throw IllegalArgumentException("Java request filters must be either a Consumer or Function that " +
131+
"takes at least one MockHttpServletRequestBuilder parameter")
132+
}
89133
}
90134

91135
private fun performRequest(mockMvc: MockMvc, requestBuilder: RequestBuilder): ResultActions {

‎provider/pact-jvm-provider-spring/src/main/kotlin/au/com/dius/pact/provider/spring/target/MockMvcTarget.kt

+3-3
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,9 @@ import au.com.dius.pact.provider.junit.TargetRequestFilter
1212
import au.com.dius.pact.provider.junit.target.BaseTarget
1313
import au.com.dius.pact.provider.junit.target.Target
1414
import au.com.dius.pact.provider.spring.MvcProviderVerifier
15-
import org.apache.http.HttpRequest
1615
import org.springframework.http.converter.HttpMessageConverter
1716
import org.springframework.test.web.servlet.MockMvc
17+
import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder
1818
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders
1919
import org.springframework.test.web.servlet.setup.MockMvcBuilders.standaloneSetup
2020
import org.springframework.test.web.servlet.setup.StandaloneMockMvcBuilder
@@ -69,7 +69,7 @@ class MockMvcTarget @JvmOverloads constructor(
6969

7070
val failures = HashMap<String, Any>()
7171

72-
1.rangeTo(runTimes).forEach {
72+
repeat(1.rangeTo(runTimes).count()) {
7373
verifier.verifyResponseFromProvider(provider, interaction as RequestResponseInteraction, interaction.description,
7474
failures, mockMvc)
7575
}
@@ -131,7 +131,7 @@ class MockMvcTarget @JvmOverloads constructor(
131131

132132
val methods = testClass.getAnnotatedMethods(TargetRequestFilter::class.java)
133133
if (methods.isNotEmpty()) {
134-
providerInfo.requestFilter = Consumer<HttpRequest> { httpRequest ->
134+
providerInfo.requestFilter = Consumer<MockHttpServletRequestBuilder> { httpRequest ->
135135
methods.forEach { method ->
136136
try {
137137
method.invokeExplosively(testTarget, httpRequest)

‎provider/pact-jvm-provider-spring/src/test/groovy/au/com/dius/pact/provider/spring/MvcProviderVerifierSpec.groovy

+4-3
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package au.com.dius.pact.provider.spring
22

33
import au.com.dius.pact.core.model.OptionalBody
44
import au.com.dius.pact.core.model.Request
5+
import au.com.dius.pact.provider.ProviderInfo
56
import org.springframework.test.web.servlet.MockMvc
67
import org.springframework.web.bind.annotation.RequestBody
78
import org.springframework.web.bind.annotation.RequestMapping
@@ -41,7 +42,7 @@ class MvcProviderVerifierSpec extends Specification {
4142
def request = new Request(body: OptionalBody.body(body.bytes))
4243

4344
when:
44-
def response = verifier.executeMockMvcRequest(mockMvc, request)
45+
def response = verifier.executeMockMvcRequest(mockMvc, request, new ProviderInfo())
4546

4647
then:
4748
response.response.contentType == 'text/plain;charset=ISO-8859-1'
@@ -53,7 +54,7 @@ class MvcProviderVerifierSpec extends Specification {
5354
def request = new Request()
5455

5556
when:
56-
def response = verifier.executeMockMvcRequest(mockMvc, request)
57+
def response = verifier.executeMockMvcRequest(mockMvc, request, new ProviderInfo())
5758

5859
then:
5960
response.response.contentType == null
@@ -65,7 +66,7 @@ class MvcProviderVerifierSpec extends Specification {
6566
def request = new Request(path: '/upload').withMultipartFileUpload('file', 'filename', 'text/csv', 'file,contents')
6667

6768
when:
68-
def response = verifier.executeMockMvcRequest(mockMvc, request)
69+
def response = verifier.executeMockMvcRequest(mockMvc, request, new ProviderInfo())
6970

7071
then:
7172
response.response.contentType == 'text/plain;charset=ISO-8859-1'

‎provider/pact-jvm-provider-spring/src/test/groovy/au/com/dius/pact/provider/spring/target/MockMvcTargetSpec.groovy

+29-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,11 @@ package au.com.dius.pact.provider.spring.target
33
import au.com.dius.pact.core.model.RequestResponseInteraction
44
import au.com.dius.pact.core.model.UnknownPactSource
55
import au.com.dius.pact.provider.junit.Provider
6+
import au.com.dius.pact.provider.junit.TargetRequestFilter
7+
import groovy.transform.CompileStatic
68
import org.junit.runners.model.TestClass
9+
import org.springframework.http.MediaType
10+
import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder
711
import org.springframework.web.bind.annotation.RequestMapping
812
import org.springframework.web.bind.annotation.RestController
913
import spock.lang.Specification
@@ -14,11 +18,20 @@ class MockMvcTargetSpec extends Specification {
1418
private MockMvcTarget mockMvcTarget
1519

1620
@RestController
17-
class TestController {
21+
static class TestController {
1822
@RequestMapping('/')
1923
String test() { 'test' }
2024
}
2125

26+
@Provider('testProvider')
27+
@CompileStatic
28+
static class TestClassWithFilter {
29+
@TargetRequestFilter
30+
void requestFilter(MockHttpServletRequestBuilder request) {
31+
request.header('X-Content-Type', MediaType.APPLICATION_ATOM_XML)
32+
}
33+
}
34+
2235
def setup() {
2336
mockMvcTarget = new MockMvcTarget()
2437
}
@@ -38,4 +51,19 @@ class MockMvcTargetSpec extends Specification {
3851
1 * controller.test()
3952
}
4053

54+
def 'invokes any request filter'() {
55+
given:
56+
def testInstance = Spy(TestClassWithFilter)
57+
mockMvcTarget.setTestClass(new TestClass(TestClassWithFilter), testInstance)
58+
def interaction = new RequestResponseInteraction('Test Interaction')
59+
def controller = Mock(TestController)
60+
mockMvcTarget.controllers = [ controller ]
61+
62+
when:
63+
mockMvcTarget.testInteraction('testConsumer', interaction, UnknownPactSource.INSTANCE, [:])
64+
65+
then:
66+
1 * testInstance.requestFilter(_)
67+
}
68+
4169
}

‎provider/pact-jvm-provider/src/main/kotlin/au/com/dius/pact/provider/ProviderClient.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,7 @@ open class ProviderClient(
198198
fun urlEncodedFormPost(request: Request) = request.method.toLowerCase() == "post" &&
199199
request.mimeType() == ContentType.APPLICATION_FORM_URLENCODED.mimeType
200200

201-
private fun isFunctionalInterface(requestFilter: Any) =
201+
fun isFunctionalInterface(requestFilter: Any) =
202202
requestFilter::class.java.interfaces.any { it.isAnnotationPresent(FunctionalInterface::class.java) }
203203

204204
@JvmStatic

0 commit comments

Comments
 (0)
Please sign in to comment.