Skip to content

Commit 056983a

Browse files
author
Oleksandr Dzhychko
authored
Merge pull request #1007 from modelix/build/replace-docker-compose-with-test-containers
Use Testcontainers instead of Docker Compose for tests with PostgreSQL
2 parents dbaa821 + eb29cd3 commit 056983a

File tree

11 files changed

+85
-86
lines changed

11 files changed

+85
-86
lines changed

gradle/libs.versions.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ dokka = "1.9.20"
4040
detekt = "1.23.6"
4141
xmlunit = "2.10.0"
4242
kotest = "5.9.1"
43+
testcontainers = "1.20.1"
4344

4445
[libraries]
4546

@@ -130,3 +131,6 @@ detekt-api = { group = "io.gitlab.arturbosch.detekt", name= "detekt-api", versio
130131
detekt-test = { group = "io.gitlab.arturbosch.detekt", name= "detekt-test", version.ref = "detekt" }
131132

132133
jimfs = { group = "com.google.jimfs", name = "jimfs", version = "1.3.0" }
134+
135+
testcontainers = { group = "org.testcontainers", name = "testcontainers", version.ref = "testcontainers" }
136+
testcontainers-postgresql = { group = "org.testcontainers", name = "postgresql", version.ref = "testcontainers" }

model-server-test/README.md

Lines changed: 0 additions & 2 deletions
This file was deleted.

model-server-test/build.gradle.kts

Lines changed: 0 additions & 22 deletions
This file was deleted.

model-server-test/docker-compose.yaml

Lines changed: 0 additions & 11 deletions
This file was deleted.

model-server/build.gradle.kts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,8 @@ dependencies {
8282
testImplementation(libs.mockk)
8383
testImplementation(kotlin("test"))
8484
testImplementation(project(":modelql-untyped"))
85+
testImplementation(libs.testcontainers)
86+
testImplementation(libs.testcontainers.postgresql)
8587
}
8688

8789
tasks.named<ShadowJar>("shadowJar") {

model-server/src/main/kotlin/org/modelix/model/server/Main.kt

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,9 +75,11 @@ import org.modelix.model.server.store.writeDump
7575
import org.slf4j.LoggerFactory
7676
import org.springframework.util.ResourceUtils
7777
import java.io.File
78+
import java.io.FileReader
7879
import java.io.IOException
7980
import java.nio.charset.StandardCharsets
8081
import java.time.Duration
82+
import java.util.Properties
8183
import javax.sql.DataSource
8284

8385
object Main {
@@ -143,9 +145,11 @@ object Main {
143145
)
144146
}
145147
} else if (cmdLineArgs.localPersistence) {
146-
storeClient = IgniteStoreClient(cmdLineArgs.jdbcConfFile, inmemory = true)
148+
val jdbcProperties = cmdLineArgs.jdbcConfFile?.let(::readJdbcProperties)
149+
storeClient = IgniteStoreClient(jdbcProperties, inmemory = true)
147150
} else {
148-
storeClient = IgniteStoreClient(cmdLineArgs.jdbcConfFile)
151+
val jdbcProperties = cmdLineArgs.jdbcConfFile?.let(::readJdbcProperties)
152+
storeClient = IgniteStoreClient(jdbcProperties)
149153
if (cmdLineArgs.schemaInit) {
150154
val dataSource: DataSource = Ignition.loadSpringBean<DataSource>(
151155
Main::class.java.getResource("ignite.xml"),
@@ -251,6 +255,16 @@ object Main {
251255
}
252256
}
253257

258+
private fun readJdbcProperties(jdbcConfFile: File): Properties {
259+
val properties = Properties()
260+
try {
261+
properties.load(FileReader(jdbcConfFile))
262+
} catch (e: IOException) {
263+
throw IllegalStateException("Could not load the JDBC configuration from ${jdbcConfFile.absolutePath}.", e)
264+
}
265+
return properties
266+
}
267+
254268
/**
255269
* Installs the status pages extension with a configuration suitable for generating application/problem+json
256270
* responses as defined in the API specification.

model-server/src/main/kotlin/org/modelix/model/server/store/IgniteStoreClient.kt

Lines changed: 8 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,6 @@ import org.modelix.model.IGenericKeyListener
2626
import org.modelix.model.lazy.RepositoryId
2727
import org.modelix.model.persistent.HashUtil
2828
import org.modelix.model.server.SqlUtils
29-
import java.io.File
30-
import java.io.FileReader
31-
import java.io.IOException
3229
import java.sql.SQLException
3330
import java.util.*
3431
import javax.cache.Cache
@@ -40,7 +37,9 @@ private val LOG = KotlinLogging.logger { }
4037
* Store client implementation with an ignite cache.
4138
* If [inmemory] is true, the data is not persisted in a database.
4239
*/
43-
class IgniteStoreClient(jdbcConfFile: File? = null, private val inmemory: Boolean = false) : IsolatingStore, AutoCloseable {
40+
class IgniteStoreClient(jdbcProperties: Properties? = null, private val inmemory: Boolean = false) :
41+
IsolatingStore,
42+
AutoCloseable {
4443

4544
companion object {
4645
private const val ENTRY_CHANGED_TOPIC = "entryChanged"
@@ -68,28 +67,14 @@ class IgniteStoreClient(jdbcConfFile: File? = null, private val inmemory: Boolea
6867
* from ignite.xml is used
6968
*/
7069
init {
71-
if (jdbcConfFile != null) {
70+
if (jdbcProperties != null) {
7271
// Given that systemPropertiesMode is set to 2 (SYSTEM_PROPERTIES_MODE_OVERRIDE) in
7372
// ignite.xml, we can override the properties through system properties
74-
try {
75-
val properties = Properties()
76-
properties.load(FileReader(jdbcConfFile))
77-
for (pn in properties.stringPropertyNames()) {
78-
if (pn.startsWith("jdbc.")) {
79-
System.setProperty(pn, properties.getProperty(pn))
80-
} else {
81-
throw RuntimeException(
82-
"Properties not related to jdbc are not permitted. Check file " +
83-
jdbcConfFile.absolutePath,
84-
)
85-
}
73+
for (propertyName in jdbcProperties.stringPropertyNames()) {
74+
require(propertyName.startsWith("jdbc.")) {
75+
"Property `$propertyName` is invalid. Only properties starting with `jdbc.` are permitted."
8676
}
87-
} catch (e: IOException) {
88-
throw RuntimeException(
89-
"We are unable to load the JDBC configuration from " +
90-
jdbcConfFile.absolutePath,
91-
e,
92-
)
77+
System.setProperty(propertyName, jdbcProperties.getProperty(propertyName))
9378
}
9479
}
9580
if (!inmemory) updateDatabaseSchema()

model-server-test/src/test/kotlin/IgnitePostgresRepositoryRemovalTest.kt renamed to model-server/src/test/kotlin/org/modelix/model/server/store/IgnitePostgresRepositoryRemovalTest.kt

Lines changed: 30 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -13,33 +13,52 @@
1313
* See the License for the specific language governing permissions and
1414
* limitations under the License.
1515
*/
16-
import org.junit.jupiter.api.AfterAll
16+
17+
package org.modelix.model.server.store
18+
1719
import org.junit.jupiter.api.Test
1820
import org.junit.jupiter.api.assertDoesNotThrow
1921
import org.modelix.model.lazy.RepositoryId
20-
import org.modelix.model.server.store.IgniteStoreClient
21-
import org.modelix.model.server.store.ObjectInRepository
22+
import org.testcontainers.containers.PostgreSQLContainer
23+
import org.testcontainers.utility.DockerImageName
2224
import java.sql.Connection
2325
import java.sql.DriverManager
26+
import java.util.Properties
2427
import kotlin.test.AfterTest
28+
import kotlin.test.BeforeTest
2529
import kotlin.test.assertEquals
2630
import kotlin.test.assertFalse
2731
import kotlin.test.assertTrue
2832

2933
class IgnitePostgresRepositoryRemovalTest {
30-
private val store = IgniteStoreClient()
34+
private lateinit var postgres: PostgreSQLContainer<*>
35+
private lateinit var store: IgniteStoreClient
36+
private lateinit var dbConnection: Connection
3137

3238
private val toDelete = RepositoryId("repository-removal-toDelete")
3339
private val existing = RepositoryId("repository-removal-existing")
3440

41+
@BeforeTest
42+
fun beforeTest() {
43+
postgres = PostgreSQLContainer(DockerImageName.parse("postgres:16.2-alpine"))
44+
.withDatabaseName("modelix")
45+
.withUsername("modelix")
46+
.withPassword("modelix")
47+
.withExposedPorts(5432)
48+
postgres.start()
49+
50+
val jdbcProperties = Properties()
51+
jdbcProperties.setProperty("jdbc.url", "jdbc:postgresql://${postgres.host}:${postgres.firstMappedPort}/")
52+
53+
store = IgniteStoreClient(jdbcProperties)
54+
55+
dbConnection = DriverManager.getConnection(System.getProperty("jdbc.url"), "modelix", "modelix")
56+
}
57+
3558
@AfterTest
36-
fun cleanup() {
37-
dbConnection.prepareStatement("DELETE FROM modelix.model WHERE repository IN ( ? , ? )").use {
38-
it.setString(1, toDelete.id)
39-
it.setString(2, existing.id)
40-
}
41-
store.putAll(store.getAll().keys.associateWith { null })
42-
store.close()
59+
fun afterTest() {
60+
store.dispose()
61+
postgres.stop()
4362
}
4463

4564
@Test
@@ -118,14 +137,4 @@ class IgnitePostgresRepositoryRemovalTest {
118137
store.removeRepositoryObjects(RepositoryId("invalid"))
119138
}
120139
}
121-
122-
companion object {
123-
val dbConnection: Connection = DriverManager.getConnection(System.getProperty("jdbc.url"), "modelix", "modelix")
124-
125-
@JvmStatic
126-
@AfterAll
127-
fun closeDbConnection() {
128-
dbConnection.close()
129-
}
130-
}
131140
}

model-server-test/src/test/kotlin/IgnitePostgresTest.kt renamed to model-server/src/test/kotlin/org/modelix/model/server/store/IgnitePostgresTest.kt

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,27 +13,48 @@
1313
* See the License for the specific language governing permissions and
1414
* limitations under the License.
1515
*/
16+
17+
package org.modelix.model.server.store
18+
1619
import org.modelix.model.lazy.RepositoryId
1720
import org.modelix.model.persistent.HashUtil
18-
import org.modelix.model.server.store.IgniteStoreClient
19-
import org.modelix.model.server.store.ObjectInRepository
21+
import org.testcontainers.containers.PostgreSQLContainer
22+
import org.testcontainers.utility.DockerImageName
23+
import org.testcontainers.utility.MountableFile
24+
import java.util.Properties
2025
import kotlin.test.AfterTest
2126
import kotlin.test.BeforeTest
2227
import kotlin.test.Test
2328
import kotlin.test.assertEquals
2429
import kotlin.test.assertTrue
2530

2631
class IgnitePostgresTest {
27-
lateinit var store: IgniteStoreClient
32+
private lateinit var postgres: PostgreSQLContainer<*>
33+
private lateinit var store: IgniteStoreClient
2834

2935
@BeforeTest
3036
fun beforeTest() {
31-
store = IgniteStoreClient()
37+
postgres = PostgreSQLContainer(DockerImageName.parse("postgres:16.2-alpine"))
38+
.withDatabaseName("modelix")
39+
.withUsername("modelix")
40+
.withPassword("modelix")
41+
.withCopyFileToContainer(
42+
MountableFile.forClasspathResource("/legacy-database.sql"),
43+
"/docker-entrypoint-initdb.d/initdb.sql",
44+
)
45+
.withExposedPorts(5432)
46+
postgres.start()
47+
48+
val jdbcProperties = Properties()
49+
jdbcProperties.setProperty("jdbc.url", "jdbc:postgresql://${postgres.host}:${postgres.firstMappedPort}/")
50+
51+
store = IgniteStoreClient(jdbcProperties)
3252
}
3353

3454
@AfterTest
3555
fun afterTest() {
3656
store.dispose()
57+
postgres.stop()
3758
}
3859

3960
@Test
File renamed without changes.

0 commit comments

Comments
 (0)