Set up postgresql testcontainers

- add .json to .editorconfig
- add spring-configuration-metadata.json
- update README.md
- add testcontainers dependencies
- add PostgresContainer
- add PostgresInitializer
This commit is contained in:
2025-01-30 15:12:25 +01:00
parent d9321f61d1
commit 1f6329f687
5 changed files with 134 additions and 0 deletions

View File

@@ -0,0 +1,11 @@
package ltd.hlaeja.test.container
import org.junit.jupiter.api.extension.ExtendWith
import org.springframework.test.context.ContextConfiguration
@Suppress("unused")
@Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.CLASS)
@ExtendWith(PostgresExtension::class)
@ContextConfiguration(initializers = [PostgresInitializer::class])
annotation class PostgresContainer

View File

@@ -0,0 +1,74 @@
package ltd.hlaeja.test.container
import java.io.BufferedReader
import java.io.InputStreamReader
import org.junit.jupiter.api.extension.AfterAllCallback
import org.junit.jupiter.api.extension.BeforeAllCallback
import org.junit.jupiter.api.extension.ExtensionContext
import org.springframework.core.io.ClassPathResource
import org.springframework.test.context.junit.jupiter.SpringExtension
import kotlinx.coroutines.runBlocking
import io.r2dbc.spi.Connection
import io.r2dbc.spi.ConnectionFactory
import kotlinx.coroutines.reactive.awaitFirstOrElse
import kotlinx.coroutines.reactive.awaitFirstOrNull
class PostgresExtension : BeforeAllCallback, AfterAllCallback {
override fun beforeAll(context: ExtensionContext) {
executeSqlFile(context, "postgres/data.sql")
}
override fun afterAll(context: ExtensionContext) {
executeSqlFile(context, "postgres/reset.sql")
}
private fun executeSqlFile(
context: ExtensionContext,
resourceSourcePath: String,
) = runBlocking {
if (ClassPathResource(resourceSourcePath).exists()) {
executeSqlStatements(
postgresConnection(context),
makeSqlStatements(ClassPathResource(resourceSourcePath)),
)
}
}
@Suppress("TooGenericExceptionThrown")
private suspend fun postgresConnection(
context: ExtensionContext,
) = SpringExtension.getApplicationContext(context)
.getBean(ConnectionFactory::class.java)
.create()
.awaitFirstOrElse { throw RuntimeException("Connection factory could not be created") }
private suspend fun executeSqlStatements(
connection: Connection,
statements: List<String>,
) {
try {
statements.forEach { statement ->
connection.createStatement(statement)
.execute()
.awaitFirstOrNull()
}
} finally {
connection.close().awaitFirstOrNull()
}
}
private fun makeSqlStatements(
classPathResource: ClassPathResource,
): List<String> = classPathResource.inputStream.use { inputStream ->
BufferedReader(InputStreamReader(inputStream))
.lines()
.filter { it.isNotEmpty() && !it.startsWith("--") }
.map { it.trim() }
.toList()
.joinToString(" ")
.split(';')
.filter { it.isNotBlank() }
.map { "${it.trim()};" }
}
}

View File

@@ -0,0 +1,32 @@
package ltd.hlaeja.test.container
import org.springframework.boot.test.util.TestPropertyValues
import org.springframework.context.ApplicationContextInitializer
import org.springframework.context.ConfigurableApplicationContext
import org.springframework.core.io.ClassPathResource
import org.testcontainers.containers.PostgreSQLContainer
@Suppress("unused")
class PostgresInitializer : ApplicationContextInitializer<ConfigurableApplicationContext> {
override fun initialize(applicationContext: ConfigurableApplicationContext) {
postgres().apply {
TestPropertyValues.of(
"spring.r2dbc.url=r2dbc:pool:postgresql://$host:$firstMappedPort/$databaseName",
"spring.r2dbc.username=$username",
"spring.r2dbc.password=$password",
).applyTo(applicationContext)
}
}
private fun postgres(): PostgreSQLContainer<*> = PostgreSQLContainer("postgres:17")
.withReuse(true)
.apply {
"postgres/schema.sql".let {
if (ClassPathResource(it).exists()) {
withInitScript(it)
}
}
start()
}
}