Skip to content

Commit 8f4529b

Browse files
committed
Add tests
Signed-off-by: Dmitry Sulman <[email protected]>
1 parent 0419540 commit 8f4529b

File tree

12 files changed

+234
-20
lines changed

12 files changed

+234
-20
lines changed

gradle/libs.versions.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
[versions]
2+
assertj = "3.27.3"
23
dokka = "2.0.0"
34
jackson = "2.19.0"
45
java = "17"
@@ -17,6 +18,7 @@ slf4j = "2.0.17"
1718
springBoot = "3.4.6"
1819

1920
[libraries]
21+
assertj-core = { group = "org.assertj", name = "assertj-core", version.ref = "assertj" }
2022
dokka-plugin = { group = "org.jetbrains.dokka", name = "org.jetbrains.dokka.gradle.plugin", version.ref = "dokka" }
2123
dokka-javadoc-plugin = { group = "org.jetbrains.dokka-javadoc", name = "org.jetbrains.dokka-javadoc.gradle.plugin", version.ref = "dokka" }
2224
jackson-bom = { group = "com.fasterxml.jackson", name = "jackson-bom", version.ref = "jackson" }
@@ -37,6 +39,7 @@ spring-boot-configurationProcessor = { group = "org.springframework.boot", name
3739
spring-boot-starter = { group = "org.springframework.boot", name = "spring-boot-starter", version.ref = "springBoot" }
3840
spring-boot-starter-reactorNetty = { group = "org.springframework.boot", name = "spring-boot-starter-reactor-netty", version.ref = "springBoot" }
3941
spring-boot-starter-test = { group = "org.springframework.boot", name = "spring-boot-starter-test", version.ref = "springBoot" }
42+
spring-boot-starter-webflux = { group = "org.springframework.boot", name = "spring-boot-starter-webflux", version.ref = "springBoot" }
4043

4144
[plugins]
4245
dokka = { id = "org.jetbrains.dokka" }

logback-access-reactor-netty-spring-boot-starter/build.gradle.kts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,5 +17,9 @@ dependencies {
1717
kapt(libs.spring.boot.autoconfigureProcessor)
1818
kapt(libs.spring.boot.configurationProcessor)
1919

20+
testImplementation(libs.assertj.core)
21+
testImplementation(libs.kotest.assertions.core.jvm)
22+
testImplementation(libs.mockk)
2023
testImplementation(libs.spring.boot.starter.test)
24+
testImplementation(libs.spring.boot.starter.webflux)
2125
}

logback-access-reactor-netty-spring-boot-starter/src/main/kotlin/io/github/dmitrysulman/logback/access/reactor/netty/autoconfigure/LogbackAccessReactorNettyProperties.kt

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,19 @@ package io.github.dmitrysulman.logback.access.reactor.netty.autoconfigure
33
import org.springframework.boot.context.properties.ConfigurationProperties
44

55
@ConfigurationProperties("logback.access.reactor.netty")
6-
class LogbackAccessReactorNettyProperties(
6+
class LogbackAccessReactorNettyProperties {
77
/**
88
* Enable Logback Access Reactor Netty auto-configuration
99
*/
10-
val enabled: Boolean?,
10+
var enabled: Boolean? = null
11+
1112
/**
1213
* Config file name
1314
*/
14-
val config: String?,
15+
var config: String? = null
16+
1517
/**
1618
* Enable debug mode
1719
*/
18-
val debug: Boolean?,
19-
)
20+
var debug: Boolean? = null
21+
}

logback-access-reactor-netty-spring-boot-starter/src/main/kotlin/io/github/dmitrysulman/logback/access/reactor/netty/autoconfigure/ReactorNettyAccessLogFactoryAutoConfiguration.kt

Lines changed: 3 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,6 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean
99
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty
1010
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication
1111
import org.springframework.boot.context.properties.EnableConfigurationProperties
12-
import org.springframework.boot.web.embedded.netty.NettyReactiveWebServerFactory
13-
import org.springframework.boot.web.server.WebServerFactoryCustomizer
1412
import org.springframework.context.annotation.Bean
1513
import org.springframework.core.env.Environment
1614
import org.springframework.core.io.ResourceLoader
@@ -39,19 +37,9 @@ class ReactorNettyAccessLogFactoryAutoConfiguration {
3937
)
4038

4139
@Bean
42-
fun reactorNettyAccessLogWebServerFactoryCustomize(
43-
reactorNettyAccessLogFactory: ReactorNettyAccessLogFactory,
44-
): WebServerFactoryCustomizer<NettyReactiveWebServerFactory> =
45-
WebServerFactoryCustomizer { factory ->
46-
factory.addServerCustomizers(
47-
{ server ->
48-
server.accessLog(
49-
true,
50-
reactorNettyAccessLogFactory,
51-
)
52-
},
53-
)
54-
}
40+
@ConditionalOnMissingBean
41+
fun reactorNettyAccessLogWebServerFactoryCustomizer(reactorNettyAccessLogFactory: ReactorNettyAccessLogFactory) =
42+
ReactorNettyAccessLogWebServerFactoryCustomizer(true, reactorNettyAccessLogFactory)
5543

5644
private fun getConfigUrl(
5745
properties: LogbackAccessReactorNettyProperties,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package io.github.dmitrysulman.logback.access.reactor.netty.autoconfigure
2+
3+
import io.github.dmitrysulman.logback.access.reactor.netty.ReactorNettyAccessLogFactory
4+
import org.springframework.boot.web.embedded.netty.NettyReactiveWebServerFactory
5+
import org.springframework.boot.web.server.WebServerFactoryCustomizer
6+
7+
class ReactorNettyAccessLogWebServerFactoryCustomizer(
8+
private val enableAccessLog: Boolean,
9+
private val reactorNettyAccessLogFactory: ReactorNettyAccessLogFactory,
10+
) : WebServerFactoryCustomizer<NettyReactiveWebServerFactory> {
11+
override fun customize(factory: NettyReactiveWebServerFactory) {
12+
factory.addServerCustomizers(
13+
{ server ->
14+
server.accessLog(
15+
enableAccessLog,
16+
reactorNettyAccessLogFactory,
17+
)
18+
},
19+
)
20+
}
21+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package io.github.dmitrysulman.logback.access.reactor.netty.autoconfigure
2+
3+
import io.kotest.matchers.booleans.shouldBeTrue
4+
import io.kotest.matchers.shouldBe
5+
import org.junit.jupiter.api.Test
6+
import org.springframework.beans.factory.annotation.Autowired
7+
import org.springframework.boot.context.properties.EnableConfigurationProperties
8+
import org.springframework.boot.test.context.SpringBootTest
9+
import org.springframework.context.annotation.Configuration
10+
11+
@SpringBootTest(
12+
classes = [LogbackAccessReactorNettyPropertiesTests::class],
13+
properties = [
14+
"logback.access.reactor.netty.enabled=true",
15+
"logback.access.reactor.netty.config=test-logback-access.xml",
16+
"logback.access.reactor.netty.debug=true",
17+
],
18+
)
19+
@EnableConfigurationProperties(LogbackAccessReactorNettyProperties::class)
20+
@Configuration
21+
class LogbackAccessReactorNettyPropertiesTests(
22+
@Autowired private val properties: LogbackAccessReactorNettyProperties,
23+
) {
24+
@Test
25+
fun `smoke test`() {
26+
properties.enabled?.shouldBeTrue()
27+
properties.config shouldBe "test-logback-access.xml"
28+
properties.debug?.shouldBeTrue()
29+
}
30+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
package io.github.dmitrysulman.logback.access.reactor.netty.autoconfigure
2+
3+
import io.github.dmitrysulman.logback.access.reactor.netty.ReactorNettyAccessLogFactory
4+
import io.mockk.mockk
5+
import io.mockk.verify
6+
import org.assertj.core.api.AssertionsForInterfaceTypes.assertThat
7+
import org.junit.jupiter.api.Test
8+
import org.springframework.beans.factory.getBean
9+
import org.springframework.beans.factory.getBeansOfType
10+
import org.springframework.boot.autoconfigure.AutoConfigurations
11+
import org.springframework.boot.autoconfigure.web.reactive.ReactiveWebServerFactoryAutoConfiguration
12+
import org.springframework.boot.test.context.FilteredClassLoader
13+
import org.springframework.boot.test.context.runner.ReactiveWebApplicationContextRunner
14+
import org.springframework.context.annotation.Bean
15+
import org.springframework.context.annotation.Configuration
16+
import reactor.netty.http.server.HttpServer
17+
18+
class ReactorNettyAccessLogFactoryAutoConfigurationTests {
19+
@Test
20+
fun `should supply beans`() {
21+
ReactiveWebApplicationContextRunner()
22+
.withConfiguration(AutoConfigurations.of(ReactorNettyAccessLogFactoryAutoConfiguration::class.java))
23+
.run { context ->
24+
assertThat(context).hasSingleBean(ReactorNettyAccessLogFactory::class.java)
25+
assertThat(context).hasSingleBean(ReactorNettyAccessLogWebServerFactoryCustomizer::class.java)
26+
}
27+
}
28+
29+
@Test
30+
fun `should not supply beans when HttpServer is not on the classpath`() {
31+
ReactiveWebApplicationContextRunner()
32+
.withConfiguration(AutoConfigurations.of(ReactorNettyAccessLogFactoryAutoConfiguration::class.java))
33+
.withClassLoader(FilteredClassLoader(HttpServer::class.java))
34+
.run { context ->
35+
assertThat(context).doesNotHaveBean(ReactorNettyAccessLogFactory::class.java)
36+
assertThat(context).doesNotHaveBean(ReactorNettyAccessLogWebServerFactoryCustomizer::class.java)
37+
}
38+
}
39+
40+
@Test
41+
fun `should not supply beans when already has user defined beans in the context`() {
42+
ReactiveWebApplicationContextRunner()
43+
.withConfiguration(AutoConfigurations.of(ReactorNettyAccessLogFactoryAutoConfiguration::class.java))
44+
.withUserConfiguration(CustomReactorNettyAccessLogFactoryConfiguration::class.java)
45+
.run { context ->
46+
assertThat(context.getBeansOfType<ReactorNettyAccessLogFactory>()).hasSize(1)
47+
assertThat(context.getBeansOfType<ReactorNettyAccessLogWebServerFactoryCustomizer>()).hasSize(1)
48+
}
49+
}
50+
51+
@Test
52+
fun `should apply customizer`() {
53+
ReactiveWebApplicationContextRunner()
54+
.withConfiguration(
55+
AutoConfigurations.of(
56+
ReactiveWebServerFactoryAutoConfiguration::class.java,
57+
ReactorNettyAccessLogFactoryAutoConfiguration::class.java,
58+
),
59+
).withUserConfiguration(MockNettyAccessLogFactoryConfiguration::class.java)
60+
.run { context ->
61+
val customizer = context.getBean<ReactorNettyAccessLogWebServerFactoryCustomizer>()
62+
verify(exactly = 1) { customizer.customize(any()) }
63+
}
64+
}
65+
66+
@Configuration(proxyBeanMethods = false)
67+
class CustomReactorNettyAccessLogFactoryConfiguration {
68+
@Bean
69+
fun customReactorNettyAccessLogFactory() = ReactorNettyAccessLogFactory()
70+
71+
@Bean
72+
fun customReactorNettyAccessLogWebServerFactoryCustomizer(customReactorNettyAccessLogFactory: ReactorNettyAccessLogFactory) =
73+
ReactorNettyAccessLogWebServerFactoryCustomizer(true, customReactorNettyAccessLogFactory)
74+
}
75+
76+
@Configuration(proxyBeanMethods = false)
77+
class MockNettyAccessLogFactoryConfiguration {
78+
private val mockReactorNettyAccessLogWebServerFactoryCustomizer =
79+
mockk<ReactorNettyAccessLogWebServerFactoryCustomizer>(relaxed = true)
80+
81+
@Bean
82+
fun mockReactorNettyAccessLogWebServerFactoryCustomizer() = mockReactorNettyAccessLogWebServerFactoryCustomizer
83+
}
84+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package io.github.dmitrysulman.logback.access.reactor.netty.autoconfigure.integration
2+
3+
import org.springframework.web.bind.annotation.GetMapping
4+
import org.springframework.web.bind.annotation.RequestParam
5+
import org.springframework.web.bind.annotation.RestController
6+
7+
@RestController
8+
class EchoController {
9+
@GetMapping("/get")
10+
fun getRequest(
11+
@RequestParam param: String,
12+
): String = param
13+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package io.github.dmitrysulman.logback.access.reactor.netty.autoconfigure.integration
2+
3+
import ch.qos.logback.access.common.spi.IAccessEvent
4+
import ch.qos.logback.core.AppenderBase
5+
6+
class EventCaptureAppender : AppenderBase<IAccessEvent>() {
7+
val list = mutableListOf<IAccessEvent>()
8+
9+
init {
10+
start()
11+
}
12+
13+
override fun append(event: IAccessEvent) {
14+
list.add(event)
15+
}
16+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package io.github.dmitrysulman.logback.access.reactor.netty.autoconfigure.integration
2+
3+
import io.github.dmitrysulman.logback.access.reactor.netty.ReactorNettyAccessLogFactory
4+
import io.kotest.matchers.shouldBe
5+
import org.junit.jupiter.api.Test
6+
import org.springframework.beans.factory.annotation.Autowired
7+
import org.springframework.boot.test.context.SpringBootTest
8+
import org.springframework.test.web.reactive.server.WebTestClient
9+
10+
@SpringBootTest(
11+
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
12+
properties = ["logback.access.reactor.netty.config=classpath:logback-access-stdout.xml"],
13+
)
14+
class IntegrationTests(
15+
@Autowired private val webTestClient: WebTestClient,
16+
@Autowired private val reactorNettyAccessLogFactory: ReactorNettyAccessLogFactory,
17+
) {
18+
@Test
19+
fun `smoke test`() {
20+
val value = "test"
21+
webTestClient
22+
.get()
23+
.uri("/get?param={param}", value)
24+
.exchange()
25+
.expectStatus()
26+
.isOk
27+
.expectBody()
28+
.returnResult()
29+
.responseBodyContent shouldBe value.toByteArray()
30+
31+
val eventCaptureAppender =
32+
reactorNettyAccessLogFactory.accessContext.getAppender("CAPTURE") as EventCaptureAppender
33+
34+
eventCaptureAppender.list.size shouldBe 1
35+
}
36+
}

0 commit comments

Comments
 (0)