Skip to content

Commit 7ec2e25

Browse files
committed
Coverage
1 parent 675e700 commit 7ec2e25

File tree

8 files changed

+428
-35
lines changed

8 files changed

+428
-35
lines changed

build.gradle.kts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
22

33
val springBootVersion = "3.2.3"
4+
val junitPlatformVersion = "1.10.2"
45
val jsonPathVersion = "2.9.0" // override transitive dep due to CVE violation
56
val embeddedRedisVersion = "1.4.1"
67
val mockkVersion = "1.13.9"
@@ -60,6 +61,7 @@ dependencies {
6061
compileOnly("org.junit.jupiter:junit-jupiter-api")
6162
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
6263
testImplementation("org.junit.jupiter:junit-jupiter")
64+
testImplementation("org.junit.platform:junit-platform-testkit")
6365
testImplementation("org.springframework.boot:spring-boot-starter-data-redis")
6466
testImplementation("org.springframework.boot:spring-boot-starter-test")
6567
testImplementation("com.jayway.jsonpath:json-path:$jsonPathVersion") // override transitive dep due to CVE violation
@@ -76,6 +78,7 @@ tasks.withType<KotlinCompile> {
7678

7779
tasks.withType<Test> {
7880
useJUnitPlatform()
81+
exclude("**/RedisValidationExtensionTest$*")
7982
}
8083

8184
tasks.jacocoTestReport {

src/main/kotlin/io/github/tobi/laa/spring/boot/embedded/redis/FindTestClassAnnotation.kt

Lines changed: 0 additions & 17 deletions
This file was deleted.
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package io.github.tobi.laa.spring.boot.embedded.redis
2+
3+
import org.springframework.core.annotation.MergedAnnotations.SearchStrategy.TYPE_HIERARCHY
4+
import org.springframework.core.annotation.MergedAnnotations.search
5+
import org.springframework.test.context.TestContextAnnotationUtils.searchEnclosingClass
6+
7+
8+
internal inline fun <reified T : Annotation> findTestClassAnnotations(
9+
testClass: Class<*>,
10+
annotationType: Class<T>
11+
): List<T> {
12+
var clazz = testClass
13+
val mergedAnnotations = mutableListOf(search(TYPE_HIERARCHY).from(testClass))
14+
while (searchEnclosingClass(clazz) && clazz.enclosingClass != null) {
15+
clazz = clazz.enclosingClass
16+
mergedAnnotations += search(TYPE_HIERARCHY).from(clazz)
17+
}
18+
return mergedAnnotations
19+
.stream()
20+
.flatMap { it.stream(annotationType) }
21+
.filter { it != null }
22+
.map { it.synthesize() }
23+
.toList()
24+
}
Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,45 @@
11
package io.github.tobi.laa.spring.boot.embedded.redis.junit.extension
22

33
import io.github.tobi.laa.spring.boot.embedded.redis.cluster.EmbeddedRedisCluster
4+
import io.github.tobi.laa.spring.boot.embedded.redis.findTestClassAnnotations
45
import io.github.tobi.laa.spring.boot.embedded.redis.server.EmbeddedRedisServer
56
import io.github.tobi.laa.spring.boot.embedded.redis.shardedcluster.EmbeddedRedisShardedCluster
67
import org.junit.jupiter.api.extension.BeforeAllCallback
78
import org.junit.jupiter.api.extension.ExtensionContext
9+
import java.util.stream.Stream
810
import kotlin.reflect.KClass
911

1012
/**
1113
* JUnit 5 extension to validate that the API is used correctly.
1214
*/
1315
internal class RedisValidationExtension : BeforeAllCallback {
1416

15-
private val VALID_PORT_RANGE = 0..65535
17+
private val validPortRange = 0..65535
1618

1719
override fun beforeAll(context: ExtensionContext?) {
1820
val embeddedRedisServer = annotation(context, EmbeddedRedisServer::class.java)
1921
val embeddedRedisCluster = annotation(context, EmbeddedRedisCluster::class.java)
2022
val embeddedRedisShardedCluster = annotation(context, EmbeddedRedisShardedCluster::class.java)
21-
validateMutuallyExclusive(embeddedRedisServer, embeddedRedisCluster, embeddedRedisShardedCluster)
23+
validateMutuallyExclusive(context)
2224
embeddedRedisServer?.let { validateServer(it) }
2325
embeddedRedisCluster?.let { validateCluster(it) }
2426
embeddedRedisShardedCluster?.let { validateShardedCluster(it) }
2527
}
2628

27-
private fun validateMutuallyExclusive(
28-
embeddedRedisServer: EmbeddedRedisServer?,
29-
embeddedRedisCluster: EmbeddedRedisCluster?,
30-
embeddedRedisShardedCluster: EmbeddedRedisShardedCluster?
31-
) {
32-
val count = listOfNotNull(embeddedRedisServer, embeddedRedisCluster, embeddedRedisShardedCluster).count()
29+
private fun validateMutuallyExclusive(context: ExtensionContext?) {
30+
val count = Stream.of(
31+
annotations(context, EmbeddedRedisServer::class.java),
32+
annotations(context, EmbeddedRedisCluster::class.java),
33+
annotations(context, EmbeddedRedisShardedCluster::class.java)
34+
)
35+
.flatMap { it.stream() }
36+
.filter { it != null }
37+
.count()
3338
require(count <= 1) { "Only one of @EmbeddedRedisServer, @EmbeddedRedisCluster, @EmbeddedRedisShardedCluster is allowed" }
3439
}
3540

3641
private fun validateServer(config: EmbeddedRedisServer) {
37-
require(config.port in VALID_PORT_RANGE) { "Port must be in range $VALID_PORT_RANGE" }
42+
require(config.port in validPortRange) { "Port must be in range $validPortRange" }
3843
require(config.configFile.isEmpty() || config.settings.isEmpty()) { "Either 'configFile' or 'settings' can be set, but not both" }
3944
validateCustomizers(config.customizer)
4045
}
@@ -46,7 +51,7 @@ internal class RedisValidationExtension : BeforeAllCallback {
4651
private fun validateShardedCluster(config: EmbeddedRedisShardedCluster) {
4752
require(config.shards.all { it.replicas > 0 }) { "Replicas for all shards must be greater than 0" }
4853
require(config.ports.isEmpty() || config.ports.size == config.shards.sumOf { it.replicas + 1 }) { "If ports are specified, they must match the number of nodes" }
49-
require(config.ports.all { it in VALID_PORT_RANGE }) { "All ports must be in range $VALID_PORT_RANGE" }
54+
require(config.ports.all { it in validPortRange }) { "All ports must be in range $validPortRange" }
5055
require(config.initializationTimeout > 0) { "Initialization timeout must be greater than 0" }
5156
validateCustomizers(config.customizer)
5257
}
@@ -59,7 +64,11 @@ internal class RedisValidationExtension : BeforeAllCallback {
5964
return customizer.constructors.any { it.parameters.isEmpty() }
6065
}
6166

62-
private fun <A : Annotation> annotation(extensionContext: ExtensionContext?, type: Class<A>): A? {
63-
return extensionContext!!.requiredTestClass.getAnnotation(type)
67+
private inline fun <reified A : Annotation> annotation(context: ExtensionContext?, type: Class<A>): A? {
68+
return annotations(context, type).firstOrNull()
69+
}
70+
71+
private inline fun <reified A : Annotation> annotations(context: ExtensionContext?, type: Class<A>): List<A> {
72+
return findTestClassAnnotations(context!!.requiredTestClass, type)
6473
}
6574
}

src/main/kotlin/io/github/tobi/laa/spring/boot/embedded/redis/server/RedisServerContextCustomizerFactory.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package io.github.tobi.laa.spring.boot.embedded.redis.server
22

3-
import io.github.tobi.laa.spring.boot.embedded.redis.findTestClassAnnotation
3+
import io.github.tobi.laa.spring.boot.embedded.redis.findTestClassAnnotations
44
import org.springframework.test.context.ContextConfigurationAttributes
55
import org.springframework.test.context.ContextCustomizer
66
import org.springframework.test.context.ContextCustomizerFactory
@@ -11,7 +11,7 @@ internal class RedisServerContextCustomizerFactory : ContextCustomizerFactory {
1111
testClass: Class<*>,
1212
configAttributes: MutableList<ContextConfigurationAttributes>
1313
): ContextCustomizer {
14-
val embeddedRedisServer = findTestClassAnnotation(testClass, EmbeddedRedisServer::class.java)
14+
val embeddedRedisServer = findTestClassAnnotations(testClass, EmbeddedRedisServer::class.java).firstOrNull()
1515
return RedisServerContextCustomizer(embeddedRedisServer!!)
1616
}
1717
}

src/main/kotlin/io/github/tobi/laa/spring/boot/embedded/redis/shardedcluster/RedisShardedClusterContextCustomizer.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,10 +60,10 @@ internal class RedisShardedClusterContextCustomizer(
6060

6161
private fun ports(): List<Int> {
6262
return if (config.ports.isEmpty()) {
63-
val nOfNodes = config.shards.map { it.replicas + 1 }.sum()
63+
val nOfNodes = config.shards.sumOf { it.replicas + 1 }
6464
range(0, nOfNodes).map { _ -> portProvider.next() }.toList()
6565
} else {
66-
config.ports.asList()
66+
config.ports.map { if (it == 0) portProvider.next() else it }.toList()
6767
}
6868
}
6969

src/main/kotlin/io/github/tobi/laa/spring/boot/embedded/redis/shardedcluster/RedisShardedClusterContextCustomizerFactory.kt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package io.github.tobi.laa.spring.boot.embedded.redis.shardedcluster
22

3-
import io.github.tobi.laa.spring.boot.embedded.redis.findTestClassAnnotation
3+
import io.github.tobi.laa.spring.boot.embedded.redis.findTestClassAnnotations
44
import org.springframework.test.context.ContextConfigurationAttributes
55
import org.springframework.test.context.ContextCustomizer
66
import org.springframework.test.context.ContextCustomizerFactory
@@ -11,7 +11,8 @@ internal class RedisShardedClusterContextCustomizerFactory : ContextCustomizerFa
1111
testClass: Class<*>,
1212
configAttributes: MutableList<ContextConfigurationAttributes>
1313
): ContextCustomizer {
14-
val embeddedRedisShardedCluster = findTestClassAnnotation(testClass, EmbeddedRedisShardedCluster::class.java)
14+
val embeddedRedisShardedCluster =
15+
findTestClassAnnotations(testClass, EmbeddedRedisShardedCluster::class.java).firstOrNull()
1516
return RedisShardedClusterContextCustomizer(embeddedRedisShardedCluster!!)
1617
}
1718
}

0 commit comments

Comments
 (0)