From c7424072888b8663cfc0cca7f0a0771b5ef61ec8 Mon Sep 17 00:00:00 2001
From: Mustapha Zorgati <15628173+mustaphazorgati@users.noreply.github.com>
Date: Wed, 15 Sep 2021 10:13:31 +0200
Subject: [PATCH] TSK-1685: added testcontainers extension
---
.github/workflows/continuous-integration.yml | 8 +-
common/taskana-common-test/pom.xml | 22 +++
.../main/java/org/junit/rules/TestRule.java | 8 ++
.../org/junit/runners/model/Statement.java | 8 ++
.../test/config/TestContainerExtension.java | 136 ++++++++++++++++++
docker-databases/prepare_db.sh | 12 +-
.../TaskanaIntegrationTestExtension.java | 14 +-
.../container-license-acceptance.txt | 1 +
pom.xml | 1 +
9 files changed, 198 insertions(+), 12 deletions(-)
create mode 100644 common/taskana-common-test/src/main/java/org/junit/rules/TestRule.java
create mode 100644 common/taskana-common-test/src/main/java/org/junit/runners/model/Statement.java
create mode 100644 common/taskana-common-test/src/main/java/pro/taskana/common/test/config/TestContainerExtension.java
create mode 100644 lib/taskana-core/src/test/resources/container-license-acceptance.txt
diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml
index c96e19edd..f0dd31427 100644
--- a/.github/workflows/continuous-integration.yml
+++ b/.github/workflows/continuous-integration.yml
@@ -250,11 +250,11 @@ jobs:
- H2
include:
- module: taskana-core
- database: POSTGRES_10
+ database: POSTGRES
- module: taskana-core
- database: DB2_11_1
+ database: DB2
- module: taskana-rest-spring-example-boot
- database: DB2_11_1
+ database: DB2
steps:
- name: Git checkout
uses: actions/checkout@v2.3.4
@@ -281,6 +281,8 @@ jobs:
run: ./mvnw -B validate -pl :taskana-rest-spring
- name: Test
run: ./mvnw -B verify -pl :${{matrix.module}} -Dcheckstyle.skip
+ env:
+ db.type: ${{ matrix.database }}
- name: Upload JaCoCo Report
if: matrix.database == 'H2'
uses: actions/upload-artifact@v2
diff --git a/common/taskana-common-test/pom.xml b/common/taskana-common-test/pom.xml
index 5fa3f6fba..09fa2e8de 100644
--- a/common/taskana-common-test/pom.xml
+++ b/common/taskana-common-test/pom.xml
@@ -106,6 +106,28 @@
org.springframework
spring-webmvc
+
+ org.testcontainers
+ db2
+ ${version.testcontainers}
+
+
+ junit
+ junit
+
+
+
+
+ org.testcontainers
+ postgresql
+ ${version.testcontainers}
+
+
+ junit
+ junit
+
+
+
diff --git a/common/taskana-common-test/src/main/java/org/junit/rules/TestRule.java b/common/taskana-common-test/src/main/java/org/junit/rules/TestRule.java
new file mode 100644
index 000000000..74f5e4ae5
--- /dev/null
+++ b/common/taskana-common-test/src/main/java/org/junit/rules/TestRule.java
@@ -0,0 +1,8 @@
+package org.junit.rules;
+
+// Testcontainers currently requires junit4 as a runtime dependency.
+// Because we use junit5 we have to use this workaround to "simulate" the classes testcontainers
+// requires in the classpath. They are not used unless a junit4 runtime is used.
+// See: https://github.com/testcontainers/testcontainers-java/issues/970#issuecomment-625044008
+@SuppressWarnings("unused")
+public interface TestRule {}
diff --git a/common/taskana-common-test/src/main/java/org/junit/runners/model/Statement.java b/common/taskana-common-test/src/main/java/org/junit/runners/model/Statement.java
new file mode 100644
index 000000000..add87960e
--- /dev/null
+++ b/common/taskana-common-test/src/main/java/org/junit/runners/model/Statement.java
@@ -0,0 +1,8 @@
+package org.junit.runners.model;
+
+// Testcontainers currently requires junit4 as a runtime dependency.
+// Because we use junit5 we have to use this workaround to "simulate" the classes testcontainers
+// requires in the classpath. They are not used unless a junit4 runtime is used.
+// See: https://github.com/testcontainers/testcontainers-java/issues/970#issuecomment-625044008
+@SuppressWarnings("unused")
+public interface Statement {}
diff --git a/common/taskana-common-test/src/main/java/pro/taskana/common/test/config/TestContainerExtension.java b/common/taskana-common-test/src/main/java/pro/taskana/common/test/config/TestContainerExtension.java
new file mode 100644
index 000000000..f521a9b8e
--- /dev/null
+++ b/common/taskana-common-test/src/main/java/pro/taskana/common/test/config/TestContainerExtension.java
@@ -0,0 +1,136 @@
+package pro.taskana.common.test.config;
+
+import static java.time.temporal.ChronoUnit.SECONDS;
+
+import java.time.Duration;
+import java.util.Optional;
+import java.util.UUID;
+import javax.sql.DataSource;
+import org.apache.ibatis.datasource.pooled.PooledDataSource;
+import org.junit.jupiter.api.extension.AfterAllCallback;
+import org.junit.jupiter.api.extension.ExtensionContext;
+import org.testcontainers.containers.Db2Container;
+import org.testcontainers.containers.JdbcDatabaseContainer;
+import org.testcontainers.containers.PostgreSQLContainer;
+import org.testcontainers.containers.wait.strategy.LogMessageWaitStrategy;
+import org.testcontainers.utility.DockerImageName;
+
+import pro.taskana.common.internal.configuration.DB;
+
+public class TestContainerExtension implements AfterAllCallback {
+
+ private final DataSource dataSource;
+ private final String schemaName;
+ private final JdbcDatabaseContainer> container;
+
+ public TestContainerExtension() {
+ DB db = getTestDatabase();
+ Optional> container = createDockerContainer(db);
+ if (container.isPresent()) {
+ this.container = container.get();
+ this.container.start();
+ dataSource = createDataSource(this.container);
+ } else {
+ dataSource = createDataSourceForH2();
+ this.container = null;
+ }
+ schemaName = determineSchemaName(db);
+ }
+
+ @Override
+ public void afterAll(ExtensionContext context) {
+ if (container != null) {
+ container.stop();
+ }
+ }
+
+ public DataSource getDataSource() {
+ return dataSource;
+ }
+
+ public String getSchemaName() {
+ return schemaName;
+ }
+
+ private DB getTestDatabase() {
+ String property = System.getenv("db.type");
+ DB db;
+ try {
+ db = DB.valueOf(property);
+ } catch (Exception ex) {
+ db = DB.H2;
+ }
+ return db;
+ }
+
+ private static String determineSchemaName(DB db) {
+ return db == DB.POSTGRES ? "taskana" : "TASKANA";
+ }
+
+ private static DataSource createDataSource(JdbcDatabaseContainer> container) {
+ PooledDataSource ds =
+ new PooledDataSource(
+ Thread.currentThread().getContextClassLoader(),
+ container.getDriverClassName(),
+ container.getJdbcUrl(),
+ container.getUsername(),
+ container.getPassword());
+ ds.setPoolTimeToWait(50);
+ ds.forceCloseAll(); // otherwise, the MyBatis pool is not initialized correctly
+ return ds;
+ }
+
+ private static Optional> createDockerContainer(DB db) {
+ switch (db) {
+ case DB2:
+ return Optional.of(
+ new Db2Container(
+ DockerImageName.parse("taskana/db2:11.1")
+ .asCompatibleSubstituteFor("ibmcom/db2"))
+ .waitingFor(
+ new LogMessageWaitStrategy()
+ .withRegEx(".*DB2START processing was successful.*")
+ .withStartupTimeout(Duration.of(60, SECONDS)))
+ .withUsername("db2inst1")
+ .withPassword("db2inst1-pwd")
+ .withDatabaseName("TSKDB"));
+ case POSTGRES:
+ return Optional.of(
+ new PostgreSQLContainer<>(DockerImageName.parse("postgres:10"))
+ .withUsername("postgres")
+ .withPassword("postgres")
+ .withDatabaseName("postgres")
+ .withCommand(
+ "/bin/sh",
+ "-c",
+ "localedef -i de_DE -c -f UTF-8 -A /usr/share/locale/locale.alias de_DE.UTF-8 "
+ + "&& export LANG=de_DE.UTF-8 "
+ + "&& ./docker-entrypoint.sh postgres -c fsync=off")
+ .waitingFor(
+ new LogMessageWaitStrategy()
+ .withRegEx(".*Datenbanksystem ist bereit, um Verbindungen anzunehmen.*\\s")
+ .withTimes(2)
+ .withStartupTimeout(Duration.of(60, SECONDS))));
+ default:
+ return Optional.empty();
+ }
+ }
+
+ private static DataSource createDataSourceForH2() {
+ PooledDataSource ds =
+ new PooledDataSource(
+ Thread.currentThread().getContextClassLoader(),
+ "org.h2.Driver",
+ "jdbc:h2:mem:"
+ + UUID.randomUUID()
+ + ";LOCK_MODE=0;"
+ + "INIT=CREATE SCHEMA IF NOT EXISTS TASKANA\\;"
+ + "SET COLLATION DEFAULT_de_DE ",
+ "sa",
+ "sa");
+ ds.setPoolTimeToWait(50);
+ ds.forceCloseAll(); // otherwise, the MyBatis pool is not initialized correctly
+
+ return ds;
+ }
+}
diff --git a/docker-databases/prepare_db.sh b/docker-databases/prepare_db.sh
index a6d6068a3..a5865f08b 100755
--- a/docker-databases/prepare_db.sh
+++ b/docker-databases/prepare_db.sh
@@ -19,8 +19,8 @@ export TOP_PID=$$
#H
#H database:
#H - H2
-#H - DB2_11_1
-#H - POSTGRES_10
+#H - DB2 | DB2_11_1
+#H - POSTGRES | POSTGRES_10
# Arguments:
# $1: exit code
function helpAndExit() {
@@ -34,10 +34,10 @@ function helpAndExit() {
function mapDBToDockerComposeServiceName() {
[[ -z "$1" || "$1" == "H2" ]] && return
case "$1" in
- DB2_11_1)
+ DB2|DB2_11_1)
echo "taskana-db2_11-1"
;;
- POSTGRES_10)
+ POSTGRES|POSTGRES_10)
echo "taskana-postgres_10"
;;
*)
@@ -54,7 +54,7 @@ function main() {
H2)
[[ -f "$propFile" ]] && rm "$propFile"
;;
- DB2_11_1)
+ DB2|DB2_11_1)
docker-compose -f $scriptDir/docker-compose.yml up -d "$(mapDBToDockerComposeServiceName "$1")"
echo 'jdbcDriver=com.ibm.db2.jcc.DB2Driver' > $propFile
@@ -63,7 +63,7 @@ function main() {
echo 'dbPassword=db2inst1-pwd' >> $propFile
echo 'schemaName=TASKANA' >> $propFile
;;
- POSTGRES_10)
+ POSTGRES|POSTGRES_10)
docker-compose -f $scriptDir/docker-compose.yml up -d "$(mapDBToDockerComposeServiceName "$1")"
echo 'jdbcDriver=org.postgresql.Driver' > $propFile
diff --git a/lib/taskana-core/src/test/java/acceptance/TaskanaIntegrationTestExtension.java b/lib/taskana-core/src/test/java/acceptance/TaskanaIntegrationTestExtension.java
index 5e36dc672..b72a9bd79 100644
--- a/lib/taskana-core/src/test/java/acceptance/TaskanaIntegrationTestExtension.java
+++ b/lib/taskana-core/src/test/java/acceptance/TaskanaIntegrationTestExtension.java
@@ -1,19 +1,22 @@
package acceptance;
+import org.junit.jupiter.api.extension.AfterAllCallback;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.api.extension.ParameterContext;
import org.junit.jupiter.api.extension.ParameterResolutionException;
import org.junit.jupiter.api.extension.ParameterResolver;
-import pro.taskana.common.test.config.DataSourceGenerator;
+import pro.taskana.common.test.config.TestContainerExtension;
-public class TaskanaIntegrationTestExtension implements ParameterResolver {
+public class TaskanaIntegrationTestExtension implements ParameterResolver, AfterAllCallback {
private final TaskanaDependencyInjectionExtension dependencyInjectionExtension;
+ private final TestContainerExtension testContainerExtension;
public TaskanaIntegrationTestExtension() throws Exception {
+ testContainerExtension = new TestContainerExtension();
dependencyInjectionExtension =
- new TaskanaDependencyInjectionExtension(DataSourceGenerator.getDataSource());
+ new TaskanaDependencyInjectionExtension(testContainerExtension.getDataSource());
}
@Override
@@ -29,4 +32,9 @@ public class TaskanaIntegrationTestExtension implements ParameterResolver {
throws ParameterResolutionException {
return dependencyInjectionExtension.resolveParameter(parameterContext, extensionContext);
}
+
+ @Override
+ public void afterAll(ExtensionContext context) {
+ testContainerExtension.afterAll(context);
+ }
}
diff --git a/lib/taskana-core/src/test/resources/container-license-acceptance.txt b/lib/taskana-core/src/test/resources/container-license-acceptance.txt
new file mode 100644
index 000000000..7f43861ab
--- /dev/null
+++ b/lib/taskana-core/src/test/resources/container-license-acceptance.txt
@@ -0,0 +1 @@
+taskana/db2:11.1
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index 936f32c57..0327e9038 100644
--- a/pom.xml
+++ b/pom.xml
@@ -88,6 +88,7 @@
0.8.7
1.2.0
2.0.11
+ 1.16.0
1.14.0