From 761c6edade158dac444d271ed2de37caea41ebfb Mon Sep 17 00:00:00 2001 From: Mustapha Zorgati <15628173+mustaphazorgati@users.noreply.github.com> Date: Mon, 20 Sep 2021 14:17:35 +0200 Subject: [PATCH] TSK-1730: Allow modification of TaskanaEngineConfiguration for tests --- common/taskana-common-test/pom.xml | 22 --- .../common/test/security/JaasExtension.java | 19 +- lib/taskana-core/pom.xml | 24 +++ .../java/acceptance/ArchitectureTest.java | 29 +++ .../TaskanaDependencyInjectionExtension.java | 69 ------- ...skanaDependencyInjectionExtensionTest.java | 185 ------------------ .../TaskanaIntegrationTestExtension.java | 40 ---- .../builder/ClassificationBuilderTest.java | 13 +- .../builder/TaskAttachmentBuilderTest.java | 16 +- .../acceptance/builder/TaskBuilderTest.java | 23 +-- .../builder/TaskCommentBuilderTest.java | 21 +- .../WorkbasketAccessItemBuilderTest.java | 17 +- .../builder/WorkbasketBuilderTest.java | 16 +- .../java/testapi/CleanTaskanaContext.java | 10 + .../TaskanaEngineConfigurationModifier.java | 9 + .../src/test/java/testapi/TaskanaInject.java | 10 + .../java/testapi/TaskanaIntegrationTest.java | 26 +++ .../TaskanaDependencyInjectionExtension.java | 63 ++++++ .../TaskanaInitializationExtension.java | 100 ++++++++++ .../extensions/TestContainerExtension.java | 115 +++++++++++ ...skanaDependencyInjectionExtensionTest.java | 152 ++++++++++++++ .../TaskanaInitializationExtensionTest.java | 78 ++++++++ .../tests/TestContainerExtensionTest.java | 95 +++++++++ .../testapi/util/DockerContainerCreator.java | 85 +------- .../testapi/util/ExtensionCommunicator.java | 40 ++++ 25 files changed, 800 insertions(+), 477 deletions(-) delete mode 100644 lib/taskana-core/src/test/java/acceptance/TaskanaDependencyInjectionExtension.java delete mode 100644 lib/taskana-core/src/test/java/acceptance/TaskanaDependencyInjectionExtensionTest.java delete mode 100644 lib/taskana-core/src/test/java/acceptance/TaskanaIntegrationTestExtension.java create mode 100644 lib/taskana-core/src/test/java/testapi/CleanTaskanaContext.java create mode 100644 lib/taskana-core/src/test/java/testapi/TaskanaEngineConfigurationModifier.java create mode 100644 lib/taskana-core/src/test/java/testapi/TaskanaInject.java create mode 100644 lib/taskana-core/src/test/java/testapi/TaskanaIntegrationTest.java create mode 100644 lib/taskana-core/src/test/java/testapi/extensions/TaskanaDependencyInjectionExtension.java create mode 100644 lib/taskana-core/src/test/java/testapi/extensions/TaskanaInitializationExtension.java create mode 100644 lib/taskana-core/src/test/java/testapi/extensions/TestContainerExtension.java create mode 100644 lib/taskana-core/src/test/java/testapi/tests/TaskanaDependencyInjectionExtensionTest.java create mode 100644 lib/taskana-core/src/test/java/testapi/tests/TaskanaInitializationExtensionTest.java create mode 100644 lib/taskana-core/src/test/java/testapi/tests/TestContainerExtensionTest.java rename common/taskana-common-test/src/main/java/pro/taskana/common/test/config/TestContainerExtension.java => lib/taskana-core/src/test/java/testapi/util/DockerContainerCreator.java (53%) create mode 100644 lib/taskana-core/src/test/java/testapi/util/ExtensionCommunicator.java diff --git a/common/taskana-common-test/pom.xml b/common/taskana-common-test/pom.xml index 13a3d0566..17e7decb1 100644 --- a/common/taskana-common-test/pom.xml +++ b/common/taskana-common-test/pom.xml @@ -106,28 +106,6 @@ 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/pro/taskana/common/test/security/JaasExtension.java b/common/taskana-common-test/src/main/java/pro/taskana/common/test/security/JaasExtension.java index a8d67fb86..80bd477ef 100644 --- a/common/taskana-common-test/src/main/java/pro/taskana/common/test/security/JaasExtension.java +++ b/common/taskana-common-test/src/main/java/pro/taskana/common/test/security/JaasExtension.java @@ -139,7 +139,7 @@ public class JaasExtension implements InvocationInterceptor, TestTemplateInvocat StreamSupport.stream(newChildrenForDynamicContainer.spliterator(), false) .map(x -> duplicateDynamicNode(x, childrenMap))); - Store store = getStore(extensionContext); + Store store = getMethodLevelStore(extensionContext); return (T) Stream.of(annotation.value()) .peek(a -> store.put(ACCESS_IDS_STORE_KEY, a)) @@ -155,7 +155,7 @@ public class JaasExtension implements InvocationInterceptor, TestTemplateInvocat ReflectiveInvocationContext invocationContext, ExtensionContext extensionContext) { WithAccessId accessId = - getStore(extensionContext).get(ACCESS_IDS_STORE_KEY, WithAccessId.class); + getMethodLevelStore(extensionContext).get(ACCESS_IDS_STORE_KEY, WithAccessId.class); performInvocationWithAccessId(invocation, accessId); } @@ -163,7 +163,7 @@ public class JaasExtension implements InvocationInterceptor, TestTemplateInvocat public void interceptDynamicTest(Invocation invocation, ExtensionContext extensionContext) { ExtensionContext testContext = getParentMethodExtensionContent(extensionContext); // Check if the test factory provided an access Id for this dynamic test. - WithAccessId o = getStore(testContext).get(ACCESS_IDS_STORE_KEY, WithAccessId.class); + WithAccessId o = getMethodLevelStore(testContext).get(ACCESS_IDS_STORE_KEY, WithAccessId.class); if (o != null) { performInvocationWithAccessId(invocation, o); } else { @@ -202,7 +202,7 @@ public class JaasExtension implements InvocationInterceptor, TestTemplateInvocat ExtensionContext context) { List accessIds = AnnotationSupport.findRepeatableAnnotations(context.getElement(), WithAccessId.class); - Store store = getStore(context); + Store store = getMethodLevelStore(context); return accessIds.stream() .peek(a -> store.put(ACCESS_IDS_STORE_KEY, a)) .map(JaasExtensionInvocationContext::new); @@ -279,14 +279,9 @@ public class JaasExtension implements InvocationInterceptor, TestTemplateInvocat "Test '%s' does not have a parent method", extensionContext.getUniqueId()))); } - /** - * Gets the store with a method-level scope. - * - * @param context context for current extension - * @return The store - */ - private Store getStore(ExtensionContext context) { - return context.getStore(Namespace.create(getClass(), context.getRequiredTestMethod())); + private static Store getMethodLevelStore(ExtensionContext context) { + return context.getStore( + Namespace.create(context.getRequiredTestClass(), context.getRequiredTestMethod())); } private static String getDisplayNameForAccessId(WithAccessId withAccessId) { diff --git a/lib/taskana-core/pom.xml b/lib/taskana-core/pom.xml index 6e7323730..6c865ed41 100644 --- a/lib/taskana-core/pom.xml +++ b/lib/taskana-core/pom.xml @@ -111,6 +111,30 @@ ${version.archunit} test + + org.testcontainers + db2 + ${version.testcontainers} + test + + + junit + junit + + + + + org.testcontainers + postgresql + ${version.testcontainers} + test + + + junit + junit + + + diff --git a/lib/taskana-core/src/test/java/acceptance/ArchitectureTest.java b/lib/taskana-core/src/test/java/acceptance/ArchitectureTest.java index 8cde5567e..5b90bd462 100644 --- a/lib/taskana-core/src/test/java/acceptance/ArchitectureTest.java +++ b/lib/taskana-core/src/test/java/acceptance/ArchitectureTest.java @@ -12,6 +12,7 @@ import com.tngtech.archunit.base.Optional; import com.tngtech.archunit.core.domain.JavaClass; import com.tngtech.archunit.core.domain.JavaClass.Predicates; import com.tngtech.archunit.core.domain.JavaClasses; +import com.tngtech.archunit.core.domain.JavaField; import com.tngtech.archunit.core.domain.JavaMethod; import com.tngtech.archunit.core.importer.ClassFileImporter; import com.tngtech.archunit.lang.ArchCondition; @@ -39,6 +40,7 @@ import org.junit.jupiter.api.DynamicTest; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestFactory; import org.junit.jupiter.api.function.ThrowingConsumer; +import testapi.TaskanaIntegrationTest; import pro.taskana.common.api.exceptions.ErrorCode; import pro.taskana.common.api.exceptions.TaskanaException; @@ -294,6 +296,33 @@ class ArchitectureTest { rule.check(importedClasses); } + @Test + void taskanaIntegrationTestsShouldOnlyHavePackagePrivateFields() { + ArchRule rule = + classes() + .that() + .areAnnotatedWith(TaskanaIntegrationTest.class) + .should(onlyHaveFieldsWithNoModifier()); + + rule.check(importedClasses); + } + + private ArchCondition onlyHaveFieldsWithNoModifier() { + return new ArchCondition("only have fields with no modifier") { + @Override + public void check(JavaClass item, ConditionEvents events) { + for (JavaField field : item.getAllFields()) { + if (!field.getModifiers().isEmpty()) { + events.add( + SimpleConditionEvent.violated( + item, + String.format("Field '%s' should not have any modifier", field.getFullName()))); + } + } + } + }; + } + private static ArchCondition beDefinedInTaskanaSubPackages( List excludePackages) { return new ArchCondition("all be defined in TASKANA_SUB_PACKAGES") { diff --git a/lib/taskana-core/src/test/java/acceptance/TaskanaDependencyInjectionExtension.java b/lib/taskana-core/src/test/java/acceptance/TaskanaDependencyInjectionExtension.java deleted file mode 100644 index b9ca72796..000000000 --- a/lib/taskana-core/src/test/java/acceptance/TaskanaDependencyInjectionExtension.java +++ /dev/null @@ -1,69 +0,0 @@ -package acceptance; - -import java.util.Map; -import javax.sql.DataSource; -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 org.junit.platform.commons.JUnitException; - -import pro.taskana.TaskanaEngineConfiguration; -import pro.taskana.classification.internal.ClassificationServiceImpl; -import pro.taskana.common.api.TaskanaEngine; -import pro.taskana.common.api.TaskanaEngine.ConnectionManagementMode; -import pro.taskana.common.api.WorkingDaysToDaysConverter; -import pro.taskana.common.internal.JobServiceImpl; -import pro.taskana.common.internal.TaskanaEngineImpl; -import pro.taskana.common.test.config.DataSourceGenerator; -import pro.taskana.monitor.internal.MonitorServiceImpl; -import pro.taskana.task.internal.TaskServiceImpl; -import pro.taskana.workbasket.internal.WorkbasketServiceImpl; - -public class TaskanaDependencyInjectionExtension implements ParameterResolver { - - private final Map, Object> instanceByClass; - - public TaskanaDependencyInjectionExtension(DataSource dataSource) throws Exception { - String schemaName = DataSourceGenerator.getSchemaName(); - TaskanaEngineConfiguration taskanaEngineConfiguration = - new TaskanaEngineConfiguration(dataSource, false, schemaName); - taskanaEngineConfiguration.setGermanPublicHolidaysEnabled(true); - TaskanaEngine taskanaEngine = taskanaEngineConfiguration.buildTaskanaEngine(); - taskanaEngine.setConnectionManagementMode(ConnectionManagementMode.AUTOCOMMIT); - instanceByClass = - Map.ofEntries( - Map.entry(TaskanaEngineConfiguration.class, taskanaEngineConfiguration), - Map.entry(TaskanaEngineImpl.class, taskanaEngine), - Map.entry(TaskServiceImpl.class, taskanaEngine.getTaskService()), - Map.entry(MonitorServiceImpl.class, taskanaEngine.getMonitorService()), - Map.entry(WorkbasketServiceImpl.class, taskanaEngine.getWorkbasketService()), - Map.entry(ClassificationServiceImpl.class, taskanaEngine.getClassificationService()), - Map.entry(JobServiceImpl.class, taskanaEngine.getJobService()), - Map.entry( - WorkingDaysToDaysConverter.class, taskanaEngine.getWorkingDaysToDaysConverter())); - } - - @Override - public boolean supportsParameter( - ParameterContext parameterContext, ExtensionContext extensionContext) - throws ParameterResolutionException { - return instanceByClass.keySet().stream() - .anyMatch(getParameterType(parameterContext)::isAssignableFrom); - } - - @Override - public Object resolveParameter( - ParameterContext parameterContext, ExtensionContext extensionContext) - throws ParameterResolutionException { - return instanceByClass.keySet().stream() - .filter(getParameterType(parameterContext)::isAssignableFrom) - .map(instanceByClass::get) - .findFirst() - .orElseThrow(() -> new JUnitException("This should never happen.")); - } - - private Class getParameterType(ParameterContext parameterContext) { - return parameterContext.getParameter().getType(); - } -} diff --git a/lib/taskana-core/src/test/java/acceptance/TaskanaDependencyInjectionExtensionTest.java b/lib/taskana-core/src/test/java/acceptance/TaskanaDependencyInjectionExtensionTest.java deleted file mode 100644 index 452125d16..000000000 --- a/lib/taskana-core/src/test/java/acceptance/TaskanaDependencyInjectionExtensionTest.java +++ /dev/null @@ -1,185 +0,0 @@ -package acceptance; - -import static org.assertj.core.api.AssertionsForClassTypes.assertThat; - -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; - -import pro.taskana.TaskanaEngineConfiguration; -import pro.taskana.classification.api.ClassificationService; -import pro.taskana.classification.internal.ClassificationServiceImpl; -import pro.taskana.common.api.JobService; -import pro.taskana.common.api.TaskanaEngine; -import pro.taskana.common.api.WorkingDaysToDaysConverter; -import pro.taskana.common.internal.JobServiceImpl; -import pro.taskana.common.internal.TaskanaEngineImpl; -import pro.taskana.monitor.api.MonitorService; -import pro.taskana.monitor.internal.MonitorServiceImpl; -import pro.taskana.task.api.TaskService; -import pro.taskana.task.internal.TaskServiceImpl; -import pro.taskana.workbasket.api.WorkbasketService; -import pro.taskana.workbasket.internal.WorkbasketServiceImpl; - -@ExtendWith(TaskanaIntegrationTestExtension.class) -public class TaskanaDependencyInjectionExtensionTest { - - @Nested - class TaskanaEngineConfigurationInjectionTest { - private final TaskanaEngineConfiguration taskanaEngineConfiguration; - - public TaskanaEngineConfigurationInjectionTest( - TaskanaEngineConfiguration taskanaEngineConfiguration) { - this.taskanaEngineConfiguration = taskanaEngineConfiguration; - } - - @Test - void should_InjectTaskanaEngineConfiguration_When_ConstructorContainsParameter() { - assertThat(taskanaEngineConfiguration).isNotNull(); - } - } - - @Nested - class TaskanaEngineInjectionTest { - private final TaskanaEngine taskanaEngine; - private final TaskanaEngineImpl taskanaEngineImpl; - - TaskanaEngineInjectionTest(TaskanaEngine taskanaEngine, TaskanaEngineImpl taskanaEngineImpl) { - this.taskanaEngine = taskanaEngine; - this.taskanaEngineImpl = taskanaEngineImpl; - } - - @Test - void should_InjectTaskanaEngine_When_ConstructorContainsParameter() { - assertThat(taskanaEngine).isNotNull(); - } - - @Test - void should_InjectTaskanaEngineImpl_When_ConstructorContainsParameter() { - assertThat(taskanaEngineImpl).isNotNull(); - } - } - - @Nested - class TaskServiceInjectionTest { - private final TaskService taskService; - private final TaskServiceImpl taskServiceImpl; - - TaskServiceInjectionTest(TaskService taskService, TaskServiceImpl taskServiceImpl) { - this.taskService = taskService; - this.taskServiceImpl = taskServiceImpl; - } - - @Test - void should_InjectTaskService_When_ConstructorContainsParameter() { - assertThat(taskService).isNotNull(); - } - - @Test - void should_InjectTaskServiceImpl_When_ConstructorContainsParameter() { - assertThat(taskServiceImpl).isNotNull(); - } - } - - @Nested - class MonitorServiceInjectionTest { - private final MonitorService monitorService; - private final MonitorServiceImpl monitorServiceImpl; - - MonitorServiceInjectionTest( - MonitorService monitorService, MonitorServiceImpl monitorServiceImpl) { - this.monitorService = monitorService; - this.monitorServiceImpl = monitorServiceImpl; - } - - @Test - void should_InjectMonitorService_When_ConstructorContainsParameter() { - assertThat(monitorService).isNotNull(); - } - - @Test - void should_InjectMonitorServiceImpl_When_ConstructorContainsParameter() { - assertThat(monitorServiceImpl).isNotNull(); - } - } - - @Nested - class WorkbasketServiceInjectionTest { - private final WorkbasketService workbasketService; - private final WorkbasketServiceImpl workbasketServiceImpl; - - WorkbasketServiceInjectionTest( - WorkbasketService workbasketService, WorkbasketServiceImpl workbasketServiceImpl) { - this.workbasketService = workbasketService; - this.workbasketServiceImpl = workbasketServiceImpl; - } - - @Test - void should_InjectWorkbasketService_When_ConstructorContainsParameter() { - assertThat(workbasketService).isNotNull(); - } - - @Test - void should_InjectWorkbasketServiceImpl_When_ConstructorContainsParameter() { - assertThat(workbasketServiceImpl).isNotNull(); - } - } - - @Nested - class ClassificationServiceInjectionTest { - private final ClassificationService classificationService; - private final ClassificationServiceImpl classificationServiceImpl; - - ClassificationServiceInjectionTest( - ClassificationService classificationService, - ClassificationServiceImpl classificationServiceImpl) { - this.classificationService = classificationService; - this.classificationServiceImpl = classificationServiceImpl; - } - - @Test - void should_InjectClassificationService_When_ConstructorContainsParameter() { - assertThat(classificationService).isNotNull(); - } - - @Test - void should_InjectClassificationServiceImpl_When_ConstructorContainsParameter() { - assertThat(classificationServiceImpl).isNotNull(); - } - } - - @Nested - class JobServiceInjectionTest { - private final JobService jobService; - private final JobServiceImpl jobServiceImpl; - - JobServiceInjectionTest(JobService jobService, JobServiceImpl jobServiceImpl) { - this.jobService = jobService; - this.jobServiceImpl = jobServiceImpl; - } - - @Test - void should_InjectJobService_When_ConstructorContainsParameter() { - assertThat(jobService).isNotNull(); - } - - @Test - void should_InjectJobServiceImpl_When_ConstructorContainsParameter() { - assertThat(jobServiceImpl).isNotNull(); - } - } - - @Nested - class WorkingDaysToDaysConverterInjectionTest { - private final WorkingDaysToDaysConverter workingDaysToDaysConverter; - - WorkingDaysToDaysConverterInjectionTest(WorkingDaysToDaysConverter workingDaysToDaysConverter) { - this.workingDaysToDaysConverter = workingDaysToDaysConverter; - } - - @Test - void should_InjectWorkingDaysToDaysConverter_When_ConstructorContainsParameter() { - assertThat(workingDaysToDaysConverter).isNotNull(); - } - } -} diff --git a/lib/taskana-core/src/test/java/acceptance/TaskanaIntegrationTestExtension.java b/lib/taskana-core/src/test/java/acceptance/TaskanaIntegrationTestExtension.java deleted file mode 100644 index b72a9bd79..000000000 --- a/lib/taskana-core/src/test/java/acceptance/TaskanaIntegrationTestExtension.java +++ /dev/null @@ -1,40 +0,0 @@ -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.TestContainerExtension; - -public class TaskanaIntegrationTestExtension implements ParameterResolver, AfterAllCallback { - - private final TaskanaDependencyInjectionExtension dependencyInjectionExtension; - private final TestContainerExtension testContainerExtension; - - public TaskanaIntegrationTestExtension() throws Exception { - testContainerExtension = new TestContainerExtension(); - dependencyInjectionExtension = - new TaskanaDependencyInjectionExtension(testContainerExtension.getDataSource()); - } - - @Override - public boolean supportsParameter( - ParameterContext parameterContext, ExtensionContext extensionContext) - throws ParameterResolutionException { - return dependencyInjectionExtension.supportsParameter(parameterContext, extensionContext); - } - - @Override - public Object resolveParameter( - ParameterContext parameterContext, ExtensionContext extensionContext) - throws ParameterResolutionException { - return dependencyInjectionExtension.resolveParameter(parameterContext, extensionContext); - } - - @Override - public void afterAll(ExtensionContext context) { - testContainerExtension.afterAll(context); - } -} diff --git a/lib/taskana-core/src/test/java/acceptance/builder/ClassificationBuilderTest.java b/lib/taskana-core/src/test/java/acceptance/builder/ClassificationBuilderTest.java index c05e72b46..1d9431757 100644 --- a/lib/taskana-core/src/test/java/acceptance/builder/ClassificationBuilderTest.java +++ b/lib/taskana-core/src/test/java/acceptance/builder/ClassificationBuilderTest.java @@ -4,7 +4,6 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatCode; import static pro.taskana.classification.internal.ClassificationBuilder.newClassification; -import acceptance.TaskanaIntegrationTestExtension; import java.time.Instant; import java.util.List; import java.util.function.BiFunction; @@ -14,7 +13,8 @@ import org.junit.jupiter.api.DynamicContainer; import org.junit.jupiter.api.DynamicTest; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestFactory; -import org.junit.jupiter.api.extension.ExtendWith; +import testapi.TaskanaInject; +import testapi.TaskanaIntegrationTest; import pro.taskana.classification.api.ClassificationCustomField; import pro.taskana.classification.api.ClassificationService; @@ -22,17 +22,12 @@ import pro.taskana.classification.api.models.Classification; import pro.taskana.classification.internal.ClassificationBuilder; import pro.taskana.classification.internal.models.ClassificationImpl; import pro.taskana.common.internal.util.Quadruple; -import pro.taskana.common.test.security.JaasExtension; import pro.taskana.common.test.security.WithAccessId; -@ExtendWith({JaasExtension.class, TaskanaIntegrationTestExtension.class}) +@TaskanaIntegrationTest class ClassificationBuilderTest { - private final ClassificationService classificationService; - - public ClassificationBuilderTest(ClassificationService classificationService) { - this.classificationService = classificationService; - } + @TaskanaInject ClassificationService classificationService; @WithAccessId(user = "businessadmin") @Test diff --git a/lib/taskana-core/src/test/java/acceptance/builder/TaskAttachmentBuilderTest.java b/lib/taskana-core/src/test/java/acceptance/builder/TaskAttachmentBuilderTest.java index fcf433efd..4db8d871e 100644 --- a/lib/taskana-core/src/test/java/acceptance/builder/TaskAttachmentBuilderTest.java +++ b/lib/taskana-core/src/test/java/acceptance/builder/TaskAttachmentBuilderTest.java @@ -3,32 +3,26 @@ package acceptance.builder; import static org.assertj.core.api.AssertionsForClassTypes.assertThat; import acceptance.DefaultTestEntities; -import acceptance.TaskanaIntegrationTestExtension; import java.time.Instant; import java.util.Map; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; +import testapi.TaskanaInject; +import testapi.TaskanaIntegrationTest; import pro.taskana.classification.api.ClassificationService; import pro.taskana.classification.api.models.ClassificationSummary; import pro.taskana.classification.internal.ClassificationBuilder; -import pro.taskana.common.test.security.JaasExtension; import pro.taskana.task.api.TaskService; import pro.taskana.task.api.models.Attachment; import pro.taskana.task.api.models.ObjectReference; import pro.taskana.task.internal.TaskAttachmentBuilder; import pro.taskana.task.internal.models.AttachmentImpl; -@ExtendWith({JaasExtension.class, TaskanaIntegrationTestExtension.class}) +@TaskanaIntegrationTest class TaskAttachmentBuilderTest { - private final ClassificationService classificationService; - private final TaskService taskService; - - TaskAttachmentBuilderTest(ClassificationService classificationService, TaskService taskService) { - this.classificationService = classificationService; - this.taskService = taskService; - } + @TaskanaInject ClassificationService classificationService; + @TaskanaInject TaskService taskService; @Test void should_PopulateAttachment_When_UsingEveryBuilderFunction() throws Exception { diff --git a/lib/taskana-core/src/test/java/acceptance/builder/TaskBuilderTest.java b/lib/taskana-core/src/test/java/acceptance/builder/TaskBuilderTest.java index ee51bd7ce..373e57bf9 100644 --- a/lib/taskana-core/src/test/java/acceptance/builder/TaskBuilderTest.java +++ b/lib/taskana-core/src/test/java/acceptance/builder/TaskBuilderTest.java @@ -9,7 +9,6 @@ import static pro.taskana.common.internal.util.CheckedSupplier.wrap; import static pro.taskana.task.internal.builder.TaskBuilder.newTask; import static pro.taskana.workbasket.internal.WorkbasketAccessItemBuilder.newWorkbasketAccessItem; -import acceptance.TaskanaIntegrationTestExtension; import java.time.Instant; import java.util.List; import java.util.Map; @@ -21,15 +20,13 @@ import org.junit.jupiter.api.DynamicContainer; import org.junit.jupiter.api.DynamicTest; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestFactory; -import org.junit.jupiter.api.TestInstance; -import org.junit.jupiter.api.TestInstance.Lifecycle; -import org.junit.jupiter.api.extension.ExtendWith; +import testapi.TaskanaInject; +import testapi.TaskanaIntegrationTest; import pro.taskana.classification.api.ClassificationService; import pro.taskana.classification.api.models.ClassificationSummary; import pro.taskana.common.api.TaskanaEngine; import pro.taskana.common.internal.util.Quadruple; -import pro.taskana.common.test.security.JaasExtension; import pro.taskana.common.test.security.WithAccessId; import pro.taskana.task.api.CallbackState; import pro.taskana.task.api.TaskCustomField; @@ -44,19 +41,13 @@ import pro.taskana.workbasket.api.WorkbasketPermission; import pro.taskana.workbasket.api.WorkbasketService; import pro.taskana.workbasket.api.models.WorkbasketSummary; -@ExtendWith({JaasExtension.class, TaskanaIntegrationTestExtension.class}) -@TestInstance(Lifecycle.PER_CLASS) +@TaskanaIntegrationTest class TaskBuilderTest { - private final TaskanaEngine taskanaEngine; - private final TaskService taskService; + @TaskanaInject TaskanaEngine taskanaEngine; + @TaskanaInject TaskService taskService; - private WorkbasketSummary workbasketSummary; - private ClassificationSummary classificationSummary; - - TaskBuilderTest(TaskanaEngine taskanaEngine, TaskService taskService) { - this.taskanaEngine = taskanaEngine; - this.taskService = taskService; - } + WorkbasketSummary workbasketSummary; + ClassificationSummary classificationSummary; @WithAccessId(user = "businessadmin") @BeforeAll diff --git a/lib/taskana-core/src/test/java/acceptance/builder/TaskCommentBuilderTest.java b/lib/taskana-core/src/test/java/acceptance/builder/TaskCommentBuilderTest.java index 60ebd20f5..6ebe5328a 100644 --- a/lib/taskana-core/src/test/java/acceptance/builder/TaskCommentBuilderTest.java +++ b/lib/taskana-core/src/test/java/acceptance/builder/TaskCommentBuilderTest.java @@ -9,7 +9,6 @@ import static pro.taskana.common.internal.util.CheckedSupplier.wrap; import static pro.taskana.task.internal.TaskCommentBuilder.newTaskComment; import static pro.taskana.workbasket.internal.WorkbasketAccessItemBuilder.newWorkbasketAccessItem; -import acceptance.TaskanaIntegrationTestExtension; import java.time.Instant; import java.util.List; import java.util.function.BiFunction; @@ -20,15 +19,13 @@ import org.junit.jupiter.api.DynamicContainer; import org.junit.jupiter.api.DynamicTest; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestFactory; -import org.junit.jupiter.api.TestInstance; -import org.junit.jupiter.api.TestInstance.Lifecycle; -import org.junit.jupiter.api.extension.ExtendWith; +import testapi.TaskanaInject; +import testapi.TaskanaIntegrationTest; import pro.taskana.classification.api.ClassificationService; import pro.taskana.classification.api.models.Classification; import pro.taskana.common.api.TaskanaEngine; import pro.taskana.common.internal.util.Quadruple; -import pro.taskana.common.test.security.JaasExtension; import pro.taskana.common.test.security.WithAccessId; import pro.taskana.task.api.TaskService; import pro.taskana.task.api.models.ObjectReference; @@ -41,19 +38,13 @@ import pro.taskana.workbasket.api.WorkbasketPermission; import pro.taskana.workbasket.api.WorkbasketService; import pro.taskana.workbasket.api.models.Workbasket; -@ExtendWith({JaasExtension.class, TaskanaIntegrationTestExtension.class}) -@TestInstance(Lifecycle.PER_CLASS) +@TaskanaIntegrationTest class TaskCommentBuilderTest { - private final TaskService taskService; - private final TaskanaEngine taskanaEngine; + @TaskanaInject TaskService taskService; + @TaskanaInject TaskanaEngine taskanaEngine; - private Task task; - - TaskCommentBuilderTest(TaskService taskService, TaskanaEngine taskanaEngine) { - this.taskService = taskService; - this.taskanaEngine = taskanaEngine; - } + Task task; @WithAccessId(user = "businessadmin") @BeforeAll diff --git a/lib/taskana-core/src/test/java/acceptance/builder/WorkbasketAccessItemBuilderTest.java b/lib/taskana-core/src/test/java/acceptance/builder/WorkbasketAccessItemBuilderTest.java index 68a7f4dd4..01f09eee8 100644 --- a/lib/taskana-core/src/test/java/acceptance/builder/WorkbasketAccessItemBuilderTest.java +++ b/lib/taskana-core/src/test/java/acceptance/builder/WorkbasketAccessItemBuilderTest.java @@ -6,13 +6,12 @@ import static org.assertj.core.api.AssertionsForClassTypes.assertThatCode; import static pro.taskana.common.internal.util.CheckedSupplier.wrap; import static pro.taskana.workbasket.internal.WorkbasketAccessItemBuilder.newWorkbasketAccessItem; -import acceptance.TaskanaIntegrationTestExtension; import java.util.List; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; +import testapi.TaskanaInject; +import testapi.TaskanaIntegrationTest; import pro.taskana.common.api.TaskanaEngine; -import pro.taskana.common.test.security.JaasExtension; import pro.taskana.common.test.security.WithAccessId; import pro.taskana.workbasket.api.WorkbasketPermission; import pro.taskana.workbasket.api.WorkbasketService; @@ -21,17 +20,11 @@ import pro.taskana.workbasket.api.models.WorkbasketAccessItem; import pro.taskana.workbasket.internal.WorkbasketAccessItemBuilder; import pro.taskana.workbasket.internal.models.WorkbasketAccessItemImpl; -@ExtendWith({JaasExtension.class, TaskanaIntegrationTestExtension.class}) +@TaskanaIntegrationTest class WorkbasketAccessItemBuilderTest { - private final WorkbasketService workbasketService; - private final TaskanaEngine taskanaEngine; - - WorkbasketAccessItemBuilderTest( - WorkbasketService workbasketService, TaskanaEngine taskanaEngine) { - this.workbasketService = workbasketService; - this.taskanaEngine = taskanaEngine; - } + @TaskanaInject WorkbasketService workbasketService; + @TaskanaInject TaskanaEngine taskanaEngine; @WithAccessId(user = "businessadmin") @Test diff --git a/lib/taskana-core/src/test/java/acceptance/builder/WorkbasketBuilderTest.java b/lib/taskana-core/src/test/java/acceptance/builder/WorkbasketBuilderTest.java index 90dc4bd95..6e3d67b64 100644 --- a/lib/taskana-core/src/test/java/acceptance/builder/WorkbasketBuilderTest.java +++ b/lib/taskana-core/src/test/java/acceptance/builder/WorkbasketBuilderTest.java @@ -5,7 +5,6 @@ import static org.assertj.core.api.Assertions.assertThatCode; import static pro.taskana.common.internal.util.CheckedSupplier.wrap; import static pro.taskana.workbasket.internal.WorkbasketBuilder.newWorkbasket; -import acceptance.TaskanaIntegrationTestExtension; import java.time.Instant; import java.util.List; import java.util.function.BiFunction; @@ -15,11 +14,11 @@ import org.junit.jupiter.api.DynamicContainer; import org.junit.jupiter.api.DynamicTest; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestFactory; -import org.junit.jupiter.api.extension.ExtendWith; +import testapi.TaskanaInject; +import testapi.TaskanaIntegrationTest; import pro.taskana.common.api.TaskanaEngine; import pro.taskana.common.internal.util.Quadruple; -import pro.taskana.common.test.security.JaasExtension; import pro.taskana.common.test.security.WithAccessId; import pro.taskana.workbasket.api.WorkbasketCustomField; import pro.taskana.workbasket.api.WorkbasketService; @@ -28,16 +27,11 @@ import pro.taskana.workbasket.api.models.Workbasket; import pro.taskana.workbasket.internal.WorkbasketBuilder; import pro.taskana.workbasket.internal.models.WorkbasketImpl; -@ExtendWith({JaasExtension.class, TaskanaIntegrationTestExtension.class}) +@TaskanaIntegrationTest class WorkbasketBuilderTest { - private final WorkbasketService workbasketService; - private final TaskanaEngine taskanaEngine; - - WorkbasketBuilderTest(WorkbasketService workbasketService, TaskanaEngine taskanaEngine) { - this.workbasketService = workbasketService; - this.taskanaEngine = taskanaEngine; - } + @TaskanaInject WorkbasketService workbasketService; + @TaskanaInject TaskanaEngine taskanaEngine; @WithAccessId(user = "businessadmin") @Test diff --git a/lib/taskana-core/src/test/java/testapi/CleanTaskanaContext.java b/lib/taskana-core/src/test/java/testapi/CleanTaskanaContext.java new file mode 100644 index 000000000..458141c8c --- /dev/null +++ b/lib/taskana-core/src/test/java/testapi/CleanTaskanaContext.java @@ -0,0 +1,10 @@ +package testapi; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +public @interface CleanTaskanaContext {} diff --git a/lib/taskana-core/src/test/java/testapi/TaskanaEngineConfigurationModifier.java b/lib/taskana-core/src/test/java/testapi/TaskanaEngineConfigurationModifier.java new file mode 100644 index 000000000..77ca86e90 --- /dev/null +++ b/lib/taskana-core/src/test/java/testapi/TaskanaEngineConfigurationModifier.java @@ -0,0 +1,9 @@ +package testapi; + +import pro.taskana.TaskanaEngineConfiguration; + +public interface TaskanaEngineConfigurationModifier { + + void modify(TaskanaEngineConfiguration taskanaEngineConfiguration); + +} diff --git a/lib/taskana-core/src/test/java/testapi/TaskanaInject.java b/lib/taskana-core/src/test/java/testapi/TaskanaInject.java new file mode 100644 index 000000000..30adb6722 --- /dev/null +++ b/lib/taskana-core/src/test/java/testapi/TaskanaInject.java @@ -0,0 +1,10 @@ +package testapi; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +public @interface TaskanaInject {} diff --git a/lib/taskana-core/src/test/java/testapi/TaskanaIntegrationTest.java b/lib/taskana-core/src/test/java/testapi/TaskanaIntegrationTest.java new file mode 100644 index 000000000..e83032c5c --- /dev/null +++ b/lib/taskana-core/src/test/java/testapi/TaskanaIntegrationTest.java @@ -0,0 +1,26 @@ +package testapi; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import org.junit.jupiter.api.TestInstance; +import org.junit.jupiter.api.TestInstance.Lifecycle; +import org.junit.jupiter.api.extension.ExtendWith; +import testapi.extensions.TaskanaDependencyInjectionExtension; +import testapi.extensions.TaskanaInitializationExtension; +import testapi.extensions.TestContainerExtension; + +import pro.taskana.common.test.security.JaasExtension; + +@ExtendWith({ + // ORDER IS IMPORTANT! + JaasExtension.class, + TestContainerExtension.class, + TaskanaInitializationExtension.class, + TaskanaDependencyInjectionExtension.class, +}) +@TestInstance(Lifecycle.PER_CLASS) +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +public @interface TaskanaIntegrationTest {} diff --git a/lib/taskana-core/src/test/java/testapi/extensions/TaskanaDependencyInjectionExtension.java b/lib/taskana-core/src/test/java/testapi/extensions/TaskanaDependencyInjectionExtension.java new file mode 100644 index 000000000..556d5b59c --- /dev/null +++ b/lib/taskana-core/src/test/java/testapi/extensions/TaskanaDependencyInjectionExtension.java @@ -0,0 +1,63 @@ +package testapi.extensions; + +import static org.junit.platform.commons.support.AnnotationSupport.findAnnotatedFields; +import static testapi.util.ExtensionCommunicator.getClassLevelStore; + +import java.lang.reflect.Field; +import java.util.Map; +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 org.junit.jupiter.api.extension.TestInstancePostProcessor; +import org.junit.platform.commons.JUnitException; +import testapi.TaskanaInject; + +public class TaskanaDependencyInjectionExtension + implements ParameterResolver, TestInstancePostProcessor { + + @Override + public boolean supportsParameter( + ParameterContext parameterContext, ExtensionContext extensionContext) + throws ParameterResolutionException { + Map, Object> instanceByClass = getTaskanaEntityMap(extensionContext); + return instanceByClass != null + && instanceByClass.containsKey(parameterContext.getParameter().getType()); + } + + @Override + public Object resolveParameter( + ParameterContext parameterContext, ExtensionContext extensionContext) + throws ParameterResolutionException { + return getTaskanaEntityMap(extensionContext).get(parameterContext.getParameter().getType()); + } + + @Override + public void postProcessTestInstance(Object testInstance, ExtensionContext context) + throws Exception { + Map, Object> instanceByClass = getTaskanaEntityMap(context); + if (instanceByClass == null) { + throw new JUnitException("Something went wrong! Could not find TASKANA entity Map in store."); + } + + for (Field field : findAnnotatedFields(testInstance.getClass(), TaskanaInject.class)) { + Object toInject = instanceByClass.get(field.getType()); + if (toInject != null) { + field.setAccessible(true); + field.set(testInstance, toInject); + } else { + throw new JUnitException( + String.format( + "Cannot inject field '%s'. " + "Type '%s' is not an injectable TASKANA type", + field.getName(), field.getType())); + } + } + } + + @SuppressWarnings("unchecked") + private static Map, Object> getTaskanaEntityMap(ExtensionContext extensionContext) { + return (Map, Object>) + getClassLevelStore(extensionContext) + .get(TaskanaInitializationExtension.STORE_TASKANA_ENTITY_MAP); + } +} diff --git a/lib/taskana-core/src/test/java/testapi/extensions/TaskanaInitializationExtension.java b/lib/taskana-core/src/test/java/testapi/extensions/TaskanaInitializationExtension.java new file mode 100644 index 000000000..0b5483737 --- /dev/null +++ b/lib/taskana-core/src/test/java/testapi/extensions/TaskanaInitializationExtension.java @@ -0,0 +1,100 @@ +package testapi.extensions; + +import static org.junit.platform.commons.support.AnnotationSupport.isAnnotated; +import static testapi.util.ExtensionCommunicator.getClassLevelStore; +import static testapi.util.ExtensionCommunicator.isTopLevelClass; + +import java.util.Map; +import javax.sql.DataSource; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.api.extension.ExtensionContext.Store; +import org.junit.jupiter.api.extension.TestInstancePostProcessor; +import org.junit.platform.commons.JUnitException; +import testapi.CleanTaskanaContext; +import testapi.TaskanaEngineConfigurationModifier; + +import pro.taskana.TaskanaEngineConfiguration; +import pro.taskana.classification.api.ClassificationService; +import pro.taskana.classification.internal.ClassificationServiceImpl; +import pro.taskana.common.api.JobService; +import pro.taskana.common.api.TaskanaEngine; +import pro.taskana.common.api.TaskanaEngine.ConnectionManagementMode; +import pro.taskana.common.api.WorkingDaysToDaysConverter; +import pro.taskana.common.api.security.CurrentUserContext; +import pro.taskana.common.internal.JobServiceImpl; +import pro.taskana.common.internal.TaskanaEngineImpl; +import pro.taskana.common.internal.security.CurrentUserContextImpl; +import pro.taskana.monitor.api.MonitorService; +import pro.taskana.monitor.internal.MonitorServiceImpl; +import pro.taskana.task.api.TaskService; +import pro.taskana.task.internal.TaskServiceImpl; +import pro.taskana.workbasket.api.WorkbasketService; +import pro.taskana.workbasket.internal.WorkbasketServiceImpl; + +public class TaskanaInitializationExtension implements TestInstancePostProcessor { + + public static final String STORE_TASKANA_ENTITY_MAP = "taskanaEntityMap"; + + @Override + public void postProcessTestInstance(Object testInstance, ExtensionContext context) + throws Exception { + Class testClass = testInstance.getClass(); + if (isTopLevelClass(testClass) + || isAnnotated(testClass, CleanTaskanaContext.class) + || testInstance instanceof TaskanaEngineConfigurationModifier) { + Store store = getClassLevelStore(context); + TaskanaEngineConfiguration taskanaEngineConfiguration = + createDefaultTaskanaEngineConfiguration(store); + + if (testInstance instanceof TaskanaEngineConfigurationModifier) { + TaskanaEngineConfigurationModifier modifier = + (TaskanaEngineConfigurationModifier) testInstance; + modifier.modify(taskanaEngineConfiguration); + } + + TaskanaEngine taskanaEngine = taskanaEngineConfiguration.buildTaskanaEngine(); + taskanaEngine.setConnectionManagementMode(ConnectionManagementMode.AUTOCOMMIT); + + store.put(STORE_TASKANA_ENTITY_MAP, generateTaskanaEntityMap(taskanaEngine)); + } + } + + private static TaskanaEngineConfiguration createDefaultTaskanaEngineConfiguration(Store store) { + String schemaName = store.get(TestContainerExtension.STORE_SCHEMA_NAME, String.class); + if (schemaName == null) { + throw new JUnitException("Expected schemaName to be defined in store, but it's not."); + } + DataSource dataSource = store.get(TestContainerExtension.STORE_DATA_SOURCE, DataSource.class); + if (dataSource == null) { + throw new JUnitException("Expected dataSource to be defined in store, but it's not."); + } + + return new TaskanaEngineConfiguration(dataSource, false, schemaName); + } + + private static Map, Object> generateTaskanaEntityMap(TaskanaEngine taskanaEngine) { + TaskService taskService = taskanaEngine.getTaskService(); + MonitorService monitorService = taskanaEngine.getMonitorService(); + WorkbasketService workbasketService = taskanaEngine.getWorkbasketService(); + ClassificationService classificationService = taskanaEngine.getClassificationService(); + JobService jobService = taskanaEngine.getJobService(); + CurrentUserContext currentUserContext = taskanaEngine.getCurrentUserContext(); + return Map.ofEntries( + Map.entry(TaskanaEngineConfiguration.class, taskanaEngine.getConfiguration()), + Map.entry(TaskanaEngineImpl.class, taskanaEngine), + Map.entry(TaskanaEngine.class, taskanaEngine), + Map.entry(TaskService.class, taskService), + Map.entry(TaskServiceImpl.class, taskService), + Map.entry(MonitorService.class, monitorService), + Map.entry(MonitorServiceImpl.class, monitorService), + Map.entry(WorkbasketService.class, workbasketService), + Map.entry(WorkbasketServiceImpl.class, workbasketService), + Map.entry(ClassificationService.class, classificationService), + Map.entry(ClassificationServiceImpl.class, classificationService), + Map.entry(JobService.class, jobService), + Map.entry(JobServiceImpl.class, jobService), + Map.entry(CurrentUserContext.class, currentUserContext), + Map.entry(CurrentUserContextImpl.class, currentUserContext), + Map.entry(WorkingDaysToDaysConverter.class, taskanaEngine.getWorkingDaysToDaysConverter())); + } +} diff --git a/lib/taskana-core/src/test/java/testapi/extensions/TestContainerExtension.java b/lib/taskana-core/src/test/java/testapi/extensions/TestContainerExtension.java new file mode 100644 index 000000000..9d56c8176 --- /dev/null +++ b/lib/taskana-core/src/test/java/testapi/extensions/TestContainerExtension.java @@ -0,0 +1,115 @@ +package testapi.extensions; + +import static org.junit.platform.commons.support.AnnotationSupport.isAnnotated; +import static testapi.util.DockerContainerCreator.createDockerContainer; +import static testapi.util.ExtensionCommunicator.getClassLevelStore; +import static testapi.util.ExtensionCommunicator.isTopLevelClass; + +import java.lang.reflect.Constructor; +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.junit.jupiter.api.extension.ExtensionContext.Store; +import org.junit.jupiter.api.extension.InvocationInterceptor; +import org.junit.jupiter.api.extension.ReflectiveInvocationContext; +import org.junit.platform.commons.support.AnnotationSupport; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.JdbcDatabaseContainer; +import testapi.CleanTaskanaContext; +import testapi.TaskanaEngineConfigurationModifier; +import testapi.util.DockerContainerCreator; + +import pro.taskana.common.internal.configuration.DB; + +public class TestContainerExtension implements AfterAllCallback, InvocationInterceptor { + + public static final String STORE_DATA_SOURCE = "datasource"; + public static final String STORE_CONTAINER = "container"; + public static final String STORE_SCHEMA_NAME = "schemaName"; + + @Override + public T interceptTestClassConstructor( + Invocation invocation, + ReflectiveInvocationContext> invocationContext, + ExtensionContext extensionContext) + throws Throwable { + Class testClass = extensionContext.getRequiredTestClass(); + if (isTopLevelClass(testClass) || isAnnotated(testClass, CleanTaskanaContext.class)) { + Store store = getClassLevelStore(extensionContext); + DB db = retrieveDatabaseFromEnv(); + store.put(STORE_SCHEMA_NAME, determineSchemaName(db)); + + createDockerContainer(db) + .ifPresentOrElse( + container -> { + container.start(); + store.put(STORE_DATA_SOURCE, DockerContainerCreator.createDataSource(container)); + store.put(STORE_CONTAINER, container); + }, + () -> store.put(STORE_DATA_SOURCE, createDataSourceForH2())); + + } else if (TaskanaEngineConfigurationModifier.class.isAssignableFrom(testClass)) { + // since the implementation of TaskanaEngineConfigurationModifier implies the generation of a + // new TaskanaEngine, we have to copy the schema name and datasource from the enclosing class' + // store to the testClass store. + // This allows the following extensions to generate a new TaskanaEngine for the testClass. + Store parentStore = getClassLevelStore(extensionContext, testClass.getEnclosingClass()); + Store store = getClassLevelStore(extensionContext); + copyValue(TestContainerExtension.STORE_SCHEMA_NAME, parentStore, store); + copyValue(TestContainerExtension.STORE_DATA_SOURCE, parentStore, store); + } + return invocation.proceed(); + } + + @Override + public void afterAll(ExtensionContext context) { + Class testClass = context.getRequiredTestClass(); + if (isTopLevelClass(testClass) + || AnnotationSupport.isAnnotated(testClass, CleanTaskanaContext.class)) { + Optional.ofNullable(getClassLevelStore(context).get(STORE_CONTAINER)) + .map(JdbcDatabaseContainer.class::cast) + .ifPresent(GenericContainer::stop); + } + } + + private static void copyValue(String key, Store source, Store destination) { + Object value = source.get(key); + destination.put(key, value); + } + + private static String determineSchemaName(DB db) { + return db == DB.POSTGRES ? "taskana" : "TASKANA"; + } + + private static DB retrieveDatabaseFromEnv() { + String property = System.getenv("db.type"); + DB db; + try { + db = DB.valueOf(property); + } catch (Exception ex) { + db = DB.H2; + } + return db; + } + + 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/lib/taskana-core/src/test/java/testapi/tests/TaskanaDependencyInjectionExtensionTest.java b/lib/taskana-core/src/test/java/testapi/tests/TaskanaDependencyInjectionExtensionTest.java new file mode 100644 index 000000000..0d529c2d4 --- /dev/null +++ b/lib/taskana-core/src/test/java/testapi/tests/TaskanaDependencyInjectionExtensionTest.java @@ -0,0 +1,152 @@ +package testapi.tests; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.Test; +import testapi.TaskanaInject; +import testapi.TaskanaIntegrationTest; + +import pro.taskana.TaskanaEngineConfiguration; +import pro.taskana.classification.api.ClassificationService; +import pro.taskana.classification.internal.ClassificationServiceImpl; +import pro.taskana.common.api.JobService; +import pro.taskana.common.api.TaskanaEngine; +import pro.taskana.common.api.WorkingDaysToDaysConverter; +import pro.taskana.common.api.security.CurrentUserContext; +import pro.taskana.common.internal.JobServiceImpl; +import pro.taskana.common.internal.TaskanaEngineImpl; +import pro.taskana.common.internal.security.CurrentUserContextImpl; +import pro.taskana.monitor.api.MonitorService; +import pro.taskana.monitor.internal.MonitorServiceImpl; +import pro.taskana.task.api.TaskService; +import pro.taskana.task.internal.TaskServiceImpl; +import pro.taskana.workbasket.api.WorkbasketService; +import pro.taskana.workbasket.internal.WorkbasketServiceImpl; + +@TaskanaIntegrationTest +class TaskanaDependencyInjectionExtensionTest { + + TaskanaEngineConfiguration taskanaEngineConfigurationNotAnnotated; + @TaskanaInject TaskanaEngineConfiguration taskanaEngineConfiguration; + @TaskanaInject TaskanaEngine taskanaEngine; + @TaskanaInject TaskanaEngine taskanaEngine2; + @TaskanaInject TaskanaEngineImpl taskanaEngineImpl; + @TaskanaInject ClassificationService classificationService; + @TaskanaInject ClassificationServiceImpl classificationServiceImpl; + @TaskanaInject WorkbasketService workbasketService; + @TaskanaInject WorkbasketServiceImpl workbasketServiceImpl; + @TaskanaInject TaskService taskService; + @TaskanaInject TaskServiceImpl taskServiceImpl; + @TaskanaInject MonitorService monitorService; + @TaskanaInject MonitorServiceImpl monitorServiceImpl; + @TaskanaInject JobService jobService; + @TaskanaInject JobServiceImpl jobServiceImpl; + @TaskanaInject WorkingDaysToDaysConverter workingDaysToDaysConverter; + @TaskanaInject CurrentUserContext currentUserContext; + @TaskanaInject CurrentUserContextImpl currentUserContextImpl; + + @Test + void should_NotInjectTaskanaEngineConfiguration_When_FieldIsNotAnnotated() { + assertThat(taskanaEngineConfigurationNotAnnotated).isNull(); + } + + @Test + void should_InjectMultipleTimes_When_FieldIsDeclaredMultipleTimes() { + assertThat(taskanaEngine).isSameAs(taskanaEngine2).isNotNull(); + } + + @Test + void should_InjectTaskanaEngineConfiguration_When_FieldIsAnnotatedOrDeclaredAsParameter( + TaskanaEngineConfiguration taskanaEngineConfiguration) { + assertThat(taskanaEngineConfiguration).isSameAs(this.taskanaEngineConfiguration).isNotNull(); + } + + @Test + void should_InjectTaskanaEngine_When_FieldIsAnnotatedOrDeclaredAsParameter( + TaskanaEngine taskanaEngine) { + assertThat(taskanaEngine).isSameAs(this.taskanaEngine).isNotNull(); + } + + @Test + void should_InjectTaskanaEngineImpl_When_FieldIsAnnotatedOrDeclaredAsParameter( + TaskanaEngineImpl taskanaEngineImpl) { + assertThat(taskanaEngineImpl).isSameAs(this.taskanaEngineImpl).isNotNull(); + } + + @Test + void should_InjectClassificationService_When_FieldIsAnnotatedOrDeclaredAsParameter( + ClassificationService classificationService) { + assertThat(classificationService).isSameAs(this.classificationService).isNotNull(); + } + + @Test + void should_InjectClassificationServiceImpl_When_FieldIsAnnotatedOrDeclaredAsParameter( + ClassificationServiceImpl classificationServiceImpl) { + assertThat(classificationServiceImpl).isSameAs(this.classificationServiceImpl).isNotNull(); + } + + @Test + void should_InjectWorkbasketService_When_FieldIsAnnotatedOrDeclaredAsParameter( + WorkbasketService workbasketService) { + assertThat(workbasketService).isSameAs(this.workbasketService).isNotNull(); + } + + @Test + void should_InjectWorkbasketServiceImpl_When_FieldIsAnnotatedOrDeclaredAsParameter( + WorkbasketServiceImpl workbasketServiceImpl) { + assertThat(workbasketServiceImpl).isSameAs(this.workbasketServiceImpl).isNotNull(); + } + + @Test + void should_InjectTaskService_When_FieldIsAnnotatedOrDeclaredAsParameter( + TaskService taskService) { + assertThat(taskService).isSameAs(this.taskService).isNotNull(); + } + + @Test + void should_InjectTaskServiceImpl_When_FieldIsAnnotatedOrDeclaredAsParameter( + TaskServiceImpl taskServiceImpl) { + assertThat(taskServiceImpl).isSameAs(this.taskServiceImpl).isNotNull(); + } + + @Test + void should_InjectMonitorService_When_FieldIsAnnotatedOrDeclaredAsParameter( + MonitorService monitorService) { + assertThat(monitorService).isSameAs(this.monitorService).isNotNull(); + } + + @Test + void should_InjectMonitorServiceImpl_When_FieldIsAnnotatedOrDeclaredAsParameter( + MonitorServiceImpl monitorServiceImpl) { + assertThat(monitorServiceImpl).isSameAs(this.monitorServiceImpl).isNotNull(); + } + + @Test + void should_InjectJobService_When_FieldIsAnnotatedOrDeclaredAsParameter(JobService jobService) { + assertThat(jobService).isSameAs(this.jobService).isNotNull(); + } + + @Test + void should_InjectJobServiceImpl_When_FieldIsAnnotatedOrDeclaredAsParameter( + JobServiceImpl jobServiceImpl) { + assertThat(jobServiceImpl).isSameAs(this.jobServiceImpl).isNotNull(); + } + + @Test + void should_InjectWorkingDaysToDaysConverter_When_FieldIsAnnotatedOrDeclaredAsParameter( + WorkingDaysToDaysConverter workingDaysToDaysConverter) { + assertThat(workingDaysToDaysConverter).isSameAs(this.workingDaysToDaysConverter).isNotNull(); + } + + @Test + void should_InjectCurrentUserContext_When_FieldIsAnnotatedOrDeclaredAsParameter( + CurrentUserContext currentUserContext) { + assertThat(currentUserContext).isSameAs(this.currentUserContext).isNotNull(); + } + + @Test + void should_InjectCurrentUserContextImpl_When_FieldIsAnnotatedOrDeclaredAsParameter( + CurrentUserContextImpl currentUserContextImpl) { + assertThat(currentUserContextImpl).isSameAs(this.currentUserContextImpl).isNotNull(); + } +} diff --git a/lib/taskana-core/src/test/java/testapi/tests/TaskanaInitializationExtensionTest.java b/lib/taskana-core/src/test/java/testapi/tests/TaskanaInitializationExtensionTest.java new file mode 100644 index 000000000..773f2f173 --- /dev/null +++ b/lib/taskana-core/src/test/java/testapi/tests/TaskanaInitializationExtensionTest.java @@ -0,0 +1,78 @@ +package testapi.tests; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.List; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import testapi.CleanTaskanaContext; +import testapi.TaskanaEngineConfigurationModifier; +import testapi.TaskanaInject; +import testapi.TaskanaIntegrationTest; + +import pro.taskana.TaskanaEngineConfiguration; + +@TaskanaIntegrationTest +class TaskanaInitializationExtensionTest { + + @TaskanaInject TaskanaEngineConfiguration taskanaEngineConfiguration; + + @Test + void should_UseDefaultTaskanaEngine_When_TestIsCreated() { + assertThat(taskanaEngineConfiguration.getDomains()) + .containsExactlyInAnyOrder("DOMAIN_A", "DOMAIN_B"); + } + + @Nested + class ReuseTaskana { + + @TaskanaInject TaskanaEngineConfiguration taskanaEngineConfiguration; + + @Test + void should_useTopLevelTaskanaInstance_For_NestedTestClasses() { + assertThat(taskanaEngineConfiguration) + .isSameAs(TaskanaInitializationExtensionTest.this.taskanaEngineConfiguration); + } + } + + @Nested + class ModifiedTaskanaEngineConfig implements TaskanaEngineConfigurationModifier { + + @TaskanaInject TaskanaEngineConfiguration taskanaEngineConfiguration; + + @Override + public void modify(TaskanaEngineConfiguration taskanaEngineConfiguration) { + taskanaEngineConfiguration.setDomains(List.of("A", "B")); + } + + @Test + void should_OverrideConfiguration_When_ClassImplementsTaskanaEngineConfigurationModifier() { + assertThat(taskanaEngineConfiguration.getDomains()).containsExactlyInAnyOrder("A", "B"); + } + + @Test + void should_createNewTaskanaInstance_For_NestedTestClassImplementingModifierInterface() { + assertThat(taskanaEngineConfiguration) + .isNotSameAs(TaskanaInitializationExtensionTest.this.taskanaEngineConfiguration); + } + } + + @CleanTaskanaContext + @Nested + class NestedTestClassAnnotatedWithCleanTaskanaContext { + + @TaskanaInject TaskanaEngineConfiguration taskanaEngineConfiguration; + + @Test + void should_createNewTaskanaInstance_For_NestedTestClassAnnotatedWithCleanTaskanaContext() { + assertThat(taskanaEngineConfiguration) + .isNotSameAs(TaskanaInitializationExtensionTest.this.taskanaEngineConfiguration); + } + + @Test + void should_UseDefaultTaskanaEngine_When_NestedClassDoesNotImplementModifier() { + assertThat(taskanaEngineConfiguration.getDomains()) + .containsExactlyInAnyOrder("DOMAIN_A", "DOMAIN_B"); + } + } +} diff --git a/lib/taskana-core/src/test/java/testapi/tests/TestContainerExtensionTest.java b/lib/taskana-core/src/test/java/testapi/tests/TestContainerExtensionTest.java new file mode 100644 index 000000000..d0536506b --- /dev/null +++ b/lib/taskana-core/src/test/java/testapi/tests/TestContainerExtensionTest.java @@ -0,0 +1,95 @@ +package testapi.tests; + +import static org.assertj.core.api.Assertions.assertThat; + +import javax.sql.DataSource; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import testapi.CleanTaskanaContext; +import testapi.TaskanaEngineConfigurationModifier; +import testapi.TaskanaInject; +import testapi.TaskanaIntegrationTest; + +import pro.taskana.TaskanaEngineConfiguration; + +@TaskanaIntegrationTest +class TestContainerExtensionTest { + + @TaskanaInject TaskanaEngineConfiguration taskanaEngineConfiguration; + + @Test + void should_CreateNewDataSource_For_TopLevelTestClass() { + DataSource datasource = taskanaEngineConfiguration.getDatasource(); + + assertThat(datasource).isNotNull(); + } + + @Nested + class NestedTestClass { + @TaskanaInject TaskanaEngineConfiguration taskanaEngineConfiguration; + + @Test + void should_ReuseDataSource_For_NestedTestClass() { + DataSource nestedDataSource = taskanaEngineConfiguration.getDatasource(); + DataSource topLevelDataSource = + TestContainerExtensionTest.this.taskanaEngineConfiguration.getDatasource(); + + assertThat(nestedDataSource).isSameAs(topLevelDataSource).isNotNull(); + } + } + + @Nested + class NestedTestClassWithConfigurationModifier implements TaskanaEngineConfigurationModifier { + @TaskanaInject TaskanaEngineConfiguration taskanaEngineConfiguration; + + @Override + public void modify(TaskanaEngineConfiguration taskanaEngineConfiguration) { + // do nothing + } + + @Test + void should_ReuseDataSource_For_NestedTestClassWhichImplementsConfigurationModifier() { + DataSource nestedDataSource = taskanaEngineConfiguration.getDatasource(); + DataSource topLevelDataSource = + TestContainerExtensionTest.this.taskanaEngineConfiguration.getDatasource(); + + assertThat(nestedDataSource).isSameAs(topLevelDataSource).isNotNull(); + } + } + + @CleanTaskanaContext + @Nested + class NestedTestClassAnnotatedWithCleanTaskanaContext { + @TaskanaInject TaskanaEngineConfiguration taskanaEngineConfiguration; + + @Test + void should_CreateNewDataSource_For_NestedTestAnnotatedWithCleanTaskanaContext() { + DataSource nestedDataSource = taskanaEngineConfiguration.getDatasource(); + DataSource topLevelDataSource = + TestContainerExtensionTest.this.taskanaEngineConfiguration.getDatasource(); + + assertThat(nestedDataSource).isNotSameAs(topLevelDataSource).isNotNull(); + } + } + + @CleanTaskanaContext + @Nested + class NestedTestClassAnnotatedWithCleanTaskanaContextAndConfigModifier + implements TaskanaEngineConfigurationModifier { + @TaskanaInject TaskanaEngineConfiguration taskanaEngineConfiguration; + + @Override + public void modify(TaskanaEngineConfiguration taskanaEngineConfiguration) { + // do nothing + } + + @Test + void should_CreateNewDataSource_For_NestedTestAnnotatedWithCleanTaskanaContext() { + DataSource nestedDataSource = taskanaEngineConfiguration.getDatasource(); + DataSource topLevelDataSource = + TestContainerExtensionTest.this.taskanaEngineConfiguration.getDatasource(); + + assertThat(nestedDataSource).isNotSameAs(topLevelDataSource).isNotNull(); + } + } +} diff --git a/common/taskana-common-test/src/main/java/pro/taskana/common/test/config/TestContainerExtension.java b/lib/taskana-core/src/test/java/testapi/util/DockerContainerCreator.java similarity index 53% rename from common/taskana-common-test/src/main/java/pro/taskana/common/test/config/TestContainerExtension.java rename to lib/taskana-core/src/test/java/testapi/util/DockerContainerCreator.java index f521a9b8e..6f03cac78 100644 --- a/common/taskana-common-test/src/main/java/pro/taskana/common/test/config/TestContainerExtension.java +++ b/lib/taskana-core/src/test/java/testapi/util/DockerContainerCreator.java @@ -1,14 +1,11 @@ -package pro.taskana.common.test.config; +package testapi.util; 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; @@ -17,70 +14,13 @@ import org.testcontainers.utility.DockerImageName; import pro.taskana.common.internal.configuration.DB; -public class TestContainerExtension implements AfterAllCallback { +public class DockerContainerCreator { - 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); + private DockerContainerCreator() { + throw new IllegalStateException("Utility class"); } - @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) { + public static Optional> createDockerContainer(DB db) { switch (db) { case DB2: return Optional.of( @@ -116,21 +56,16 @@ public class TestContainerExtension implements AfterAllCallback { } } - private static DataSource createDataSourceForH2() { + public static DataSource createDataSource(JdbcDatabaseContainer container) { 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"); + container.getDriverClassName(), + container.getJdbcUrl(), + container.getUsername(), + container.getPassword()); ds.setPoolTimeToWait(50); ds.forceCloseAll(); // otherwise, the MyBatis pool is not initialized correctly - return ds; } } diff --git a/lib/taskana-core/src/test/java/testapi/util/ExtensionCommunicator.java b/lib/taskana-core/src/test/java/testapi/util/ExtensionCommunicator.java new file mode 100644 index 000000000..3f4e50d79 --- /dev/null +++ b/lib/taskana-core/src/test/java/testapi/util/ExtensionCommunicator.java @@ -0,0 +1,40 @@ +package testapi.util; + +import static org.junit.platform.commons.support.AnnotationSupport.isAnnotated; + +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.api.extension.ExtensionContext.Namespace; +import org.junit.jupiter.api.extension.ExtensionContext.Store; +import testapi.CleanTaskanaContext; +import testapi.TaskanaEngineConfigurationModifier; + +public class ExtensionCommunicator { + + private ExtensionCommunicator() { + throw new IllegalStateException("utility class"); + } + + public static boolean isTopLevelClass(Class testClass) { + return testClass.getEnclosingClass() == null; + } + + public static Store getClassLevelStore(ExtensionContext context) { + return getClassLevelStore(context, context.getRequiredTestClass()); + } + + public static Store getClassLevelStore(ExtensionContext context, Class testClass) { + return context.getStore(determineNamespace(testClass)); + } + + private static Namespace determineNamespace(Class testClass) { + if (isTopLevelClass(testClass)) { + return Namespace.create(testClass); + } else if (isAnnotated(testClass, CleanTaskanaContext.class)) { + return Namespace.create(testClass.getEnclosingClass(), testClass); + } else if (TaskanaEngineConfigurationModifier.class.isAssignableFrom(testClass)) { + return Namespace.create( + testClass.getEnclosingClass(), testClass, TaskanaEngineConfigurationModifier.class); + } + return Namespace.create(testClass.getEnclosingClass()); + } +}