Skip to content

Commit 67a6670

Browse files
committed
feat(JUnit5): Support multiple @MockServerConfig annotations on a provider test #1675
1 parent 89d5313 commit 67a6670

File tree

4 files changed

+345
-139
lines changed

4 files changed

+345
-139
lines changed

consumer/junit5/src/main/kotlin/au/com/dius/pact/consumer/junit5/PactConsumerTestExt.kt

Lines changed: 96 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,8 @@ import java.util.Optional
4949
import java.util.concurrent.ConcurrentHashMap
5050
import kotlin.reflect.full.findAnnotation
5151

52-
class PactConsumerTestExt : Extension, BeforeTestExecutionCallback, BeforeAllCallback, ParameterResolver, AfterTestExecutionCallback, AfterAllCallback {
52+
class PactConsumerTestExt : Extension, BeforeTestExecutionCallback, BeforeAllCallback, ParameterResolver,
53+
AfterTestExecutionCallback, AfterAllCallback {
5354

5455
private val ep: ExpressionParser = ExpressionParser()
5556

@@ -131,7 +132,7 @@ class PactConsumerTestExt : Extension, BeforeTestExecutionCallback, BeforeAllCal
131132
): Any {
132133
val pact = lookupPact(providerInfo.first, providerInfo.second, extensionContext)
133134
return if (type.isAssignableFrom(MockServer::class.java) && mockServerConfigured(extensionContext)) {
134-
setupMockServer(providerInfo.first, providerInfo.second, extensionContext)
135+
setupMockServerForProvider(providerInfo.first, providerInfo.second, extensionContext)
135136
} else when (providerInfo.first.providerType) {
136137
ProviderType.ASYNCH -> when {
137138
type.isAssignableFrom(List::class.java) -> pact.interactions
@@ -169,7 +170,8 @@ class PactConsumerTestExt : Extension, BeforeTestExecutionCallback, BeforeAllCal
169170
else -> throw UnsupportedOperationException("Could not inject parameter $type into test method")
170171
}
171172
else -> when {
172-
type.isAssignableFrom(MockServer::class.java) -> setupMockServer(providerInfo.first, providerInfo.second, extensionContext)
173+
type.isAssignableFrom(MockServer::class.java) ->
174+
setupMockServerForProvider(providerInfo.first, providerInfo.second, extensionContext)
173175
type.isAssignableFrom(RequestResponsePact::class.java) -> pact.asRequestResponsePact().unwrap()
174176
type.isAssignableFrom(V4Pact::class.java) -> pact.asV4Pact().unwrap()
175177
type.isAssignableFrom(V4Interaction.SynchronousHttp::class.java) -> {
@@ -215,20 +217,24 @@ class PactConsumerTestExt : Extension, BeforeTestExecutionCallback, BeforeAllCal
215217
providerInfo.providerType == null ||
216218
providerInfo.providerType == ProviderType.SYNCH ||
217219
providerInfo.providerType == ProviderType.UNSPECIFIED) {
218-
val mockServer = setupMockServer(providerInfo, pactMethod, context)
220+
val mockServer = setupMockServerForProvider(providerInfo, pactMethod, context)
219221
mockServer.start()
220222
mockServer.waitForServer()
221223
}
222224
}
223225
}
224226

225-
private fun setupMockServer(providerInfo: ProviderInfo, pactMethod: String, context: ExtensionContext): AbstractBaseMockServer {
227+
private fun setupMockServerForProvider(
228+
providerInfo: ProviderInfo,
229+
pactMethod: String,
230+
context: ExtensionContext
231+
): AbstractBaseMockServer {
226232
val store = context.getStore(NAMESPACE)
227233
val key = "mockServer:${providerInfo.providerName}"
228234
return when {
229235
store[key] != null -> store[key] as AbstractBaseMockServer
230236
else -> {
231-
val config = mockServerConfigFromAnnotation(context).merge(providerInfo.mockServerConfig())
237+
val config = mockServerConfigFromAnnotation(context, providerInfo).merge(providerInfo.mockServerConfig())
232238
store.put("mockServerConfig:${providerInfo.providerName}", config)
233239
val mockServer = mockServer(lookupPact(providerInfo, pactMethod, context), config)
234240
store.put(key, JUnit5MockServerSupport(mockServer))
@@ -240,26 +246,62 @@ class PactConsumerTestExt : Extension, BeforeTestExecutionCallback, BeforeAllCal
240246
private fun mockServerConfigured(extensionContext: ExtensionContext): Boolean {
241247
val mockServerConfig = AnnotationSupport.findAnnotation(extensionContext.requiredTestClass,
242248
MockServerConfig::class.java)
243-
val mockServerConfigMethod = AnnotationSupport.findAnnotation(extensionContext.requiredTestMethod,
249+
val mockServerConfigs = AnnotationSupport.findRepeatableAnnotations(extensionContext.requiredTestClass,
244250
MockServerConfig::class.java)
251+
val testMethod = extensionContext.testMethod
252+
val mockServerConfigMethod = if (testMethod.isPresent) {
253+
AnnotationSupport.findAnnotation(testMethod.get(), MockServerConfig::class.java)
254+
} else Optional.empty()
255+
val mockServerConfigMethods = if (testMethod.isPresent) {
256+
AnnotationSupport.findRepeatableAnnotations(testMethod.get(), MockServerConfig::class.java)
257+
} else emptyList()
258+
245259
return mockServerConfig != null && mockServerConfig.isPresent ||
246-
mockServerConfigMethod != null && mockServerConfigMethod.isPresent
260+
mockServerConfigs.isNotEmpty() ||
261+
mockServerConfigMethod != null && mockServerConfigMethod.isPresent ||
262+
mockServerConfigMethods.isNotEmpty()
247263
}
248264

249-
private fun mockServerConfigFromAnnotation(context: ExtensionContext): MockProviderConfig? {
250-
val mockServerConfigMethod = if (context.testMethod.isPresent)
265+
private fun mockServerConfigFromAnnotation(
266+
context: ExtensionContext,
267+
providerInfo: ProviderInfo?
268+
): MockProviderConfig? {
269+
val mockServerConfigFromMethod = if (context.testMethod.isPresent)
251270
AnnotationSupport.findAnnotation(context.testMethod.get(), MockServerConfig::class.java)
252271
else null
253-
return if (mockServerConfigMethod != null && mockServerConfigMethod.isPresent) {
254-
MockProviderConfig.fromMockServerAnnotation(mockServerConfigMethod)
255-
} else {
256-
val mockServerConfig = AnnotationSupport.findAnnotation(context.requiredTestClass,
257-
MockServerConfig::class.java)
258-
if (mockServerConfig != null && mockServerConfig.isPresent) {
272+
val mockServerConfigsFromMethod = if (context.testMethod.isPresent)
273+
AnnotationSupport.findRepeatableAnnotations(context.testMethod.get(), MockServerConfig::class.java)
274+
else emptyList()
275+
val mockServerConfig = AnnotationSupport.findAnnotation(context.requiredTestClass, MockServerConfig::class.java)
276+
val mockServerConfigs = AnnotationSupport.findRepeatableAnnotations(context.requiredTestClass,
277+
MockServerConfig::class.java)
278+
279+
return when {
280+
mockServerConfigFromMethod != null && mockServerConfigFromMethod.isPresent ->
281+
MockProviderConfig.fromMockServerAnnotation(mockServerConfigFromMethod)
282+
283+
mockServerConfig != null && mockServerConfig.isPresent ->
259284
MockProviderConfig.fromMockServerAnnotation(mockServerConfig)
260-
} else {
261-
null
285+
286+
mockServerConfigsFromMethod.isNotEmpty() -> {
287+
val config = if (providerInfo != null) {
288+
Optional.ofNullable(mockServerConfigsFromMethod.firstOrNull { it.providerName == providerInfo.providerName })
289+
} else {
290+
Optional.ofNullable(mockServerConfigsFromMethod.firstOrNull())
291+
}
292+
MockProviderConfig.fromMockServerAnnotation(config)
293+
}
294+
295+
mockServerConfigs.isNotEmpty() -> {
296+
val config = if (providerInfo != null) {
297+
Optional.ofNullable(mockServerConfigs.firstOrNull { it.providerName == providerInfo.providerName })
298+
} else {
299+
Optional.ofNullable(mockServerConfigs.firstOrNull())
300+
}
301+
MockProviderConfig.fromMockServerAnnotation(config)
262302
}
303+
304+
else -> null
263305
}
264306
}
265307

@@ -269,7 +311,8 @@ class PactConsumerTestExt : Extension, BeforeTestExecutionCallback, BeforeAllCal
269311
val providerInfo = when {
270312
store["providers"] != null -> store["providers"] as List<Pair<ProviderInfo, String>>
271313
else -> {
272-
val methodAnnotation = if (context.testMethod.isPresent && AnnotationSupport.isAnnotated(context.testMethod.get(), PactTestFor::class.java)) {
314+
val methodAnnotation = if (context.testMethod.isPresent &&
315+
AnnotationSupport.isAnnotated(context.testMethod.get(), PactTestFor::class.java)) {
273316
val testMethod = context.testMethod.get()
274317
logger.debug { "Found @PactTestFor annotation on test method $testMethod" }
275318
AnnotationSupport.findAnnotation(testMethod, PactTestFor::class.java).get()
@@ -281,7 +324,9 @@ class PactConsumerTestExt : Extension, BeforeTestExecutionCallback, BeforeAllCal
281324
logger.debug { "Found @PactTestFor annotation on test ${context.requiredTestClass}" }
282325
AnnotationSupport.findAnnotation(context.requiredTestClass, PactTestFor::class.java).get()
283326
} else if (AnnotationSupport.isAnnotated(context.requiredTestClass, Nested::class.java)) {
284-
logger.debug { "Found @Nested annotation on test class ${context.requiredTestClass}, will search the enclosing classes" }
327+
logger.debug {
328+
"Found @Nested annotation on test class ${context.requiredTestClass}, will search the enclosing classes"
329+
}
285330
val searchResult = Annotations.searchForAnnotation(context.requiredTestClass.kotlin, PactTestFor::class)
286331
if (searchResult != null) {
287332
logger.debug { "Found @PactTestFor annotation on outer $searchResult" }
@@ -293,53 +338,49 @@ class PactConsumerTestExt : Extension, BeforeTestExecutionCallback, BeforeAllCal
293338
null
294339
}
295340

296-
val mockServerConfig = mockServerConfigFromAnnotation(context)
341+
val providerInfo = when {
342+
classAnnotation != null && methodAnnotation != null ->
343+
ProviderInfo.fromAnnotation(methodAnnotation)
344+
.merge(ProviderInfo.fromAnnotation(classAnnotation))
345+
classAnnotation != null -> ProviderInfo.fromAnnotation(classAnnotation)
346+
methodAnnotation != null -> ProviderInfo.fromAnnotation(methodAnnotation)
347+
else -> {
348+
logger.warn { "No @PactTestFor annotation found on test class, using defaults" }
349+
null
350+
}
351+
}
297352

298353
val providers = when {
299-
classAnnotation != null && methodAnnotation != null -> {
300-
val provider = ProviderInfo.fromAnnotation(methodAnnotation)
301-
.merge(ProviderInfo.fromAnnotation(classAnnotation))
302-
.withMockServerConfig(mockServerConfig)
354+
providerInfo != null -> {
303355
when {
304-
methodAnnotation.pactMethods.isNotEmpty() -> {
356+
methodAnnotation != null -> if (methodAnnotation.pactMethods.isNotEmpty()) {
305357
methodAnnotation.pactMethods.map {
306358
val providerName = providerNameFromPactMethod(it, context)
307-
provider.copy(providerName = providerName) to it
359+
val provider = providerInfo.copy(providerName = providerName)
360+
val mockServerConfig = mockServerConfigFromAnnotation(context, provider)
361+
provider.withMockServerConfig(mockServerConfig) to it
308362
}
363+
} else {
364+
val mockServerConfig = mockServerConfigFromAnnotation(context, providerInfo)
365+
val provider = providerInfo.withMockServerConfig(mockServerConfig)
366+
listOf(provider to methodAnnotation.pactMethod)
309367
}
310-
classAnnotation.pactMethods.isNotEmpty() -> {
368+
classAnnotation != null -> if (classAnnotation.pactMethods.isNotEmpty()) {
311369
classAnnotation.pactMethods.map {
312370
val providerName = providerNameFromPactMethod(it, context)
313-
provider.copy(providerName = providerName) to it
371+
val provider = providerInfo.copy(providerName = providerName)
372+
val mockServerConfig = mockServerConfigFromAnnotation(context, provider)
373+
provider.withMockServerConfig(mockServerConfig) to it
314374
}
375+
} else {
376+
val mockServerConfig = mockServerConfigFromAnnotation(context, providerInfo)
377+
val provider = providerInfo.withMockServerConfig(mockServerConfig)
378+
listOf(provider to classAnnotation.pactMethod)
315379
}
316-
else -> listOf(provider to methodAnnotation.pactMethod.ifEmpty { classAnnotation.pactMethod })
317-
}
318-
}
319-
320-
classAnnotation != null -> {
321-
val annotation = ProviderInfo.fromAnnotation(classAnnotation)
322-
.withMockServerConfig(mockServerConfig)
323-
if (classAnnotation.pactMethods.isNotEmpty()) {
324-
classAnnotation.pactMethods.map {
325-
val providerName = providerNameFromPactMethod(it, context)
326-
annotation.copy(providerName = providerName) to it
327-
}
328-
} else {
329-
listOf(annotation to classAnnotation.pactMethod)
330-
}
331-
}
332-
333-
methodAnnotation != null -> {
334-
val annotation = ProviderInfo.fromAnnotation(methodAnnotation)
335-
.withMockServerConfig(mockServerConfig)
336-
if (methodAnnotation.pactMethods.isNotEmpty()) {
337-
methodAnnotation.pactMethods.map {
338-
val providerName = providerNameFromPactMethod(it, context)
339-
annotation.copy(providerName = providerName) to it
380+
else -> {
381+
logger.warn { "No @PactTestFor annotation found on test class, using defaults" }
382+
listOf(ProviderInfo() to "")
340383
}
341-
} else {
342-
listOf(annotation to methodAnnotation.pactMethod)
343384
}
344385
}
345386
else -> {

0 commit comments

Comments
 (0)