TSK-1856: duplicating the test-api into a dedicated module

This commit is contained in:
ryzheboka 2022-04-26 14:28:10 +02:00 committed by Elena Mokeeva
parent 7821c12338
commit 54f01f8927
29 changed files with 2432 additions and 1 deletions

View File

@ -247,6 +247,7 @@ jobs:
- taskana-core
- taskana-cdi
- taskana-cdi-example
- taskana-test-api
- taskana-spring
- taskana-spring-example
- taskana-spi-routing-dmn-router
@ -263,6 +264,10 @@ jobs:
database: POSTGRES
- module: taskana-core
database: DB2
- module: taskana-test-api
database: POSTGRES
- module: taskana-test-api
database: DB2
- module: taskana-rest-spring-example-boot
database: DB2
- module: taskana-rest-spring-example-wildfly
@ -352,7 +357,7 @@ jobs:
-pl :taskana-parent,\
:taskana-common-parent,:taskana-common-logging,:taskana-common,:taskana-common-security,\
:taskana-common-data,:taskana-common-test,\
:taskana-lib-parent,:taskana-core,:taskana-cdi,:taskana-spring,\
:taskana-lib-parent,:taskana-core,:taskana-cdi,:taskana-spring, :taskana-test-api\
:taskana-rest-parent,:taskana-web,:taskana-rest-spring,\
:taskana-history-parent,:taskana-simplehistory-provider,:taskana-simplehistory-rest-spring,:taskana-loghistory-provider,\
:taskana-routing-parent,:taskana-spi-routing-dmn-router,:taskana-routing-rest

View File

@ -59,6 +59,11 @@
<artifactId>taskana-spring</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>pro.taskana</groupId>
<artifactId>taskana-test-api</artifactId>
<version>${project.version}</version>
</dependency>
<!-- all rest dependencies -->
<dependency>

View File

@ -22,5 +22,6 @@
<module>taskana-spring</module>
<module>taskana-cdi-example</module>
<module>taskana-spring-example</module>
<module>taskana-test-api</module>
</modules>
</project>

View File

@ -0,0 +1,99 @@
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>taskana-test-api</artifactId>
<name>${project.groupId}:${project.artifactId}</name>
<description>The taskana test-api to include in your project.</description>
<parent>
<groupId>pro.taskana</groupId>
<artifactId>taskana-lib-parent</artifactId>
<version>5.1.1-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<dependencies>
<dependency>
<groupId>pro.taskana</groupId>
<artifactId>taskana-common-logging</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>pro.taskana</groupId>
<artifactId>taskana-common</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>pro.taskana</groupId>
<artifactId>taskana-core</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-inline</artifactId>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>db2</artifactId>
<version>${version.testcontainers}</version>
<exclusions>
<exclusion>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>postgresql</artifactId>
<version>${version.testcontainers}</version>
<exclusions>
<exclusion>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- test dependencies -->
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.ibm.db2.jcc</groupId>
<artifactId>db2jcc4</artifactId>
<version>${version.db2}</version>
<scope>test</scope>
</dependency>
</dependencies>
<repositories>
<!-- this repository is needed to fetch com.ibm.db2.jcc -->
<repository>
<id>novatec public</id>
<name>novatec-repository</name>
<url>https://repository.novatec-gmbh.de/content/repositories/novatec/</url>
</repository>
</repositories>
</project>

View File

@ -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 {}

View File

@ -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 {}

View File

@ -0,0 +1,10 @@
package pro.taskana.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 {}

View File

@ -0,0 +1,71 @@
package pro.taskana.testapi;
import static java.time.temporal.ChronoUnit.SECONDS;
import java.time.Duration;
import java.util.Optional;
import javax.sql.DataSource;
import org.apache.ibatis.datasource.pooled.PooledDataSource;
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 DockerContainerCreator {
private DockerContainerCreator() {
throw new IllegalStateException("Utility class");
}
public static Optional<JdbcDatabaseContainer<?>> createDockerContainer(DB db) {
switch (db) {
case DB2:
return Optional.of(
new Db2Container(
DockerImageName.parse("taskana/db2:11.5")
.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();
}
}
public 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;
}
}

View File

@ -0,0 +1,8 @@
package pro.taskana.testapi;
import pro.taskana.TaskanaEngineConfiguration;
public interface TaskanaEngineConfigurationModifier {
void modify(TaskanaEngineConfiguration taskanaEngineConfiguration);
}

View File

@ -0,0 +1,36 @@
package pro.taskana.testapi;
import java.lang.reflect.Field;
import org.apache.ibatis.session.SqlSession;
import pro.taskana.common.api.TaskanaEngine;
import pro.taskana.common.internal.InternalTaskanaEngine;
import pro.taskana.common.internal.TaskanaEngineImpl;
/** Utility class to enable unit tests to access mappers directly. */
public class TaskanaEngineProxy {
private final InternalTaskanaEngine engine;
public TaskanaEngineProxy(TaskanaEngine taskanaEngine) throws Exception {
Field internal = TaskanaEngineImpl.class.getDeclaredField("internalTaskanaEngineImpl");
internal.setAccessible(true);
engine = (InternalTaskanaEngine) internal.get(taskanaEngine);
}
public InternalTaskanaEngine getEngine() {
return engine;
}
public SqlSession getSqlSession() {
return engine.getSqlSession();
}
public void openConnection() {
engine.openConnection();
}
public void returnConnection() {
engine.returnConnection();
}
}

View File

@ -0,0 +1,10 @@
package pro.taskana.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 {}

View File

@ -0,0 +1,26 @@
package pro.taskana.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 pro.taskana.testapi.extensions.TaskanaDependencyInjectionExtension;
import pro.taskana.testapi.extensions.TaskanaInitializationExtension;
import pro.taskana.testapi.extensions.TestContainerExtension;
import pro.taskana.testapi.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 {}

View File

@ -0,0 +1,26 @@
package pro.taskana.testapi;
import java.lang.annotation.ElementType;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import pro.taskana.testapi.WithServiceProvider.WithServiceProviders;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Repeatable(WithServiceProviders.class)
public @interface WithServiceProvider {
Class<?> serviceProviderInterface();
Class<?>[] serviceProviders();
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@interface WithServiceProviders {
WithServiceProvider[] value();
}
}

View File

@ -0,0 +1,64 @@
package pro.taskana.testapi.extensions;
import static org.junit.platform.commons.support.AnnotationSupport.findAnnotatedFields;
import static pro.taskana.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 pro.taskana.testapi.TaskanaInject;
public class TaskanaDependencyInjectionExtension
implements ParameterResolver, TestInstancePostProcessor {
@Override
public boolean supportsParameter(
ParameterContext parameterContext, ExtensionContext extensionContext)
throws ParameterResolutionException {
Map<Class<?>, 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<Class<?>, 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<Class<?>, Object> getTaskanaEntityMap(ExtensionContext extensionContext) {
return (Map<Class<?>, Object>)
getClassLevelStore(extensionContext)
.get(TaskanaInitializationExtension.STORE_TASKANA_ENTITY_MAP);
}
}

View File

@ -0,0 +1,126 @@
package pro.taskana.testapi.extensions;
import static org.junit.platform.commons.support.AnnotationSupport.isAnnotated;
import static pro.taskana.testapi.util.ExtensionCommunicator.getClassLevelStore;
import static pro.taskana.testapi.util.ExtensionCommunicator.isTopLevelClass;
import java.util.Map;
import javax.sql.DataSource;
import org.apache.ibatis.session.SqlSession;
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 org.mockito.MockedStatic;
import org.mockito.Mockito;
import pro.taskana.TaskanaEngineConfiguration;
import pro.taskana.classification.api.ClassificationService;
import pro.taskana.classification.internal.ClassificationServiceImpl;
import pro.taskana.common.api.ConfigurationService;
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.ConfigurationMapper;
import pro.taskana.common.internal.ConfigurationServiceImpl;
import pro.taskana.common.internal.InternalTaskanaEngine;
import pro.taskana.common.internal.JobServiceImpl;
import pro.taskana.common.internal.TaskanaEngineImpl;
import pro.taskana.common.internal.security.CurrentUserContextImpl;
import pro.taskana.common.internal.util.SpiLoader;
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.testapi.CleanTaskanaContext;
import pro.taskana.testapi.TaskanaEngineConfigurationModifier;
import pro.taskana.testapi.TaskanaEngineProxy;
import pro.taskana.testapi.WithServiceProvider;
import pro.taskana.testapi.util.ServiceProviderExtractor;
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)
|| isAnnotated(testClass, WithServiceProvider.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;
try (MockedStatic<SpiLoader> staticMock = Mockito.mockStatic(SpiLoader.class)) {
ServiceProviderExtractor.extractServiceProviders(testClass)
.forEach(
(spi, serviceProviders) ->
staticMock.when(() -> SpiLoader.load(spi)).thenReturn(serviceProviders));
taskanaEngine =
taskanaEngineConfiguration.buildTaskanaEngine(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<Class<?>, Object> generateTaskanaEntityMap(TaskanaEngine taskanaEngine)
throws Exception {
TaskService taskService = taskanaEngine.getTaskService();
TaskanaEngineProxy taskanaEngineProxy = new TaskanaEngineProxy(taskanaEngine);
MonitorService monitorService = taskanaEngine.getMonitorService();
WorkbasketService workbasketService = taskanaEngine.getWorkbasketService();
ClassificationService classificationService = taskanaEngine.getClassificationService();
JobService jobService = taskanaEngine.getJobService();
CurrentUserContext currentUserContext = taskanaEngine.getCurrentUserContext();
SqlSession sqlSession = taskanaEngineProxy.getSqlSession();
return Map.ofEntries(
Map.entry(TaskanaEngineConfiguration.class, taskanaEngine.getConfiguration()),
Map.entry(TaskanaEngineImpl.class, taskanaEngine),
Map.entry(TaskanaEngine.class, taskanaEngine),
Map.entry(InternalTaskanaEngine.class, taskanaEngineProxy.getEngine()),
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(ConfigurationService.class, taskanaEngine.getConfigurationService()),
Map.entry(ConfigurationServiceImpl.class, taskanaEngine.getConfigurationService()),
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()),
Map.entry(ConfigurationMapper.class, sqlSession.getMapper(ConfigurationMapper.class)));
}
}

View File

@ -0,0 +1,117 @@
package pro.taskana.testapi.extensions;
import static org.junit.platform.commons.support.AnnotationSupport.isAnnotated;
import static pro.taskana.testapi.DockerContainerCreator.createDataSource;
import static pro.taskana.testapi.DockerContainerCreator.createDockerContainer;
import static pro.taskana.testapi.util.ExtensionCommunicator.getClassLevelStore;
import static pro.taskana.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 pro.taskana.common.internal.configuration.DB;
import pro.taskana.testapi.CleanTaskanaContext;
import pro.taskana.testapi.TaskanaEngineConfigurationModifier;
import pro.taskana.testapi.WithServiceProvider;
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> T interceptTestClassConstructor(
Invocation<T> invocation,
ReflectiveInvocationContext<Constructor<T>> 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, createDataSource(container));
store.put(STORE_CONTAINER, container);
},
() -> store.put(STORE_DATA_SOURCE, createDataSourceForH2()));
} else if (TaskanaEngineConfigurationModifier.class.isAssignableFrom(testClass)
|| isAnnotated(testClass, WithServiceProvider.class)) {
// 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");
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;
}
}

View File

@ -0,0 +1,328 @@
package pro.taskana.testapi.security;
import static org.junit.platform.commons.support.AnnotationSupport.isAnnotated;
import static pro.taskana.common.internal.util.CheckedFunction.wrapExceptFor;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.security.Principal;
import java.security.PrivilegedAction;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
import javax.security.auth.Subject;
import org.junit.jupiter.api.DynamicContainer;
import org.junit.jupiter.api.DynamicNode;
import org.junit.jupiter.api.extension.DynamicTestInvocationContext;
import org.junit.jupiter.api.extension.Extension;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.api.extension.ExtensionContext.Namespace;
import org.junit.jupiter.api.extension.ExtensionContext.Store;
import org.junit.jupiter.api.extension.InvocationInterceptor;
import org.junit.jupiter.api.extension.ParameterContext;
import org.junit.jupiter.api.extension.ParameterResolver;
import org.junit.jupiter.api.extension.ReflectiveInvocationContext;
import org.junit.jupiter.api.extension.TestTemplateInvocationContext;
import org.junit.jupiter.api.extension.TestTemplateInvocationContextProvider;
import org.junit.platform.commons.JUnitException;
import org.junit.platform.commons.support.AnnotationSupport;
import org.opentest4j.TestAbortedException;
import pro.taskana.common.api.exceptions.SystemException;
import pro.taskana.common.api.security.GroupPrincipal;
import pro.taskana.common.api.security.UserPrincipal;
import pro.taskana.testapi.security.WithAccessId.WithAccessIds;
/** Runner for integration tests that enables JAAS subject. */
public class JaasExtension implements InvocationInterceptor, TestTemplateInvocationContextProvider {
private static final String ACCESS_IDS_STORE_KEY = "accessIds";
// region InvocationInterceptor
@Override
public <T> T interceptTestClassConstructor(
Invocation<T> invocation,
ReflectiveInvocationContext<Constructor<T>> invocationContext,
ExtensionContext extensionContext) {
return extractAccessIdAndPerformInvocation(invocation, invocationContext.getExecutable());
}
@Override
public void interceptBeforeAllMethod(
Invocation<Void> invocation,
ReflectiveInvocationContext<Method> invocationContext,
ExtensionContext extensionContext) {
extractAccessIdAndPerformInvocation(invocation, invocationContext.getExecutable());
}
@Override
public void interceptBeforeEachMethod(
Invocation<Void> invocation,
ReflectiveInvocationContext<Method> invocationContext,
ExtensionContext extensionContext) {
extractAccessIdAndPerformInvocation(invocation, invocationContext.getExecutable());
}
@Override
public void interceptTestMethod(
Invocation<Void> invocation,
ReflectiveInvocationContext<Method> invocationContext,
ExtensionContext extensionContext) {
if (isAnnotated(invocationContext.getExecutable(), WithAccessIds.class)) {
throw new JUnitException("Please use @TestTemplate instead of @Test for multiple accessIds");
}
extractAccessIdAndPerformInvocation(invocation, invocationContext.getExecutable());
}
@Override
@SuppressWarnings("unchecked")
public <T> T interceptTestFactoryMethod(
Invocation<T> invocation,
ReflectiveInvocationContext<Method> invocationContext,
ExtensionContext extensionContext) {
WithAccessIds annotation = invocationContext.getExecutable().getAnnotation(WithAccessIds.class);
if (annotation != null) {
// our goal is to run each test returned from the test factory X times. X is the amount of
// WithAccessId annotations. In order to achieve this we are wrapping the result from the
// factory (the returning tests) in a dynamicContainer for each accessId. Since we don't know
// what the factory will return we have to check for every possible return type. All possible
// return types can be found here:
// https://junit.org/junit5/docs/current/user-guide/#writing-tests-dynamic-tests
// After checking each return type we abuse the return type of T and hardly change it to
// Stream<DynamicContainer> no matter what the factory returns. This return type is allowed
// per definition (See link above), but is not the type T. Hence we have an unchecked cast at
// the end to keep the compiler happy...
// we are using the first annotation to run the factory method with.
T factoryResult = performInvocationWithAccessId(invocation, annotation.value()[0]);
Iterable<DynamicNode> newChildrenForDynamicContainer;
// TestFactory must have one of the following return types. See link above for further details
if (factoryResult instanceof DynamicNode) {
newChildrenForDynamicContainer = Collections.singleton((DynamicNode) factoryResult);
} else if (factoryResult instanceof Stream) {
Stream<DynamicNode> nodes = (Stream<DynamicNode>) factoryResult;
newChildrenForDynamicContainer = nodes.collect(Collectors.toList());
} else if (factoryResult instanceof Iterable) {
newChildrenForDynamicContainer = (Iterable<DynamicNode>) factoryResult;
} else if (factoryResult instanceof Iterator) {
newChildrenForDynamicContainer = () -> (Iterator<DynamicNode>) factoryResult;
} else if (factoryResult instanceof DynamicNode[]) {
newChildrenForDynamicContainer = Arrays.asList((DynamicNode[]) factoryResult);
} else {
throw new SystemException(
String.format(
"Testfactory '%s' did not return a proper type",
invocationContext.getExecutable().getName()));
}
// Currently, a DynamicContainer has children from this type: Stream<DynamicNode>
// Because of this the children can only be extracted once (Streams can only be operated
// once). This is obviously not ok since we want to execute each node X times. So we have to
// manually insert all children recursively to extract them X times...
Map<String, List<DynamicNode>> childrenMap = new HashMap<>();
persistDynamicContainerChildren(newChildrenForDynamicContainer, childrenMap);
Function<WithAccessId, DynamicContainer> wrapTestsInDynamicContainer =
accessId ->
DynamicContainer.dynamicContainer(
getDisplayNameForAccessId(accessId),
StreamSupport.stream(newChildrenForDynamicContainer.spliterator(), false)
.map(x -> duplicateDynamicNode(x, childrenMap)));
Store store = getMethodLevelStore(extensionContext);
return (T)
Stream.of(annotation.value())
.peek(a -> store.put(ACCESS_IDS_STORE_KEY, a))
.map(wrapTestsInDynamicContainer);
}
return extractAccessIdAndPerformInvocation(invocation, invocationContext.getExecutable());
}
@Override
public void interceptTestTemplateMethod(
Invocation<Void> invocation,
ReflectiveInvocationContext<Method> invocationContext,
ExtensionContext extensionContext) {
WithAccessId accessId =
getMethodLevelStore(extensionContext).get(ACCESS_IDS_STORE_KEY, WithAccessId.class);
performInvocationWithAccessId(invocation, accessId);
}
@Override
public void interceptDynamicTest(
Invocation<Void> invocation,
DynamicTestInvocationContext invocationContext,
ExtensionContext extensionContext) {
ExtensionContext testContext = getParentMethodExtensionContent(extensionContext);
// Check if the test factory provided an access Id for this dynamic test.
WithAccessId o = getMethodLevelStore(testContext).get(ACCESS_IDS_STORE_KEY, WithAccessId.class);
if (o != null) {
performInvocationWithAccessId(invocation, o);
} else {
extractAccessIdAndPerformInvocation(invocation, testContext.getRequiredTestMethod());
}
}
@Override
public void interceptAfterEachMethod(
Invocation<Void> invocation,
ReflectiveInvocationContext<Method> invocationContext,
ExtensionContext extensionContext) {
extractAccessIdAndPerformInvocation(invocation, invocationContext.getExecutable());
}
@Override
public void interceptAfterAllMethod(
Invocation<Void> invocation,
ReflectiveInvocationContext<Method> invocationContext,
ExtensionContext extensionContext) {
extractAccessIdAndPerformInvocation(invocation, invocationContext.getExecutable());
}
// endregion
// region TestTemplateInvocationContextProvider
@Override
public boolean supportsTestTemplate(ExtensionContext context) {
return isAnnotated(context.getElement(), WithAccessIds.class)
|| isAnnotated(context.getElement(), WithAccessId.class);
}
@Override
public Stream<TestTemplateInvocationContext> provideTestTemplateInvocationContexts(
ExtensionContext context) {
List<WithAccessId> accessIds =
AnnotationSupport.findRepeatableAnnotations(context.getElement(), WithAccessId.class);
Store store = getMethodLevelStore(context);
return accessIds.stream()
.peek(a -> store.put(ACCESS_IDS_STORE_KEY, a))
.map(JaasExtensionInvocationContext::new);
}
// endregion
private static void persistDynamicContainerChildren(
Iterable<DynamicNode> nodes, Map<String, List<DynamicNode>> childrenMap) {
nodes.forEach(
node -> {
if (node instanceof DynamicContainer) {
DynamicContainer container = (DynamicContainer) node;
List<DynamicNode> children = container.getChildren().collect(Collectors.toList());
childrenMap.put(container.hashCode() + container.getDisplayName(), children);
persistDynamicContainerChildren(children, childrenMap);
}
});
}
private static DynamicNode duplicateDynamicNode(
DynamicNode node, Map<String, List<DynamicNode>> lookupMap) {
if (node instanceof DynamicContainer) {
DynamicContainer container = (DynamicContainer) node;
Stream<DynamicNode> children =
lookupMap.get(node.hashCode() + node.getDisplayName()).stream()
.map(x -> duplicateDynamicNode(x, lookupMap));
return DynamicContainer.dynamicContainer(container.getDisplayName(), children);
}
return node;
}
private static <T> T extractAccessIdAndPerformInvocation(
Invocation<T> invocation, AnnotatedElement executable) {
return performInvocationWithAccessId(invocation, executable.getAnnotation(WithAccessId.class));
}
private static <T> T performInvocationWithAccessId(
Invocation<T> invocation, WithAccessId withAccessId) {
Subject subject = new Subject();
subject.getPrincipals().addAll(getPrincipals(withAccessId));
Function<Invocation<T>, T> proceedInvocation =
wrapExceptFor(Invocation::proceed, TestAbortedException.class);
PrivilegedAction<T> performInvocation = () -> proceedInvocation.apply(invocation);
return Subject.doAs(subject, performInvocation);
}
private static List<Principal> getPrincipals(WithAccessId withAccessId) {
if (withAccessId != null) {
return Stream.concat(
Stream.of(withAccessId.user()).map(UserPrincipal::new),
Arrays.stream(withAccessId.groups()).map(GroupPrincipal::new))
.collect(Collectors.toList());
}
return Collections.emptyList();
}
private ExtensionContext getParentMethodExtensionContent(ExtensionContext extensionContext) {
Optional<ExtensionContext> parent = extensionContext.getParent();
// the class MethodExtensionContext is part of junit-jupiter-engine and has only a
// package-private visibility thus this workaround is needed.
while (!parent
.map(Object::getClass)
.map(Class::getName)
.filter(s -> s.endsWith("MethodExtensionContext"))
.isPresent()) {
parent = parent.flatMap(ExtensionContext::getParent);
}
return parent.orElseThrow(
() ->
new JUnitException(
String.format(
"Test '%s' does not have a parent method", extensionContext.getUniqueId())));
}
private static Store getMethodLevelStore(ExtensionContext context) {
return context.getStore(
Namespace.create(context.getRequiredTestClass(), context.getRequiredTestMethod()));
}
private static String getDisplayNameForAccessId(WithAccessId withAccessId) {
return String.format("for user '%s'", withAccessId.user());
}
private static class JaasExtensionInvocationContext implements TestTemplateInvocationContext {
private final WithAccessId withAccessId;
private JaasExtensionInvocationContext(WithAccessId withAccessId) {
this.withAccessId = withAccessId;
}
@Override
public String getDisplayName(int invocationIndex) {
return getDisplayNameForAccessId(withAccessId);
}
@Override
public List<Extension> getAdditionalExtensions() {
return Collections.singletonList(new WithAccessIdParameterResolver());
}
private class WithAccessIdParameterResolver implements ParameterResolver {
@Override
public boolean supportsParameter(
ParameterContext parameterContext, ExtensionContext extensionContext) {
return parameterContext.getParameter().getType().equals(WithAccessId.class);
}
@Override
public Object resolveParameter(
ParameterContext parameterContext, ExtensionContext extensionContext) {
return withAccessId;
}
}
}
}

View File

@ -0,0 +1,24 @@
package pro.taskana.testapi.security;
import java.lang.annotation.ElementType;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.CONSTRUCTOR})
@Repeatable(WithAccessId.WithAccessIds.class)
public @interface WithAccessId {
String user();
String[] groups() default {};
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@interface WithAccessIds {
WithAccessId[] value();
}
}

View File

@ -0,0 +1,52 @@
package pro.taskana.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 pro.taskana.testapi.CleanTaskanaContext;
import pro.taskana.testapi.TaskanaEngineConfigurationModifier;
import pro.taskana.testapi.WithServiceProvider;
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));
}
public static Class<?> getTopLevelClass(Class<?> testClazz) {
Class<?> parent = testClazz;
while (parent.getEnclosingClass() != null) {
parent = parent.getEnclosingClass();
}
return parent;
}
private static Namespace determineNamespace(Class<?> testClass) {
if (isTopLevelClass(testClass)) {
return Namespace.create(testClass);
} else if (isAnnotated(testClass, CleanTaskanaContext.class)) {
return Namespace.create(getTopLevelClass(testClass), testClass, CleanTaskanaContext.class);
} else if (isAnnotated(testClass, WithServiceProvider.class)) {
return Namespace.create(getTopLevelClass(testClass), testClass, WithServiceProvider.class);
} else if (TaskanaEngineConfigurationModifier.class.isAssignableFrom(testClass)) {
return Namespace.create(
getTopLevelClass(testClass), testClass, TaskanaEngineConfigurationModifier.class);
}
return Namespace.create(getTopLevelClass(testClass));
}
}

View File

@ -0,0 +1,73 @@
package pro.taskana.testapi.util;
import static org.junit.platform.commons.support.AnnotationSupport.findRepeatableAnnotations;
import static pro.taskana.common.internal.util.CheckedFunction.wrap;
import java.lang.reflect.Constructor;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.stream.Collectors;
import org.junit.platform.commons.JUnitException;
import pro.taskana.spi.history.api.TaskanaHistory;
import pro.taskana.spi.priority.api.PriorityServiceProvider;
import pro.taskana.spi.routing.api.TaskRoutingProvider;
import pro.taskana.spi.task.api.CreateTaskPreprocessor;
import pro.taskana.testapi.WithServiceProvider;
public class ServiceProviderExtractor {
private static final Set<Class<?>> TASKANA_SERVICE_PROVIDER_INTERFACES =
Set.of(
TaskanaHistory.class,
PriorityServiceProvider.class,
TaskRoutingProvider.class,
CreateTaskPreprocessor.class);
private ServiceProviderExtractor() {
throw new IllegalStateException("utility class");
}
public static Map<Class<?>, List<Object>> extractServiceProviders(Class<?> testClass) {
List<WithServiceProvider> withServiceProviders =
findRepeatableAnnotations(testClass, WithServiceProvider.class);
return groupServiceProvidersByServiceProviderInterface(withServiceProviders).entrySet().stream()
.peek(entry -> validateServiceProviders(entry.getKey(), entry.getValue()))
.collect(
Collectors.toMap(
Entry::getKey, entry -> instantiateServiceProviders(entry.getValue())));
}
private static void validateServiceProviders(Class<?> spi, List<Class<?>> serviceProviders) {
if (!TASKANA_SERVICE_PROVIDER_INTERFACES.contains(spi)) {
throw new JUnitException(String.format("SPI '%s' is not a TASKANA SPI.", spi));
}
if (!serviceProviders.stream().allMatch(spi::isAssignableFrom)) {
throw new JUnitException(
String.format(
"At least one ServiceProvider does not implement the requested SPI '%s'", spi));
}
}
private static Map<Class<?>, List<Class<?>>> groupServiceProvidersByServiceProviderInterface(
List<WithServiceProvider> withServiceProviders) {
return withServiceProviders.stream()
.collect(
Collectors.groupingBy(
WithServiceProvider::serviceProviderInterface,
Collectors.flatMapping(
annotation -> Arrays.stream(annotation.serviceProviders()),
Collectors.toList())));
}
private static List<Object> instantiateServiceProviders(List<Class<?>> serviceProviders) {
return serviceProviders.stream()
.map(wrap(Class::getDeclaredConstructor))
.map(wrap(Constructor::newInstance))
.collect(Collectors.toList());
}
}

View File

@ -0,0 +1,184 @@
package pro.taskana.testapi.tests;
import static org.assertj.core.api.Assertions.assertThat;
import org.junit.jupiter.api.Test;
import pro.taskana.TaskanaEngineConfiguration;
import pro.taskana.classification.api.ClassificationService;
import pro.taskana.classification.internal.ClassificationServiceImpl;
import pro.taskana.common.api.ConfigurationService;
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.ConfigurationMapper;
import pro.taskana.common.internal.ConfigurationServiceImpl;
import pro.taskana.common.internal.InternalTaskanaEngine;
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.testapi.TaskanaInject;
import pro.taskana.testapi.TaskanaIntegrationTest;
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 InternalTaskanaEngine internalTaskanaEngine;
@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 ConfigurationService configurationService;
@TaskanaInject ConfigurationServiceImpl configurationServiceImpl;
@TaskanaInject WorkingDaysToDaysConverter workingDaysToDaysConverter;
@TaskanaInject CurrentUserContext currentUserContext;
@TaskanaInject CurrentUserContextImpl currentUserContextImpl;
@TaskanaInject ConfigurationMapper configurationMapper;
@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_InjectInternalTaskanaEngine_When_FieldIsAnnotatedOrDeclaredAsParameter(
InternalTaskanaEngine internalTaskanaEngine) {
assertThat(internalTaskanaEngine).isSameAs(this.internalTaskanaEngine).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_InjectConfigurationService_When_FieldIsAnnotatedOrDeclaredAsParameter(
ConfigurationService configurationService) {
assertThat(configurationService).isSameAs(this.configurationService).isNotNull();
}
@Test
void should_InjectConfigurationServiceImpl_When_FieldIsAnnotatedOrDeclaredAsParameter(
ConfigurationServiceImpl configurationServiceImpl) {
assertThat(configurationServiceImpl).isSameAs(this.configurationServiceImpl).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();
}
@Test
void should_InjectConfigurationMapper_When_FieldIsAnnotatedOrDeclaredAsParameter(
ConfigurationMapper configurationMapper) {
assertThat(configurationMapper).isSameAs(this.configurationMapper).isNotNull();
}
}

View File

@ -0,0 +1,107 @@
package pro.taskana.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 org.junit.jupiter.api.TestInstance;
import org.junit.jupiter.api.TestInstance.Lifecycle;
import pro.taskana.TaskanaEngineConfiguration;
import pro.taskana.spi.priority.api.PriorityServiceProvider;
import pro.taskana.testapi.CleanTaskanaContext;
import pro.taskana.testapi.TaskanaEngineConfigurationModifier;
import pro.taskana.testapi.TaskanaInject;
import pro.taskana.testapi.TaskanaIntegrationTest;
import pro.taskana.testapi.WithServiceProvider;
@TaskanaIntegrationTest
class TaskanaInitializationExtensionTest {
@TaskanaInject TaskanaEngineConfiguration taskanaEngineConfiguration;
@Test
void should_UseDefaultTaskanaEngine_When_TestIsCreated() {
assertThat(taskanaEngineConfiguration.getDomains())
.containsExactlyInAnyOrder("DOMAIN_A", "DOMAIN_B");
}
@Nested
@TestInstance(Lifecycle.PER_CLASS)
class ReuseTaskana {
@TaskanaInject TaskanaEngineConfiguration taskanaEngineConfiguration;
@Test
void should_useTopLevelTaskanaInstance_For_NestedTestClasses() {
assertThat(taskanaEngineConfiguration)
.isSameAs(TaskanaInitializationExtensionTest.this.taskanaEngineConfiguration);
}
}
@Nested
@TestInstance(Lifecycle.PER_CLASS)
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
@TestInstance(Lifecycle.PER_CLASS)
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");
}
}
@WithServiceProvider(
serviceProviderInterface = PriorityServiceProvider.class,
serviceProviders = TestPriorityServiceProvider.class)
@Nested
@TestInstance(Lifecycle.PER_CLASS)
class NestedTestClassWithServiceProvider {
@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");
}
}
}

View File

@ -0,0 +1,172 @@
package pro.taskana.testapi.tests;
import static org.assertj.core.api.Assertions.assertThat;
import java.sql.Connection;
import javax.sql.DataSource;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInstance;
import org.junit.jupiter.api.TestInstance.Lifecycle;
import pro.taskana.TaskanaEngineConfiguration;
import pro.taskana.spi.priority.api.PriorityServiceProvider;
import pro.taskana.testapi.CleanTaskanaContext;
import pro.taskana.testapi.TaskanaEngineConfigurationModifier;
import pro.taskana.testapi.TaskanaInject;
import pro.taskana.testapi.TaskanaIntegrationTest;
import pro.taskana.testapi.WithServiceProvider;
@TaskanaIntegrationTest
class TestContainerExtensionTest {
@TaskanaInject TaskanaEngineConfiguration taskanaEngineConfiguration;
@Test
void should_CreateNewDataSource_For_TopLevelTestClass() {
DataSource datasource = taskanaEngineConfiguration.getDatasource();
assertThat(datasource).isNotNull();
}
@Nested
@TestInstance(Lifecycle.PER_CLASS)
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
@TestInstance(Lifecycle.PER_CLASS)
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
@TestInstance(Lifecycle.PER_CLASS)
class NestedTestClassAnnotatedWithCleanTaskanaContext {
@TaskanaInject TaskanaEngineConfiguration taskanaEngineConfiguration;
@Test
void should_CreateNewDataSource_For_NestedTestAnnotatedWithCleanTaskanaContext()
throws Exception {
DataSource nestedDataSource = taskanaEngineConfiguration.getDatasource();
DataSource topLevelDataSource =
TestContainerExtensionTest.this.taskanaEngineConfiguration.getDatasource();
String nestedDataSourceUrl;
String topLevelDataSourceUrl;
try (Connection connection = nestedDataSource.getConnection()) {
nestedDataSourceUrl = connection.getMetaData().getURL();
}
try (Connection connection = topLevelDataSource.getConnection()) {
topLevelDataSourceUrl = connection.getMetaData().getURL();
}
assertThat(nestedDataSourceUrl).isNotEqualTo(topLevelDataSourceUrl).isNotNull();
}
@CleanTaskanaContext
@Nested
@TestInstance(Lifecycle.PER_CLASS)
class NestedNestedTestClassAnnotatedWithCleanTaskanaContext {
@TaskanaInject TaskanaEngineConfiguration taskanaEngineConfiguration;
@Test
void should_CreateNewDataSource_For_NestedTestAnnotatedWithCleanTaskanaContext()
throws Exception {
DataSource nestedNestedDataSource = taskanaEngineConfiguration.getDatasource();
DataSource nestedDataSource =
NestedTestClassAnnotatedWithCleanTaskanaContext.this.taskanaEngineConfiguration
.getDatasource();
DataSource topLevelDataSource =
TestContainerExtensionTest.this.taskanaEngineConfiguration.getDatasource();
String nestedNestedDataSourceUrl;
String nestedDataSourceUrl;
String topLevelDataSourceUrl;
try (Connection connection = nestedNestedDataSource.getConnection()) {
nestedNestedDataSourceUrl = connection.getMetaData().getURL();
}
try (Connection connection = nestedDataSource.getConnection()) {
nestedDataSourceUrl = connection.getMetaData().getURL();
}
try (Connection connection = topLevelDataSource.getConnection()) {
topLevelDataSourceUrl = connection.getMetaData().getURL();
}
assertThat(nestedNestedDataSourceUrl)
.isNotEqualTo(nestedDataSourceUrl)
.isNotEqualTo(topLevelDataSourceUrl)
.isNotNull();
}
}
}
@CleanTaskanaContext
@Nested
@TestInstance(Lifecycle.PER_CLASS)
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();
}
}
@WithServiceProvider(
serviceProviderInterface = PriorityServiceProvider.class,
serviceProviders = TestPriorityServiceProvider.class)
@Nested
@TestInstance(Lifecycle.PER_CLASS)
class NestedTestClassWithServiceProvider {
@TaskanaInject TaskanaEngineConfiguration taskanaEngineConfiguration;
@Test
void should_ReuseDataSource_For_NestedTestClassWithServiceProvider() {
DataSource nestedDataSource = taskanaEngineConfiguration.getDatasource();
DataSource topLevelDataSource =
TestContainerExtensionTest.this.taskanaEngineConfiguration.getDatasource();
assertThat(nestedDataSource).isSameAs(topLevelDataSource).isNotNull();
}
}
}

View File

@ -0,0 +1,40 @@
package pro.taskana.testapi.tests;
import java.time.Duration;
import java.time.Instant;
import java.util.OptionalInt;
import pro.taskana.common.api.WorkingDaysToDaysConverter;
import pro.taskana.common.api.WorkingTimeCalculator;
import pro.taskana.spi.priority.api.PriorityServiceProvider;
import pro.taskana.task.api.TaskCustomField;
import pro.taskana.task.api.models.TaskSummary;
public class TestPriorityServiceProvider implements PriorityServiceProvider {
private static final int MULTIPLIER = 10;
private final WorkingDaysToDaysConverter converter = new WorkingDaysToDaysConverter(true, true);
private final WorkingTimeCalculator calculator = new WorkingTimeCalculator(converter);
@Override
public OptionalInt calculatePriority(TaskSummary taskSummary) {
long priority;
try {
priority =
calculator
.workingTimeBetweenTwoTimestamps(taskSummary.getCreated(), Instant.now())
.toMinutes()
+ 1;
} catch (Exception e) {
priority = Duration.between(taskSummary.getCreated(), Instant.now()).toMinutes();
}
if (Boolean.parseBoolean(taskSummary.getCustomField(TaskCustomField.CUSTOM_6))) {
priority *= MULTIPLIER;
}
return OptionalInt.of(Math.toIntExact(priority));
}
}

View File

@ -0,0 +1,738 @@
package pro.taskana.testapi.tests.security;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.DynamicContainer.dynamicContainer;
import static org.junit.jupiter.api.DynamicTest.dynamicTest;
import java.util.Iterator;
import java.util.List;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DynamicContainer;
import org.junit.jupiter.api.DynamicTest;
import org.junit.jupiter.api.Nested;
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.TestTemplate;
import org.junit.jupiter.api.extension.ExtendWith;
import pro.taskana.common.api.security.CurrentUserContext;
import pro.taskana.common.internal.security.CurrentUserContextImpl;
import pro.taskana.testapi.security.JaasExtension;
import pro.taskana.testapi.security.WithAccessId;
import pro.taskana.testapi.tests.security.JaasExtensionTestExtensions.ShouldThrowJunitException;
import pro.taskana.testapi.tests.security.JaasExtensionTestExtensions.ShouldThrowParameterResolutionException;
@ExtendWith(JaasExtension.class)
class JaasExtensionTest {
private static final String INSIDE_DYNAMIC_TEST_USER = "inside_dynamic_test";
private static final CurrentUserContext CURRENT_USER_CONTEXT = new CurrentUserContextImpl(true);
private static final DynamicTest NOT_NULL_DYNAMIC_TEST =
dynamicTest("dynamic test", () -> assertThat(CURRENT_USER_CONTEXT.getUserid()).isNotNull());
private static final DynamicTest NULL_DYNAMIC_TEST =
dynamicTest("dynamic test", () -> assertThat(CURRENT_USER_CONTEXT.getUserid()).isNull());
private static final DynamicTest DYNAMIC_TEST_USER_DYNAMIC_TEST =
dynamicTest(
"dynamic test",
() -> assertThat(CURRENT_USER_CONTEXT.getUserid()).isEqualTo(INSIDE_DYNAMIC_TEST_USER));
// region JaasExtension#interceptBeforeAllMethod
@BeforeAll
static void should_NotSetJaasSubject_When_AnnotationIsMissing_On_BeforeAll() {
assertThat(CURRENT_USER_CONTEXT.getUserid()).isNull();
}
@WithAccessId(user = "beforeall")
@BeforeAll
static void should_SetJaasSubject_When_AnnotationExists_On_BeforeAll() {
assertThat(CURRENT_USER_CONTEXT.getUserid()).isEqualTo("beforeall");
}
@WithAccessId(user = "beforeall")
@WithAccessId(user = "beforeall2")
@BeforeAll
static void should_NotSetJaasSubject_When_MultipleAnnotationsExist_On_BeforeAll() {
assertThat(CURRENT_USER_CONTEXT.getUserid()).isNull();
}
// endregion
// region JaasExtension#interceptBeforeEachMethod
@BeforeEach
void should_NotSetJaasSubject_When_AnnotationIsMissing_On_BeforeEach() {
assertThat(CURRENT_USER_CONTEXT.getUserid()).isNull();
}
@WithAccessId(user = "beforeeach")
@BeforeEach
void should_SetJaasSubject_When_AnnotationExists_On_BeforeEach() {
assertThat(CURRENT_USER_CONTEXT.getUserid()).isEqualTo("beforeeach");
}
@WithAccessId(user = "beforeeach")
@WithAccessId(user = "beforeeach2")
@BeforeEach
void should_NotSetJaasSubject_When_MultipleAnnotationsExist_On_BeforeEach() {
assertThat(CURRENT_USER_CONTEXT.getUserid()).isNull();
}
// endregion
// region JaasExtension#interceptAfterEachMethod
@AfterEach
void should_NotSetJaasSubject_When_AnnotationIsMissing_On_AfterEach() {
assertThat(CURRENT_USER_CONTEXT.getUserid()).isNull();
}
@WithAccessId(user = "aftereach")
@AfterEach
void should_SetJaasSubject_When_AnnotationExists_On_AfterEach() {
assertThat(CURRENT_USER_CONTEXT.getUserid()).isEqualTo("aftereach");
}
@WithAccessId(user = "aftereach")
@WithAccessId(user = "afterach2")
@AfterEach
void should_NotSetJaasSubject_When_MultipleAnnotationsExist_On_AfterEach() {
assertThat(CURRENT_USER_CONTEXT.getUserid()).isNull();
}
// endregion
// region JaasExtension#interceptAfterAllMethod
@AfterAll
static void should_NotSetJaasSubject_When_AnnotationIsMissing_On_AfterAll() {
assertThat(CURRENT_USER_CONTEXT.getUserid()).isNull();
}
@WithAccessId(user = "afterall")
@AfterAll
static void should_SetJaasSubject_When_AnnotationExists_On_AfterAll() {
assertThat(CURRENT_USER_CONTEXT.getUserid()).isEqualTo("afterall");
}
@WithAccessId(user = "afterall")
@WithAccessId(user = "afterall2")
@AfterAll
static void should_NotSetJaasSubject_When_MultipleAnnotationsExist_On_AfterAll() {
assertThat(CURRENT_USER_CONTEXT.getUserid()).isNull();
}
// endregion
// region JaasExtension#interceptTestMethod
@Test
void should_NotSetJaasSubject_When_AnnotationIsMissing_On_Test() {
assertThat(CURRENT_USER_CONTEXT.getUserid()).isNull();
}
@WithAccessId(user = "user")
@Test
void should_SetJaasSubject_When_AnnotationExists_On_Test() {
assertThat(CURRENT_USER_CONTEXT.getUserid()).isEqualTo("user");
assertThat(CURRENT_USER_CONTEXT.getGroupIds()).isEmpty();
}
@WithAccessId(
user = "user",
groups = {"group1", "group2"})
@Test
void should_SetJaasSubjectWithGroups_When_AnnotationExistsWithGroups_On_Test() {
assertThat(CURRENT_USER_CONTEXT.getUserid()).isEqualTo("user");
assertThat(CURRENT_USER_CONTEXT.getGroupIds()).containsExactlyInAnyOrder("group1", "group2");
}
@WithAccessId(user = "user")
@Test
@ExtendWith(ShouldThrowParameterResolutionException.class)
void should_NotInjectParameter_When_TestTemplateIsNotUsed(
@SuppressWarnings("unused") WithAccessId accessId) {
// THIS IS NOT RELEVANT
assertThat(true).isTrue();
}
@WithAccessId(user = "user")
@WithAccessId(user = "user2")
@Test
@ExtendWith(ShouldThrowJunitException.class)
void should_ThrowJunitException_When_MultipleAnnotationsExist_On_Test() {
// THIS IS NOT RELEVANT
assertThat(true).isTrue();
}
// endregion
// region JaasExtension#interceptTestFactory
@TestFactory
List<DynamicTest> should_NotSetJaasSubject_When_AnnotationIsMissing_On_TestFactory() {
assertThat(CURRENT_USER_CONTEXT.getUserid()).isNull();
return List.of();
}
@WithAccessId(user = "testfactory")
@TestFactory
List<DynamicTest> should_SetJaasSubject_When_AnnotationExists_On_TestFactory() {
assertThat(CURRENT_USER_CONTEXT.getUserid()).isEqualTo("testfactory");
return List.of();
}
@WithAccessId(user = "testfactory1")
@WithAccessId(user = "testfactory2")
@TestFactory
List<DynamicTest>
should_SetJaasSubjectFromFirstAnnotation_When_MultipleAnnotationsExists_On_TestFactory() {
assertThat(CURRENT_USER_CONTEXT.getUserid()).isEqualTo("testfactory1");
return List.of();
}
// endregion
// region JaasExtension#interceptTestTemplateMethod
@WithAccessId(user = "testtemplate")
@TestTemplate
void should_SetJaasSubject_When_AnnotationExists_On_TestTemplate() {
assertThat(CURRENT_USER_CONTEXT.getUserid()).isEqualTo("testtemplate");
}
@WithAccessId(user = "testtemplate1")
@WithAccessId(user = "testtemplate2")
@WithAccessId(user = "testtemplate3")
@TestTemplate
void should_SetMultipleJaasSubjects_When_MultipleAnnotationsExist_On_TestTemplate(
WithAccessId accessId) {
assertThat(CURRENT_USER_CONTEXT.getUserid()).isEqualTo(accessId.user());
}
@WithAccessId(user = "testtemplate1", groups = "abc")
@TestTemplate
void should_InjectCorrectAccessId_When_AnnotationExists_On_TestTemplate(WithAccessId accessId) {
assertThat(accessId.user()).isEqualTo("testtemplate1");
assertThat(accessId.groups()).containsExactly("abc");
}
// endregion
// region JaasExtension#interceptDynamicTest
// region RETURNING DynamicNode
// WITH DynamicTest
@TestFactory
DynamicTest should_NotSetAccessIdForDynamicTest_When_AnnotationIsMissing() {
return NULL_DYNAMIC_TEST;
}
@WithAccessId(user = INSIDE_DYNAMIC_TEST_USER)
@TestFactory
DynamicTest should_SetAccessIdForDynamicTest_When_AnnotationExists() {
return DYNAMIC_TEST_USER_DYNAMIC_TEST;
}
@WithAccessId(user = INSIDE_DYNAMIC_TEST_USER)
@WithAccessId(user = INSIDE_DYNAMIC_TEST_USER)
@TestFactory
DynamicTest should_SetMultipleAccessIdForDynamicTest_When_AnnotationsExist() {
return NOT_NULL_DYNAMIC_TEST;
}
// WITH DynamicContainer
@TestFactory
DynamicContainer should_NotSetAccessIdForDynamicContainer_When_AnnotationIsMissing() {
return dynamicContainer("dynamic container", Stream.of(NULL_DYNAMIC_TEST, NULL_DYNAMIC_TEST));
}
@WithAccessId(user = INSIDE_DYNAMIC_TEST_USER)
@TestFactory
DynamicContainer should_SetAccessIdForDynamicContainer_When_AnnotationExists() {
return dynamicContainer(
"dynamic container",
Stream.of(DYNAMIC_TEST_USER_DYNAMIC_TEST, DYNAMIC_TEST_USER_DYNAMIC_TEST));
}
@WithAccessId(user = INSIDE_DYNAMIC_TEST_USER)
@WithAccessId(user = INSIDE_DYNAMIC_TEST_USER)
@TestFactory
DynamicContainer should_SetMultipleAccessIdForDynamicContainer_When_AnnotationsExist() {
return dynamicContainer(
"dynamic container", Stream.of(NOT_NULL_DYNAMIC_TEST, NOT_NULL_DYNAMIC_TEST));
}
// WITH nested DynamicContainer
@TestFactory
DynamicContainer should_NotSetAccessIdForNestedDynamicContainer_When_AnnotationIsMissing() {
DynamicContainer container =
dynamicContainer("inside container", Stream.of(NULL_DYNAMIC_TEST, NULL_DYNAMIC_TEST));
return dynamicContainer("outside container", Stream.of(container, NULL_DYNAMIC_TEST));
}
@WithAccessId(user = INSIDE_DYNAMIC_TEST_USER)
@TestFactory
DynamicContainer should_SetAccessIdForNestedDynamicContainer_When_AnnotationExists() {
DynamicContainer container =
dynamicContainer(
"nested container",
Stream.of(DYNAMIC_TEST_USER_DYNAMIC_TEST, DYNAMIC_TEST_USER_DYNAMIC_TEST));
return dynamicContainer(
"outside container", Stream.of(container, DYNAMIC_TEST_USER_DYNAMIC_TEST));
}
@WithAccessId(user = INSIDE_DYNAMIC_TEST_USER)
@WithAccessId(user = INSIDE_DYNAMIC_TEST_USER)
@TestFactory
DynamicContainer should_SetMultipleAccessIdForNestedDynamicContainer_When_AnnotationsExist() {
DynamicContainer container =
dynamicContainer(
"inside container", Stream.of(NOT_NULL_DYNAMIC_TEST, NOT_NULL_DYNAMIC_TEST));
return dynamicContainer("outside container", Stream.of(container, NOT_NULL_DYNAMIC_TEST));
}
// endregion
// region RETURNING Stream<DynamicNode>
@TestFactory
Stream<DynamicTest> should_NotSetAccessIdForDynamicTestInStream_When_AnnotationIsMissing() {
return Stream.of(NULL_DYNAMIC_TEST, NULL_DYNAMIC_TEST);
}
@WithAccessId(user = INSIDE_DYNAMIC_TEST_USER)
@TestFactory
Stream<DynamicTest> should_SetAccessIdForDynamicTestInStream_When_AnnotationExists() {
return Stream.of(DYNAMIC_TEST_USER_DYNAMIC_TEST, DYNAMIC_TEST_USER_DYNAMIC_TEST);
}
@WithAccessId(user = INSIDE_DYNAMIC_TEST_USER)
@WithAccessId(user = INSIDE_DYNAMIC_TEST_USER)
@TestFactory
Stream<DynamicTest> should_SetMultipleAccessIdForDynamicTestInStream_When_AnnotationsExist() {
return Stream.of(NOT_NULL_DYNAMIC_TEST, NOT_NULL_DYNAMIC_TEST);
}
// WITH DynamicContainer
@TestFactory
Stream<DynamicContainer>
should_NotSetAccessIdForDynamicContainerInStream_When_AnnotationIsMissing() {
Supplier<DynamicContainer> supplier =
() ->
dynamicContainer("dynamic container", Stream.of(NULL_DYNAMIC_TEST, NULL_DYNAMIC_TEST));
return Stream.generate(supplier).limit(2);
}
@WithAccessId(user = INSIDE_DYNAMIC_TEST_USER)
@TestFactory
Stream<DynamicContainer> should_SetAccessIdForDynamicContainerInStream_When_AnnotationExists() {
Supplier<DynamicContainer> supplier =
() ->
dynamicContainer(
"dynamic container",
Stream.of(DYNAMIC_TEST_USER_DYNAMIC_TEST, DYNAMIC_TEST_USER_DYNAMIC_TEST));
return Stream.generate(supplier).limit(2);
}
@WithAccessId(user = INSIDE_DYNAMIC_TEST_USER)
@WithAccessId(user = INSIDE_DYNAMIC_TEST_USER)
@TestFactory
Stream<DynamicContainer>
should_SetMultipleAccessIdForDynamicContainerInStream_When_AnnotationsExist() {
Supplier<DynamicContainer> supplier =
() ->
dynamicContainer(
"dynamic container", Stream.of(NOT_NULL_DYNAMIC_TEST, NOT_NULL_DYNAMIC_TEST));
return Stream.generate(supplier).limit(2);
}
// WITH nested DynamicContainer
@TestFactory
Stream<DynamicContainer>
should_NotSetAccessIdForNestedDynamicContainerInStream_When_AnnotationIsMissing() {
Supplier<DynamicContainer> supplier =
() -> dynamicContainer("inside container", Stream.of(NULL_DYNAMIC_TEST, NULL_DYNAMIC_TEST));
Supplier<DynamicContainer> outsideSupplier =
() -> dynamicContainer("outside container", Stream.of(supplier.get(), NULL_DYNAMIC_TEST));
return Stream.generate(outsideSupplier).limit(2);
}
@WithAccessId(user = INSIDE_DYNAMIC_TEST_USER)
@TestFactory
Stream<DynamicContainer>
should_SetAccessIdForNestedDynamicContainerInStream_When_AnnotationExists() {
Supplier<DynamicContainer> supplier =
() ->
dynamicContainer(
"inside container",
Stream.of(DYNAMIC_TEST_USER_DYNAMIC_TEST, DYNAMIC_TEST_USER_DYNAMIC_TEST));
Supplier<DynamicContainer> outsideSupplier =
() ->
dynamicContainer(
"outside container", Stream.of(supplier.get(), DYNAMIC_TEST_USER_DYNAMIC_TEST));
return Stream.generate(outsideSupplier).limit(2);
}
@WithAccessId(user = INSIDE_DYNAMIC_TEST_USER)
@WithAccessId(user = INSIDE_DYNAMIC_TEST_USER)
@TestFactory
Stream<DynamicContainer>
should_SetMultipleAccessIdForNestedDynamicContainerInStream_When_AnnotationsExist() {
Supplier<DynamicContainer> supplier =
() ->
dynamicContainer(
"inside container", Stream.of(NOT_NULL_DYNAMIC_TEST, NOT_NULL_DYNAMIC_TEST));
Supplier<DynamicContainer> outsideSupplier =
() ->
dynamicContainer("outside container", Stream.of(supplier.get(), NOT_NULL_DYNAMIC_TEST));
return Stream.generate(outsideSupplier).limit(2);
}
// endregion
// region RETURNING Iterable<DynamicNode>
@TestFactory
Iterable<DynamicTest> should_NotSetAccessIdForDynamicTestInIterable_When_AnnotationIsMissing() {
return Stream.of(NULL_DYNAMIC_TEST, NULL_DYNAMIC_TEST).collect(Collectors.toList());
}
@WithAccessId(user = INSIDE_DYNAMIC_TEST_USER)
@TestFactory
Iterable<DynamicTest> should_SetAccessIdForDynamicTestInIterable_When_AnnotationExists() {
return Stream.of(DYNAMIC_TEST_USER_DYNAMIC_TEST, DYNAMIC_TEST_USER_DYNAMIC_TEST)
.collect(Collectors.toList());
}
@WithAccessId(user = INSIDE_DYNAMIC_TEST_USER)
@WithAccessId(user = INSIDE_DYNAMIC_TEST_USER)
@TestFactory
Iterable<DynamicTest> should_SetMultipleAccessIdForDynamicTestInIterable_When_AnnotationsExist() {
return Stream.of(NOT_NULL_DYNAMIC_TEST, NOT_NULL_DYNAMIC_TEST).collect(Collectors.toList());
}
// WITH DynamicContainer
@TestFactory
Iterable<DynamicContainer>
should_NotSetAccessIdForDynamicContainerInIterable_When_AnnotationIsMissing() {
Supplier<DynamicContainer> supplier =
() ->
dynamicContainer("dynamic container", Stream.of(NULL_DYNAMIC_TEST, NULL_DYNAMIC_TEST));
return Stream.generate(supplier).limit(2).collect(Collectors.toList());
}
@WithAccessId(user = INSIDE_DYNAMIC_TEST_USER)
@TestFactory
Iterable<DynamicContainer>
should_SetAccessIdForDynamicContainerInIterable_When_AnnotationExists() {
Supplier<DynamicContainer> supplier =
() ->
dynamicContainer(
"dynamic container",
Stream.of(DYNAMIC_TEST_USER_DYNAMIC_TEST, DYNAMIC_TEST_USER_DYNAMIC_TEST));
return Stream.generate(supplier).limit(2).collect(Collectors.toList());
}
@WithAccessId(user = INSIDE_DYNAMIC_TEST_USER)
@WithAccessId(user = INSIDE_DYNAMIC_TEST_USER)
@TestFactory
Iterable<DynamicContainer>
should_SetMultipleAccessIdForDynamicContainerInIterable_When_AnnotationsExist() {
Supplier<DynamicContainer> supplier =
() ->
dynamicContainer(
"dynamic container", Stream.of(NOT_NULL_DYNAMIC_TEST, NOT_NULL_DYNAMIC_TEST));
return Stream.generate(supplier).limit(2).collect(Collectors.toList());
}
// WITH nested DynamicContainer
@TestFactory
Iterable<DynamicContainer>
should_NotSetAccessIdForNestedDynamicContainerInIterable_When_AnnotationIsMissing() {
Supplier<DynamicContainer> supplier =
() -> dynamicContainer("inside container", Stream.of(NULL_DYNAMIC_TEST, NULL_DYNAMIC_TEST));
Supplier<DynamicContainer> outsideSupplier =
() -> dynamicContainer("outside container", Stream.of(supplier.get(), NULL_DYNAMIC_TEST));
return Stream.generate(outsideSupplier).limit(2).collect(Collectors.toList());
}
@WithAccessId(user = INSIDE_DYNAMIC_TEST_USER)
@TestFactory
Iterable<DynamicContainer>
should_SetAccessIdForNestedDynamicContainerInIterable_When_AnnotationExists() {
Supplier<DynamicContainer> supplier =
() ->
dynamicContainer(
"inside container",
Stream.of(DYNAMIC_TEST_USER_DYNAMIC_TEST, DYNAMIC_TEST_USER_DYNAMIC_TEST));
Supplier<DynamicContainer> outsideSupplier =
() ->
dynamicContainer(
"outside container", Stream.of(supplier.get(), DYNAMIC_TEST_USER_DYNAMIC_TEST));
return Stream.generate(outsideSupplier).limit(2).collect(Collectors.toList());
}
@WithAccessId(user = INSIDE_DYNAMIC_TEST_USER)
@WithAccessId(user = INSIDE_DYNAMIC_TEST_USER)
@TestFactory
Iterable<DynamicContainer>
should_SetMultipleAccessIdForNestedDynamicContainerInIterable_When_AnnotationsExist() {
Supplier<DynamicContainer> supplier =
() ->
dynamicContainer(
"inside container", Stream.of(NOT_NULL_DYNAMIC_TEST, NOT_NULL_DYNAMIC_TEST));
Supplier<DynamicContainer> outsideSupplier =
() ->
dynamicContainer("outside container", Stream.of(supplier.get(), NOT_NULL_DYNAMIC_TEST));
return Stream.generate(outsideSupplier).limit(2).collect(Collectors.toList());
}
// endregion
// region RETURNING Iterator<DynamicNode>
@TestFactory
Iterator<DynamicTest> should_NotSetAccessIdForDynamicTestInIterator_When_AnnotationIsMissing() {
return Stream.of(NULL_DYNAMIC_TEST, NULL_DYNAMIC_TEST).iterator();
}
@WithAccessId(user = INSIDE_DYNAMIC_TEST_USER)
@TestFactory
Iterator<DynamicTest> should_SetAccessIdForDynamicTestInIterator_When_AnnotationExists() {
return Stream.of(DYNAMIC_TEST_USER_DYNAMIC_TEST, DYNAMIC_TEST_USER_DYNAMIC_TEST).iterator();
}
@WithAccessId(user = INSIDE_DYNAMIC_TEST_USER)
@WithAccessId(user = INSIDE_DYNAMIC_TEST_USER)
@TestFactory
Iterator<DynamicTest> should_SetMultipleAccessIdForDynamicTestInIterator_When_AnnotationsExist() {
return Stream.of(NOT_NULL_DYNAMIC_TEST, NOT_NULL_DYNAMIC_TEST).iterator();
}
// WITH DynamicContainer
@TestFactory
Iterator<DynamicContainer>
should_NotSetAccessIdForDynamicContainerInIterator_When_AnnotationIsMissing() {
Supplier<DynamicContainer> supplier =
() ->
dynamicContainer("dynamic container", Stream.of(NULL_DYNAMIC_TEST, NULL_DYNAMIC_TEST));
return Stream.generate(supplier).limit(2).iterator();
}
@WithAccessId(user = INSIDE_DYNAMIC_TEST_USER)
@TestFactory
Iterator<DynamicContainer>
should_SetAccessIdForDynamicContainerInIterator_When_AnnotationExists() {
Supplier<DynamicContainer> supplier =
() ->
dynamicContainer(
"dynamic container",
Stream.of(DYNAMIC_TEST_USER_DYNAMIC_TEST, DYNAMIC_TEST_USER_DYNAMIC_TEST));
return Stream.generate(supplier).limit(2).iterator();
}
@WithAccessId(user = INSIDE_DYNAMIC_TEST_USER)
@WithAccessId(user = INSIDE_DYNAMIC_TEST_USER)
@TestFactory
Iterator<DynamicContainer>
should_SetMultipleAccessIdForDynamicContainerInIterator_When_AnnotationsExist() {
Supplier<DynamicContainer> supplier =
() ->
dynamicContainer(
"dynamic container", Stream.of(NOT_NULL_DYNAMIC_TEST, NOT_NULL_DYNAMIC_TEST));
return Stream.generate(supplier).limit(2).iterator();
}
// WITH nested DynamicContainer
@TestFactory
Iterator<DynamicContainer>
should_NotSetAccessIdForNestedDynamicContainerInIterator_When_AnnotationIsMissing() {
Supplier<DynamicContainer> supplier =
() -> dynamicContainer("inside container", Stream.of(NULL_DYNAMIC_TEST, NULL_DYNAMIC_TEST));
Supplier<DynamicContainer> outsideSupplier =
() -> dynamicContainer("outside container", Stream.of(supplier.get(), NULL_DYNAMIC_TEST));
return Stream.generate(outsideSupplier).limit(2).iterator();
}
@WithAccessId(user = INSIDE_DYNAMIC_TEST_USER)
@TestFactory
Iterator<DynamicContainer>
should_SetAccessIdForNestedDynamicContainerInIterator_When_AnnotationExists() {
Supplier<DynamicContainer> supplier =
() ->
dynamicContainer(
"inside container",
Stream.of(DYNAMIC_TEST_USER_DYNAMIC_TEST, DYNAMIC_TEST_USER_DYNAMIC_TEST));
Supplier<DynamicContainer> outsideSupplier =
() ->
dynamicContainer(
"outside container", Stream.of(supplier.get(), DYNAMIC_TEST_USER_DYNAMIC_TEST));
return Stream.generate(outsideSupplier).limit(2).iterator();
}
@WithAccessId(user = INSIDE_DYNAMIC_TEST_USER)
@WithAccessId(user = INSIDE_DYNAMIC_TEST_USER)
@TestFactory
Iterator<DynamicContainer>
should_SetMultipleAccessIdForNestedDynamicContainerInIterator_When_AnnotationsExist() {
Supplier<DynamicContainer> supplier =
() ->
dynamicContainer(
"inside container", Stream.of(NOT_NULL_DYNAMIC_TEST, NOT_NULL_DYNAMIC_TEST));
Supplier<DynamicContainer> outsideSupplier =
() ->
dynamicContainer("outside container", Stream.of(supplier.get(), NOT_NULL_DYNAMIC_TEST));
return Stream.generate(outsideSupplier).limit(2).iterator();
}
// endregion
// region RETURNING DynamicNode[]
@TestFactory
DynamicTest[] should_NotSetAccessIdForDynamicTestInArray_When_AnnotationIsMissing() {
return Stream.of(NULL_DYNAMIC_TEST, NULL_DYNAMIC_TEST).toArray(DynamicTest[]::new);
}
@WithAccessId(user = INSIDE_DYNAMIC_TEST_USER)
@TestFactory
DynamicTest[] should_SetAccessIdForDynamicTestInArray_When_AnnotationExists() {
return Stream.of(DYNAMIC_TEST_USER_DYNAMIC_TEST, DYNAMIC_TEST_USER_DYNAMIC_TEST)
.toArray(DynamicTest[]::new);
}
@WithAccessId(user = INSIDE_DYNAMIC_TEST_USER)
@WithAccessId(user = INSIDE_DYNAMIC_TEST_USER)
@TestFactory
DynamicTest[] should_SetMultipleAccessIdForDynamicTestInArray_When_AnnotationsExist() {
return Stream.of(NOT_NULL_DYNAMIC_TEST, NOT_NULL_DYNAMIC_TEST).toArray(DynamicTest[]::new);
}
// WITH DynamicContainer
@TestFactory
DynamicContainer[] should_NotSetAccessIdForDynamicContainerInArray_When_AnnotationIsMissing() {
Supplier<DynamicContainer> supplier =
() ->
dynamicContainer("dynamic container", Stream.of(NULL_DYNAMIC_TEST, NULL_DYNAMIC_TEST));
return Stream.generate(supplier).limit(2).toArray(DynamicContainer[]::new);
}
@WithAccessId(user = INSIDE_DYNAMIC_TEST_USER)
@TestFactory
DynamicContainer[] should_SetAccessIdForDynamicContainerInArray_When_AnnotationExists() {
Supplier<DynamicContainer> supplier =
() ->
dynamicContainer(
"dynamic container",
Stream.of(DYNAMIC_TEST_USER_DYNAMIC_TEST, DYNAMIC_TEST_USER_DYNAMIC_TEST));
return Stream.generate(supplier).limit(2).toArray(DynamicContainer[]::new);
}
@WithAccessId(user = INSIDE_DYNAMIC_TEST_USER)
@WithAccessId(user = INSIDE_DYNAMIC_TEST_USER)
@TestFactory
DynamicContainer[] should_SetMultipleAccessIdForDynamicContainerInArray_When_AnnotationsExist() {
Supplier<DynamicContainer> supplier =
() ->
dynamicContainer(
"dynamic container", Stream.of(NOT_NULL_DYNAMIC_TEST, NOT_NULL_DYNAMIC_TEST));
return Stream.generate(supplier).limit(2).toArray(DynamicContainer[]::new);
}
// WITH nested DynamicContainer
@TestFactory
DynamicContainer[]
should_NotSetAccessIdForNestedDynamicContainerInArray_When_AnnotationIsMissing() {
Supplier<DynamicContainer> supplier =
() -> dynamicContainer("inside container", Stream.of(NULL_DYNAMIC_TEST, NULL_DYNAMIC_TEST));
Supplier<DynamicContainer> outsideSupplier =
() -> dynamicContainer("outside container", Stream.of(supplier.get(), NULL_DYNAMIC_TEST));
return Stream.generate(outsideSupplier).limit(2).toArray(DynamicContainer[]::new);
}
@WithAccessId(user = INSIDE_DYNAMIC_TEST_USER)
@TestFactory
DynamicContainer[] should_SetAccessIdForNestedDynamicContainerInArray_When_AnnotationExists() {
Supplier<DynamicContainer> supplier =
() ->
dynamicContainer(
"inside container",
Stream.of(DYNAMIC_TEST_USER_DYNAMIC_TEST, DYNAMIC_TEST_USER_DYNAMIC_TEST));
Supplier<DynamicContainer> outsideSupplier =
() ->
dynamicContainer(
"outside container", Stream.of(supplier.get(), DYNAMIC_TEST_USER_DYNAMIC_TEST));
return Stream.generate(outsideSupplier).limit(2).toArray(DynamicContainer[]::new);
}
@WithAccessId(user = INSIDE_DYNAMIC_TEST_USER)
@WithAccessId(user = INSIDE_DYNAMIC_TEST_USER)
@TestFactory
DynamicContainer[]
should_SetMultipleAccessIdForNestedDynamicContainerInArray_When_AnnotationsExist() {
Supplier<DynamicContainer> supplier =
() ->
dynamicContainer(
"inside container", Stream.of(NOT_NULL_DYNAMIC_TEST, NOT_NULL_DYNAMIC_TEST));
Supplier<DynamicContainer> outsideSupplier =
() ->
dynamicContainer("outside container", Stream.of(supplier.get(), NOT_NULL_DYNAMIC_TEST));
return Stream.generate(outsideSupplier).limit(2).toArray(DynamicContainer[]::new);
}
// endregion
// region JaasExtension#interceptTestClassConstructor
@Nested
@TestInstance(Lifecycle.PER_CLASS)
class ConstructorWithoutAccessId {
ConstructorWithoutAccessId() {
assertThat(CURRENT_USER_CONTEXT.getUserid()).isNull();
}
@Test
void should_NotSetJaasSubject_When_AnnotationIsMissing_On_Constructor() {
assertThat(CURRENT_USER_CONTEXT.getUserid()).isNull();
}
}
@Nested
@TestInstance(Lifecycle.PER_CLASS)
class ConstructorWithAccessId {
@WithAccessId(user = "constructor")
ConstructorWithAccessId() {
assertThat(CURRENT_USER_CONTEXT.getUserid()).isEqualTo("constructor");
}
@Test
void should_SetJaasSubject_When_AnnotationExists_On_Constructor() {
assertThat(CURRENT_USER_CONTEXT.getUserid()).isNull();
}
}
// endregion
// endregion
}

View File

@ -0,0 +1,37 @@
package pro.taskana.testapi.tests.security;
import static org.assertj.core.api.Assertions.assertThat;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.api.extension.ParameterResolutionException;
import org.junit.jupiter.api.extension.TestExecutionExceptionHandler;
import org.junit.platform.commons.JUnitException;
public class JaasExtensionTestExtensions {
static class ShouldThrowParameterResolutionException implements TestExecutionExceptionHandler {
@Override
public void handleTestExecutionException(ExtensionContext context, Throwable throwable)
throws Throwable {
if (throwable instanceof ParameterResolutionException) {
return;
}
throw throwable;
}
}
static class ShouldThrowJunitException implements TestExecutionExceptionHandler {
@Override
public void handleTestExecutionException(ExtensionContext context, Throwable throwable)
throws Throwable {
if (throwable instanceof JUnitException) {
JUnitException exception = (JUnitException) throwable;
assertThat(exception.getMessage())
.isEqualTo("Please use @TestTemplate instead of @Test for multiple accessIds");
return;
}
throw throwable;
}
}
}

View File

@ -0,0 +1 @@
taskana/db2:11.5

View File

@ -0,0 +1,35 @@
# SLF4J's SimpleLogger configuration file
# Simple implementation of Logger that sends all enabled log messages, for all defined loggers, to System.err.
# Default logging detail level for all instances of SimpleLogger.
# Must be one of ("trace", "debug", "info", "warn", or "error").
# If not specified, defaults to "info".
org.slf4j.simpleLogger.defaultLogLevel=info
# Logging detail level for a SimpleLogger instance named "xxxxx".
# Must be one of ("trace", "debug", "info", "warn", or "error").
# If not specified, the default logging detail level is used.
org.slf4j.simpleLogger.log.pro.taskana=info
org.slf4j.simpleLogger.log.org.apache.ibatis=info
# Set to true if you want the current date and time to be included in output messages.
# Default is false, and will output the number of milliseconds elapsed since startup.
org.slf4j.simpleLogger.showDateTime=true
# The date and time format to be used in the output messages.
# The pattern describing the date and time format is the same that is used in java.text.SimpleDateFormat.
# If the format is not specified or is invalid, the default format is used.
# The default format is yyyy-MM-dd HH:mm:ss:SSS Z.
org.slf4j.simpleLogger.dateTimeFormat=yyyy-MM-dd HH:mm:ss:SSS Z
# Set to true if you want to output the current thread name.
# Defaults to true.
org.slf4j.simpleLogger.showThreadName=true
# Set to true if you want the Logger instance name to be included in output messages.
# Defaults to true.
#org.slf4j.simpleLogger.showLogName=true
# Set to true if you want the last component of the name to be included in output messages.
# Defaults to false.
org.slf4j.simpleLogger.showShortLogName=false

View File

@ -0,0 +1,20 @@
taskana.roles.user=cn=ksc-users,cn=groups,OU=Test,O=TASKANA | teamlead-1 | teamlead-2 | user-1-1 | user-1-2 | user-2-1 | user-2-2 | user-b-1 | user-b-2
taskana.roles.admin=admin | uid=admin,cn=users,OU=Test,O=TASKANA
taskana.roles.businessadmin=businessadmin | cn=business-admins,cn=groups,OU=Test,O=TASKANA
taskana.roles.monitor=monitor | cn=monitor-users,cn=groups,OU=Test,O=TASKANA
taskana.roles.taskadmin=taskadmin
taskana.domains=Domain_A , DOMAIN_B
taskana.classification.types=TASK , document
taskana.classification.categories.task=EXTERNAL, manual, autoMAtic, Process
taskana.classification.categories.document=EXTERNAL
taskana.jobs.maxRetries=3
taskana.jobs.batchSize=50
taskana.jobs.cleanup.runEvery=P1D
taskana.jobs.cleanup.firstRunAt=2018-07-25T08:00:00Z
taskana.jobs.cleanup.minimumAge=P14D
taskana.german.holidays.enabled=true
taskana.german.holidays.corpus-christi.enabled=false
taskana.history.deletion.on.task.deletion.enabled=true
taskana.validation.allowTimestampServiceLevelMismatch=false
taskana.query.includeLongName=false