From 3d64b9d50e217c0241ce2ad11476aed072f344a8 Mon Sep 17 00:00:00 2001 From: Abhijit Sarkar Date: Thu, 12 Nov 2020 19:52:34 -0800 Subject: [PATCH] Close #1 If a user wants to use values from the Spring environment, they can create a EmbeddedRedisConfigurer bean. If not available, a new instance shall be created. --- README.md | 3 +- build.gradle.kts | 12 ++-- gradle.properties | 4 +- settings.gradle.kts | 5 ++ .../test/redis/AutoConfigureEmbeddedRedis.kt | 1 + .../test/redis/EmbeddedRedisConfigurer.kt | 3 +- .../test/redis/EmbeddedRedisLifecycle.kt | 9 ++- .../redis/AutoConfigureWithRandomPortsTest.kt | 46 +------------ .../redis/EmbeddedRedisConfigurerBeanTest.kt | 50 ++++++++++++++ .../test/redis/EmbeddedRedisConfigurerTest.kt | 69 +++++++++++++++++++ 10 files changed, 148 insertions(+), 54 deletions(-) create mode 100644 src/test/kotlin/com/asarkar/spring/test/redis/EmbeddedRedisConfigurerBeanTest.kt create mode 100644 src/test/kotlin/com/asarkar/spring/test/redis/EmbeddedRedisConfigurerTest.kt diff --git a/README.md b/README.md index d0a3d83..52f2e25 100644 --- a/README.md +++ b/README.md @@ -46,7 +46,8 @@ public class AutoConfigureWithRandomPortsTest { If you want to configure the `RedisServer` before it's started, provide an implementation for [EmbeddedRedisConfigurer](src/main/kotlin/com/asarkar/spring/test/redis/EmbeddedRedisConfigurer.kt), and set the -class name in the annotation element `serverConfigurerClass`. +class name in the annotation element `serverConfigurerClass`. If a Spring bean of this type exists, it'll be used; +otherwise, a new instance will be created using the public no-argument constructor. See KDoc for more details. diff --git a/build.gradle.kts b/build.gradle.kts index 605b04f..84522c1 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -48,9 +48,13 @@ tasks.withType { } } -tasks.withType { - outputFormat = "html" - outputDirectory = "$buildDir/javadoc" +tasks.dokkaHtml.configure { + outputDirectory.set(buildDir.resolve("javadoc")) + dokkaSourceSets.configureEach { + jdkVersion.set(8) + skipEmptyPackages.set(true) + platform.set(org.jetbrains.dokka.Platform.jvm) + } } tasks.test { @@ -71,7 +75,7 @@ val kdocJar by tasks.creating(Jar::class) { group = JavaBasePlugin.DOCUMENTATION_GROUP description = "Creates KDoc" archiveClassifier.set("javadoc") - from(tasks.dokka) + from(tasks.dokkaHtml) } tasks.jar.configure { diff --git a/gradle.properties b/gradle.properties index 1fa7ffa..ee10222 100644 --- a/gradle.properties +++ b/gradle.properties @@ -5,12 +5,12 @@ embeddedRedisVersion=0.7.3 lettuceVersion=6.0.1.RELEASE jUnitVersion=5.7.0 kotlinVersion=1.4.10 -dokkaPluginVersion=0.10.1 +dokkaPluginVersion=1.4.10.2 ktlintVersion=9.4.0 bintrayPluginVersion=1.8.5 projectGroup=com.asarkar.spring -projectVersion=1.0.0 +projectVersion=1.1.0 projectDescription=Starts a Redis server and makes the port available as Spring Boot environment property projectLabels=spring, spring-boot, redis, embedded-redis, test, integration-test licenseName=Apache-2.0 diff --git a/settings.gradle.kts b/settings.gradle.kts index 4ab3ba9..d1e0dd2 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,4 +1,9 @@ pluginManagement { + repositories { + gradlePluginPortal() + jcenter() + } + val kotlinVersion: String by settings val dokkaPluginVersion: String by settings val ktlintVersion: String by settings diff --git a/src/main/kotlin/com/asarkar/spring/test/redis/AutoConfigureEmbeddedRedis.kt b/src/main/kotlin/com/asarkar/spring/test/redis/AutoConfigureEmbeddedRedis.kt index 14ed3a3..6657c04 100644 --- a/src/main/kotlin/com/asarkar/spring/test/redis/AutoConfigureEmbeddedRedis.kt +++ b/src/main/kotlin/com/asarkar/spring/test/redis/AutoConfigureEmbeddedRedis.kt @@ -7,6 +7,7 @@ import java.lang.annotation.Inherited /** * Annotation for test classes that want to start a Redis server as part of the Spring application Context. + * * @property serverConfigurerClass [EmbeddedRedisConfigurer] implementation class. Defaults to empty string. * @property port Redis server port. Defaults to 6379. Set 0 to use a random port. * diff --git a/src/main/kotlin/com/asarkar/spring/test/redis/EmbeddedRedisConfigurer.kt b/src/main/kotlin/com/asarkar/spring/test/redis/EmbeddedRedisConfigurer.kt index 9ee7966..33dc681 100644 --- a/src/main/kotlin/com/asarkar/spring/test/redis/EmbeddedRedisConfigurer.kt +++ b/src/main/kotlin/com/asarkar/spring/test/redis/EmbeddedRedisConfigurer.kt @@ -4,7 +4,8 @@ import redis.embedded.RedisServerBuilder /** * Class that gets called with a `RedisServerBuilder` giving the user a chance to configure the server before it's - * started. Implementations must have a public no-argument constructor. + * started. If a Spring bean of this type exists, it'll be used; otherwise, a new instance will be created using + * the public no-argument constructor. * * @author Abhijit Sarkar * @since 1.0.0 diff --git a/src/main/kotlin/com/asarkar/spring/test/redis/EmbeddedRedisLifecycle.kt b/src/main/kotlin/com/asarkar/spring/test/redis/EmbeddedRedisLifecycle.kt index c040f99..487346a 100644 --- a/src/main/kotlin/com/asarkar/spring/test/redis/EmbeddedRedisLifecycle.kt +++ b/src/main/kotlin/com/asarkar/spring/test/redis/EmbeddedRedisLifecycle.kt @@ -1,6 +1,7 @@ package com.asarkar.spring.test.redis import org.slf4j.LoggerFactory +import org.springframework.beans.factory.annotation.Autowired import org.springframework.beans.factory.annotation.Value import org.springframework.context.SmartLifecycle import org.springframework.util.ReflectionUtils @@ -15,7 +16,10 @@ open class EmbeddedRedisLifecycle : SmartLifecycle { @Value("\${embedded-redis.port:-1}") var port: Int = -1 - lateinit var redisServer: RedisServer + @Autowired(required = false) + private var serverConfigurerBean: EmbeddedRedisConfigurer? = null + + private lateinit var redisServer: RedisServer override fun start() { if (isRunning || port <= 0) return @@ -31,6 +35,9 @@ open class EmbeddedRedisLifecycle : SmartLifecycle { private fun serverConfigurer(): EmbeddedRedisConfigurer? { return if (serverConfigurerClass.isNotEmpty()) { val clazz = Class.forName(serverConfigurerClass) + if (serverConfigurerBean != null) { + return serverConfigurerBean + } return try { ReflectionUtils.accessibleConstructor(clazz as Class) .newInstance() diff --git a/src/test/kotlin/com/asarkar/spring/test/redis/AutoConfigureWithRandomPortsTest.kt b/src/test/kotlin/com/asarkar/spring/test/redis/AutoConfigureWithRandomPortsTest.kt index a784f7e..7c85f6a 100644 --- a/src/test/kotlin/com/asarkar/spring/test/redis/AutoConfigureWithRandomPortsTest.kt +++ b/src/test/kotlin/com/asarkar/spring/test/redis/AutoConfigureWithRandomPortsTest.kt @@ -2,42 +2,13 @@ package com.asarkar.spring.test.redis import io.lettuce.core.RedisClient import org.assertj.core.api.Assertions.assertThat -import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.Test -import org.slf4j.LoggerFactory import org.springframework.beans.factory.annotation.Value import org.springframework.boot.test.context.SpringBootTest -import redis.embedded.RedisServerBuilder -import java.nio.file.Files -import java.nio.file.Paths -import kotlin.random.Random - -private val charPool: List = ('a'..'z') + ('A'..'Z') + ('0'..'9') -private val file = Paths.get(AutoConfigureWithRandomPortsTest::class.java.getResource("/").toURI()) - .let { testPath -> - generateSequence(testPath) { - if (Files.isDirectory(it) && Files.exists(it.resolve("build.gradle.kts"))) { - null - } else { - it.parent - } - } - .take(10) // should be plenty - .toList() - .last() - .resolve("build") - .resolve( - (1..6) - .map { Random.nextInt(0, charPool.size) } - .map(charPool::get) - .joinToString("") - ) - } @SpringBootTest @AutoConfigureEmbeddedRedis( - port = 0, - serverConfigurerClass = "com.asarkar.spring.test.redis.TestEmbeddedRedisConfigurer" + port = 0 ) class AutoConfigureWithRandomPortsTest { @Value("\${embedded-redis.port:-1}") @@ -50,20 +21,5 @@ class AutoConfigureWithRandomPortsTest { val syncCommands = redisClient.connect().sync() syncCommands.set("key", "value") assertThat(syncCommands.get("key")).isEqualTo("value") - assertThat(Files.exists(file)).isTrue - } - - @AfterEach - fun afterEach() { - Files.deleteIfExists(file) - } -} - -class TestEmbeddedRedisConfigurer : EmbeddedRedisConfigurer { - private val log = LoggerFactory.getLogger(TestEmbeddedRedisConfigurer::class.java) - - override fun configure(builder: RedisServerBuilder) { - Files.createFile(file) - .also { log.info("Created file: {}", it.toAbsolutePath()) } } } diff --git a/src/test/kotlin/com/asarkar/spring/test/redis/EmbeddedRedisConfigurerBeanTest.kt b/src/test/kotlin/com/asarkar/spring/test/redis/EmbeddedRedisConfigurerBeanTest.kt new file mode 100644 index 0000000..dc6fef2 --- /dev/null +++ b/src/test/kotlin/com/asarkar/spring/test/redis/EmbeddedRedisConfigurerBeanTest.kt @@ -0,0 +1,50 @@ +package com.asarkar.spring.test.redis + +import io.lettuce.core.RedisClient +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.beans.factory.annotation.Value +import org.springframework.boot.test.context.SpringBootTest +import org.springframework.boot.test.context.TestConfiguration +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Import +import redis.embedded.RedisServerBuilder + +@SpringBootTest +@AutoConfigureEmbeddedRedis( + port = 0, + serverConfigurerClass = "com.asarkar.spring.test.redis.TestEmbeddedRedisConfigurerBean" +) +@Import(EmbeddedRedisConfigurerBeanTestConfiguration::class) +class EmbeddedRedisConfigurerBeanTest { + @Value("\${embedded-redis.port:-1}") + private var port: Int = -1 + + @Autowired + private lateinit var embeddedRedisConfigurer: TestEmbeddedRedisConfigurerBean + + @Test + fun testEmbeddedRedisConfigurer() { + val redisClient = RedisClient + .create("redis://localhost:$port/") + val syncCommands = redisClient.connect().sync() + syncCommands.set("key", "value") + assertThat(syncCommands.get("key")).isEqualTo("value") + assertThat(embeddedRedisConfigurer.called).isTrue + } +} + +@TestConfiguration +open class EmbeddedRedisConfigurerBeanTestConfiguration { + @Bean + open fun embeddedRedisConfigurer() = TestEmbeddedRedisConfigurerBean() +} + +class TestEmbeddedRedisConfigurerBean : EmbeddedRedisConfigurer { + var called: Boolean = false + + override fun configure(builder: RedisServerBuilder) { + called = true + } +} diff --git a/src/test/kotlin/com/asarkar/spring/test/redis/EmbeddedRedisConfigurerTest.kt b/src/test/kotlin/com/asarkar/spring/test/redis/EmbeddedRedisConfigurerTest.kt new file mode 100644 index 0000000..bfec974 --- /dev/null +++ b/src/test/kotlin/com/asarkar/spring/test/redis/EmbeddedRedisConfigurerTest.kt @@ -0,0 +1,69 @@ +package com.asarkar.spring.test.redis + +import io.lettuce.core.RedisClient +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.Test +import org.slf4j.LoggerFactory +import org.springframework.beans.factory.annotation.Value +import org.springframework.boot.test.context.SpringBootTest +import redis.embedded.RedisServerBuilder +import java.nio.file.Files +import java.nio.file.Paths +import kotlin.random.Random + +private val charPool: List = ('a'..'z') + ('A'..'Z') + ('0'..'9') +private val file = Paths.get(AutoConfigureWithRandomPortsTest::class.java.getResource("/").toURI()) + .let { testPath -> + generateSequence(testPath) { + if (Files.isDirectory(it) && Files.exists(it.resolve("build.gradle.kts"))) { + null + } else { + it.parent + } + } + .take(10) // should be plenty + .toList() + .last() + .resolve("build") + .resolve( + (1..6) + .map { Random.nextInt(0, charPool.size) } + .map(charPool::get) + .joinToString("") + ) + } + +@SpringBootTest +@AutoConfigureEmbeddedRedis( + port = 0, + serverConfigurerClass = "com.asarkar.spring.test.redis.TestEmbeddedRedisConfigurer" +) +class EmbeddedRedisConfigurerTest { + @Value("\${embedded-redis.port:-1}") + private var port: Int = -1 + + @Test + fun testEmbeddedRedisConfigurer() { + val redisClient = RedisClient + .create("redis://localhost:$port/") + val syncCommands = redisClient.connect().sync() + syncCommands.set("key", "value") + assertThat(syncCommands.get("key")).isEqualTo("value") + assertThat(Files.exists(file)).isTrue + } + + @AfterEach + fun afterEach() { + Files.deleteIfExists(file) + } +} + +class TestEmbeddedRedisConfigurer : EmbeddedRedisConfigurer { + private val log = LoggerFactory.getLogger(TestEmbeddedRedisConfigurer::class.java) + + override fun configure(builder: RedisServerBuilder) { + Files.createFile(file) + .also { log.info("Created file: {}", it.toAbsolutePath()) } + } +}