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

@@ -2,6 +2,19 @@
In the forge of software development, where annotations ignite, A crucible of testing, common classes to excite. Each annotation examined, with attention to detail and might, Their effects on code behavior, tested through day and night. From mockk objects to test doubles, a toolkit to refine, Developers and testers, their skills to redefine. The Annotation Validator, a sentinel of code integrity true, A library of verification, where testing wisdom shines anew.
## Postgres Test Container
`@PostgresContainer` Annotation for integration tests.
Initialize Postgres test container using spring properties for R2DBC,
script located in `src/<test path>/resources/postgres` folder.
* `schema.sql` file containing all structure and functions when star.
* `data.sql` file containing all data added before all test.
* `reset.sql` file containing all to reset database after all test.
if file exist it will be loaded...
## Releasing library
Run `release.sh` script from `master` branch.

View File

@@ -7,7 +7,11 @@ plugins {
}
dependencies {
implementation(hlaeja.kotlinx.coroutines)
implementation(hlaeja.springboot.starter.r2dbc)
implementation(hlaeja.springboot.starter.test)
implementation(hlaeja.testcontainers.junit)
implementation(hlaeja.testcontainers.postgresql)
testRuntimeOnly(hlaeja.junit.platform.launcher)
}

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()
}
}