TSK-2005: validate TaskanaConfiguration

This commit is contained in:
arolfes 2023-02-28 17:53:14 +01:00 committed by Mustapha Zorgati
parent 0e52daae4a
commit 773ac7619d
7 changed files with 362 additions and 30 deletions

View File

@ -58,6 +58,7 @@ import org.junit.jupiter.api.TestInstance;
import org.junit.jupiter.api.TestInstance.Lifecycle;
import org.junit.jupiter.api.TestTemplate;
import org.junit.jupiter.api.function.ThrowingConsumer;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.platform.commons.support.AnnotationSupport;
import pro.taskana.TaskanaConfiguration;
@ -110,6 +111,7 @@ class ArchitectureTest {
.that(
are(
annotatedWith(Test.class)
.or(annotatedWith(ParameterizedTest.class))
.or(annotatedWith(TestFactory.class))
.or(annotatedWith(TestTemplate.class))))
.and()

View File

@ -2,6 +2,7 @@ package acceptance;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import java.lang.reflect.Field;
import java.time.DayOfWeek;
@ -15,16 +16,20 @@ import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Stream;
import org.assertj.core.api.ThrowableAssert.ThrowingCallable;
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 org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import pro.taskana.TaskanaConfiguration;
import pro.taskana.TaskanaConfiguration.Builder;
import pro.taskana.common.api.CustomHoliday;
import pro.taskana.common.api.LocalTimeInterval;
import pro.taskana.common.api.TaskanaRole;
import pro.taskana.common.api.exceptions.InvalidArgumentException;
import pro.taskana.common.internal.util.ReflectionUtil;
import pro.taskana.testapi.extensions.TestContainerExtension;
import pro.taskana.workbasket.api.WorkbasketPermission;
@ -98,7 +103,7 @@ class TaskanaConfigurationTest {
int expectedNumberOfJobRetries = 500;
Instant expectedCleanupJobFirstJun = Instant.MIN;
Duration expectedCleanupJobRunEvery = Duration.ofDays(2);
Duration expectedCleanupJobMinimumAge = Duration.ZERO;
Duration expectedCleanupJobMinimumAge = Duration.ofDays(1);
int expectedPriorityJobBatchSize = 49;
Instant expectedPriorityJobFirstRun = Instant.MIN.plus(1, ChronoUnit.DAYS);
Instant expectedUserRefreshJobFirstRun = Instant.MIN.plus(2, ChronoUnit.DAYS);
@ -209,7 +214,7 @@ class TaskanaConfigurationTest {
.maxNumberOfJobRetries(500)
.cleanupJobFirstRun(Instant.MIN)
.cleanupJobRunEvery(Duration.ofDays(2))
.cleanupJobMinimumAge(Duration.ZERO)
.cleanupJobMinimumAge(Duration.ofDays(1))
.taskCleanupJobAllCompletedSameParentBusiness(false)
.priorityJobBatchSize(49)
.priorityJobFirstRun(Instant.MIN.plus(1, ChronoUnit.DAYS))
@ -240,4 +245,197 @@ class TaskanaConfigurationTest {
.usingRecursiveComparison()
.isEqualTo(configuration);
}
@ParameterizedTest
@ValueSource(ints = {-1, 0})
void should_ThrowInvalidArgumentException_When_JobBatchSizeIsNotPositive(int jobBatchSize) {
TaskanaConfiguration.Builder builder =
new TaskanaConfiguration.Builder(
TestContainerExtension.createDataSourceForH2(), false, "TASKANA")
.jobBatchSize(jobBatchSize);
ThrowingCallable call = builder::build;
assertThatThrownBy(call)
.isInstanceOf(InvalidArgumentException.class)
.hasMessageContaining(
"Parameter jobBatchSize (taskana.jobs.batchSize) must be a positive integer");
}
@ParameterizedTest
@ValueSource(ints = {-1, 0})
void should_ThrowInvalidArgumentException_When_MaxNumberOfJobRetriesIsNotPositive(
int maxNumberOfJobRetries) {
TaskanaConfiguration.Builder builder =
new TaskanaConfiguration.Builder(
TestContainerExtension.createDataSourceForH2(), false, "TASKANA")
.jobBatchSize(1)
.maxNumberOfJobRetries(maxNumberOfJobRetries);
ThrowingCallable call = builder::build;
assertThatThrownBy(call)
.isInstanceOf(InvalidArgumentException.class)
.hasMessageContaining(
"Parameter maxNumberOfJobRetries (taskana.jobs.maxRetries)"
+ " must be a positive integer");
}
@ParameterizedTest
@ValueSource(strings = {"P-1D", "P0D"})
void should_ThrowInvalidArgumentException_When_CleanupJobRunEveryIsNotPositive(
String cleanupJobRunEvery) {
TaskanaConfiguration.Builder builder =
new TaskanaConfiguration.Builder(
TestContainerExtension.createDataSourceForH2(), false, "TASKANA")
.jobBatchSize(1)
.maxNumberOfJobRetries(1)
.cleanupJobRunEvery(Duration.parse(cleanupJobRunEvery));
ThrowingCallable call = builder::build;
assertThatThrownBy(call)
.isInstanceOf(InvalidArgumentException.class)
.hasMessageContaining(
"Parameter cleanupJobRunEvery (taskana.jobs.cleanup.runEvery)"
+ " must be a positive integer");
}
@Test
void should_ThrowInvalidArgumentException_When_CleanupJobMinimumAgeIsNegative() {
TaskanaConfiguration.Builder builder =
new TaskanaConfiguration.Builder(
TestContainerExtension.createDataSourceForH2(), false, "TASKANA")
.jobBatchSize(1)
.maxNumberOfJobRetries(1)
.cleanupJobRunEvery(Duration.ofDays(1))
.cleanupJobMinimumAge(Duration.ofDays(-1));
ThrowingCallable call = builder::build;
assertThatThrownBy(call)
.isInstanceOf(InvalidArgumentException.class)
.hasMessageContaining(
"Parameter cleanupJobMinimumAge (taskana.jobs.cleanup.minimumAge)"
+ " must be a positive integer");
}
@ParameterizedTest
@ValueSource(ints = {-1, 0})
void should_ThrowInvalidArgumentException_When_PriorityJobBatchSizeIsNotPositive(
int priorityJobBatchSize) {
TaskanaConfiguration.Builder builder =
new TaskanaConfiguration.Builder(
TestContainerExtension.createDataSourceForH2(), false, "TASKANA")
.jobBatchSize(1)
.maxNumberOfJobRetries(1)
.cleanupJobRunEvery(Duration.ofDays(1))
.cleanupJobMinimumAge(Duration.ofDays(1))
.priorityJobBatchSize(priorityJobBatchSize);
ThrowingCallable call = builder::build;
assertThatThrownBy(call)
.isInstanceOf(InvalidArgumentException.class)
.hasMessageContaining(
"Parameter priorityJobBatchSize (taskana.jobs.priority.batchSize)"
+ " must be a positive integer");
}
@ParameterizedTest
@ValueSource(strings = {"P-1D", "P0D"})
void should_ThrowInvalidArgumentException_When_PriorityJobRunEveryIsNotPositive(
String priorityJobRunEvery) {
TaskanaConfiguration.Builder builder =
new TaskanaConfiguration.Builder(
TestContainerExtension.createDataSourceForH2(), false, "TASKANA")
.jobBatchSize(1)
.maxNumberOfJobRetries(1)
.cleanupJobRunEvery(Duration.ofDays(1))
.cleanupJobMinimumAge(Duration.ofDays(1))
.priorityJobBatchSize(1)
.priorityJobRunEvery(Duration.parse(priorityJobRunEvery));
ThrowingCallable call = builder::build;
assertThatThrownBy(call)
.isInstanceOf(InvalidArgumentException.class)
.hasMessageContaining(
"Parameter priorityJobRunEvery (taskana.jobs.priority.runEvery)"
+ " must be a positive integer");
}
@ParameterizedTest
@ValueSource(strings = {"P-1D", "P0D"})
void should_ThrowInvalidArgumentException_When_UserRefreshJobRunEveryIsNotPositive(
String userRefreshJobRunEvery) {
TaskanaConfiguration.Builder builder =
new TaskanaConfiguration.Builder(
TestContainerExtension.createDataSourceForH2(), false, "TASKANA")
.jobBatchSize(1)
.maxNumberOfJobRetries(1)
.cleanupJobRunEvery(Duration.ofDays(1))
.cleanupJobMinimumAge(Duration.ofDays(1))
.priorityJobBatchSize(1)
.priorityJobRunEvery(Duration.ofDays(1))
.userRefreshJobRunEvery(Duration.parse(userRefreshJobRunEvery));
ThrowingCallable call = builder::build;
assertThatThrownBy(call)
.isInstanceOf(InvalidArgumentException.class)
.hasMessageContaining(
"Parameter userRefreshJobRunEvery (taskana.jobs.user.refresh.runEvery)"
+ " must be a positive integer");
}
@Test
void should_ThrowInvalidArgumentException_When_JobSchededulerInitialStartDelayIsNotPositive() {
TaskanaConfiguration.Builder builder =
new TaskanaConfiguration.Builder(
TestContainerExtension.createDataSourceForH2(), false, "TASKANA")
.jobBatchSize(1)
.maxNumberOfJobRetries(1)
.cleanupJobRunEvery(Duration.ofDays(1))
.cleanupJobMinimumAge(Duration.ofDays(1))
.priorityJobBatchSize(1)
.priorityJobRunEvery(Duration.ofDays(1))
.userRefreshJobRunEvery(Duration.ofDays(1))
.jobSchedulerInitialStartDelay(-1);
ThrowingCallable call = builder::build;
assertThatThrownBy(call)
.isInstanceOf(InvalidArgumentException.class)
.hasMessageContaining(
"Parameter jobSchedulerInitialStartDelay (taskana.jobscheduler.initialstartdelay)"
+ " must be a positive integer");
}
@ParameterizedTest
@ValueSource(ints = {-1, 0})
void should_ThrowInvalidArgumentException_When_JobSchedulerPeriodIsNotPositive(
int jobSchedulerPeriod) {
TaskanaConfiguration.Builder builder =
new TaskanaConfiguration.Builder(
TestContainerExtension.createDataSourceForH2(), false, "TASKANA")
.jobBatchSize(1)
.maxNumberOfJobRetries(1)
.cleanupJobRunEvery(Duration.ofDays(1))
.cleanupJobMinimumAge(Duration.ofDays(1))
.priorityJobBatchSize(1)
.priorityJobRunEvery(Duration.ofDays(1))
.userRefreshJobRunEvery(Duration.ofDays(1))
.jobSchedulerInitialStartDelay(1)
.jobSchedulerPeriod(jobSchedulerPeriod);
ThrowingCallable call = builder::build;
assertThatThrownBy(call)
.isInstanceOf(InvalidArgumentException.class)
.hasMessageContaining(
"Parameter jobSchedulerPeriod (taskana.jobscheduler.period)"
+ " must be a positive integer");
}
}

View File

@ -32,6 +32,7 @@ import org.slf4j.LoggerFactory;
import pro.taskana.common.api.CustomHoliday;
import pro.taskana.common.api.LocalTimeInterval;
import pro.taskana.common.api.TaskanaRole;
import pro.taskana.common.api.exceptions.InvalidArgumentException;
import pro.taskana.common.api.exceptions.SystemException;
import pro.taskana.common.internal.configuration.DB;
import pro.taskana.common.internal.configuration.TaskanaProperty;
@ -403,6 +404,89 @@ public class TaskanaConfiguration {
return true;
}
@Override
public String toString() {
return "TaskanaConfiguration [dataSource="
+ dataSource
+ ", securityEnabled="
+ securityEnabled
+ ", useManagedTransactions="
+ useManagedTransactions
+ ", schemaName="
+ schemaName
+ ", germanPublicHolidaysEnabled="
+ germanPublicHolidaysEnabled
+ ", corpusChristiEnabled="
+ corpusChristiEnabled
+ ", deleteHistoryOnTaskDeletionEnabled="
+ deleteHistoryOnTaskDeletionEnabled
+ ", properties="
+ properties
+ ", workingTimeSchedule="
+ workingTimeSchedule
+ ", jobSchedulerEnabled="
+ jobSchedulerEnabled
+ ", jobSchedulerInitialStartDelay="
+ jobSchedulerInitialStartDelay
+ ", jobSchedulerPeriod="
+ jobSchedulerPeriod
+ ", jobSchedulerPeriodTimeUnit="
+ jobSchedulerPeriodTimeUnit
+ ", jobSchedulerEnableTaskCleanupJob="
+ jobSchedulerEnableTaskCleanupJob
+ ", jobSchedulerEnableTaskUpdatePriorityJob="
+ jobSchedulerEnableTaskUpdatePriorityJob
+ ", jobSchedulerEnableWorkbasketCleanupJob="
+ jobSchedulerEnableWorkbasketCleanupJob
+ ", jobSchedulerEnableUserInfoRefreshJob="
+ jobSchedulerEnableUserInfoRefreshJob
+ ", jobSchedulerEnableHistorieCleanupJob="
+ jobSchedulerEnableHistorieCleanupJob
+ ", jobSchedulerCustomJobs="
+ jobSchedulerCustomJobs
+ ", domains="
+ domains
+ ", roleMap="
+ roleMap
+ ", classificationTypes="
+ classificationTypes
+ ", classificationCategoriesByType="
+ classificationCategoriesByType
+ ", allowTimestampServiceLevelMismatch="
+ allowTimestampServiceLevelMismatch
+ ", customHolidays="
+ customHolidays
+ ", jobBatchSize="
+ jobBatchSize
+ ", maxNumberOfJobRetries="
+ maxNumberOfJobRetries
+ ", cleanupJobFirstRun="
+ cleanupJobFirstRun
+ ", cleanupJobRunEvery="
+ cleanupJobRunEvery
+ ", cleanupJobMinimumAge="
+ cleanupJobMinimumAge
+ ", taskCleanupJobAllCompletedSameParentBusiness="
+ taskCleanupJobAllCompletedSameParentBusiness
+ ", priorityJobBatchSize="
+ priorityJobBatchSize
+ ", priorityJobFirstRun="
+ priorityJobFirstRun
+ ", priorityJobRunEvery="
+ priorityJobRunEvery
+ ", priorityJobActive="
+ priorityJobActive
+ ", userRefreshJobRunEvery="
+ userRefreshJobRunEvery
+ ", userRefreshJobFirstRun="
+ userRefreshJobFirstRun
+ ", addAdditionalUserInfo="
+ addAdditionalUserInfo
+ ", minimalPermissionsToAssignDomains="
+ minimalPermissionsToAssignDomains
+ "]";
}
public static class Builder {
private static final Logger LOGGER = LoggerFactory.getLogger(Builder.class);
@ -455,41 +539,36 @@ public class TaskanaConfiguration {
// endregion
// region job configuration
// TODO validate this is positive
@TaskanaProperty("taskana.jobs.batchSize")
private int jobBatchSize = 100;
// TODO validate this is positive
@TaskanaProperty("taskana.jobs.maxRetries")
private int maxNumberOfJobRetries = 3;
@TaskanaProperty("taskana.jobs.cleanup.firstRunAt")
private Instant cleanupJobFirstRun = Instant.parse("2018-01-01T00:00:00Z");
// TODO: validate this is positive
@TaskanaProperty("taskana.jobs.cleanup.runEvery")
private Duration cleanupJobRunEvery = Duration.ofDays(1);
// TODO: validate this is positive
@TaskanaProperty("taskana.jobs.cleanup.minimumAge")
private Duration cleanupJobMinimumAge = Duration.ofDays(14);
@TaskanaProperty("taskana.jobs.cleanup.allCompletedSameParentBusiness")
private boolean taskCleanupJobAllCompletedSameParentBusiness = true;
// TODO: validate this is positive
@TaskanaProperty("taskana.jobs.priority.batchSize")
private int priorityJobBatchSize = 100;
@TaskanaProperty("taskana.jobs.priority.firstRunAt")
private Instant priorityJobFirstRun = Instant.parse("2018-01-01T00:00:00Z");
// TODO: validate this is positive
@TaskanaProperty("taskana.jobs.priority.runEvery")
private Duration priorityJobRunEvery = Duration.ofDays(1);
@TaskanaProperty("taskana.jobs.priority.active")
private boolean priorityJobActive = false;
// TODO: validate this is positive
@TaskanaProperty("taskana.jobs.user.refresh.runEvery")
private Duration userRefreshJobRunEvery = Duration.ofDays(1);
@ -811,6 +890,7 @@ public class TaskanaConfiguration {
}
public TaskanaConfiguration build() {
validateConfiguration();
return new TaskanaConfiguration(this);
}
@ -872,6 +952,59 @@ public class TaskanaConfiguration {
return this;
}
private void validateConfiguration() {
if (jobBatchSize <= 0) {
throw new InvalidArgumentException(
"Parameter jobBatchSize (taskana.jobs.batchSize) must be a positive integer");
}
if (maxNumberOfJobRetries <= 0) {
throw new InvalidArgumentException(
"Parameter maxNumberOfJobRetries (taskana.jobs.maxRetries)"
+ " must be a positive integer");
}
if (cleanupJobRunEvery == null
|| cleanupJobRunEvery.isNegative()
|| cleanupJobRunEvery.isZero()) {
throw new InvalidArgumentException(
"Parameter cleanupJobRunEvery (taskana.jobs.cleanup.runEvery)"
+ " must be a positive integer");
}
if (cleanupJobMinimumAge == null || cleanupJobMinimumAge.isNegative()) {
throw new InvalidArgumentException(
"Parameter cleanupJobMinimumAge (taskana.jobs.cleanup.minimumAge)"
+ " must be a positive integer");
}
if (priorityJobBatchSize <= 0) {
throw new InvalidArgumentException(
"Parameter priorityJobBatchSize (taskana.jobs.priority.batchSize)"
+ " must be a positive integer");
}
if (priorityJobRunEvery == null
|| priorityJobRunEvery.isNegative()
|| priorityJobRunEvery.isZero()) {
throw new InvalidArgumentException(
"Parameter priorityJobRunEvery (taskana.jobs.priority.runEvery)"
+ " must be a positive integer");
}
if (userRefreshJobRunEvery == null
|| userRefreshJobRunEvery.isNegative()
|| userRefreshJobRunEvery.isZero()) {
throw new InvalidArgumentException(
"Parameter userRefreshJobRunEvery (taskana.jobs.user.refresh.runEvery)"
+ " must be a positive integer");
}
if (jobSchedulerInitialStartDelay < 0) {
throw new InvalidArgumentException(
"Parameter jobSchedulerInitialStartDelay (taskana.jobscheduler.initialstartdelay)"
+ " must be a positive integer");
}
if (jobSchedulerPeriod <= 0) {
throw new InvalidArgumentException(
"Parameter jobSchedulerPeriod (taskana.jobscheduler.period) "
+ "must be a positive integer");
}
}
private String initSchemaName(String schemaName) {
if (schemaName == null || schemaName.isEmpty() || schemaName.isBlank()) {
throw new SystemException("schema name can't be null or empty");

View File

@ -75,7 +75,7 @@ class TaskCleanupJobAccTest extends AbstractAccTest {
@WithAccessId(user = "admin")
@Test
void shouldCleanCompletedTasksUntilDateWithSameParentBussiness() throws Exception {
void shouldCleanCompletedTasksUntilDateWithSameParentBusiness() throws Exception {
long totalTasksCount = taskService.createTaskQuery().count();
assertThat(totalTasksCount).isEqualTo(99);
@ -163,7 +163,7 @@ class TaskCleanupJobAccTest extends AbstractAccTest {
TaskanaConfiguration taskanaEngineConfiguration =
new TaskanaConfiguration.Builder(AbstractAccTest.taskanaEngine.getConfiguration())
.taskCleanupJobAllCompletedSameParentBusiness(true)
.cleanupJobMinimumAge(Duration.ZERO)
.cleanupJobMinimumAge(Duration.ofMillis(1))
.build();
TaskanaEngine taskanaEngine =
TaskanaEngine.buildTaskanaEngine(
@ -226,7 +226,7 @@ class TaskCleanupJobAccTest extends AbstractAccTest {
.cleanupJobRunEvery(runEvery)
.cleanupJobFirstRun(firstRun)
.jobSchedulerEnabled(true)
.jobSchedulerInitialStartDelay(0)
.jobSchedulerInitialStartDelay(1)
.jobSchedulerPeriod(1)
.jobSchedulerPeriodTimeUnit(TimeUnit.SECONDS)
.jobSchedulerEnableTaskCleanupJob(true)
@ -247,7 +247,7 @@ class TaskCleanupJobAccTest extends AbstractAccTest {
throws Exception {
TaskanaConfiguration taskanaEngineConfiguration =
new TaskanaConfiguration.Builder(AbstractAccTest.taskanaEngine.getConfiguration())
.cleanupJobRunEvery(Duration.ZERO)
.cleanupJobRunEvery(Duration.ofMillis(1))
.cleanupJobFirstRun(Instant.now().plus(5, ChronoUnit.MINUTES))
.build();

View File

@ -2,7 +2,6 @@ package acceptance.jobs;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatCode;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import acceptance.AbstractAccTest;
import java.time.Duration;
@ -18,7 +17,6 @@ import pro.taskana.common.api.BaseQuery.SortDirection;
import pro.taskana.common.api.ScheduledJob;
import pro.taskana.common.api.TaskanaEngine;
import pro.taskana.common.api.TaskanaEngine.ConnectionManagementMode;
import pro.taskana.common.api.exceptions.SystemException;
import pro.taskana.common.internal.jobs.AbstractTaskanaJob;
import pro.taskana.common.test.security.JaasExtension;
import pro.taskana.common.test.security.WithAccessId;
@ -55,21 +53,6 @@ class TaskUpdatePriorityJobAccTest extends AbstractAccTest {
assertThatCode(job::execute).doesNotThrowAnyException();
}
@Test
@WithAccessId(user = "admin")
void should_catchException_When_executedWithWrongSettings() throws Exception {
// given
TaskanaConfiguration taskanaEngineConfiguration =
new TaskanaConfiguration.Builder(AbstractAccTest.taskanaEngineConfiguration)
.priorityJobBatchSize(0)
.build();
TaskUpdatePriorityJob job =
new TaskUpdatePriorityJob(TaskanaEngine.buildTaskanaEngine(taskanaEngineConfiguration));
// then
assertThatThrownBy(job::execute).isInstanceOf(SystemException.class);
}
@Test
@WithAccessId(user = "admin")
void should_doNothing_When_NotActive() throws Exception {

View File

@ -32,6 +32,7 @@ class SqlConnectionRunnerAccTest extends AbstractAccTest {
connection.prepareStatement("select * from TASK where ID = ?");
preparedStatement.setString(1, taskId);
ResultSet resultSet = preparedStatement.executeQuery();
// then
assertThat(resultSet.next()).isTrue();
});
}

View File

@ -16,3 +16,18 @@ taskana.jobs.history.batchSize=50
taskana.jobs.history.cleanup.firstRunAt=2018-07-25T08:00:00Z
taskana.jobs.history.cleanup.minimumAge=P14D
taskana.jobs.history.cleanup.runEvery=P1D
# enable or disable the jobscheduler at all
# set it to false and no jobs are running
taskana.jobscheduler.enabled=false
# wait time before the first job run in millis
taskana.jobscheduler.initialstartdelay=100000
# sleeping time befor the next job runs
taskana.jobscheduler.period=12
# timeunit for the sleeping period
# Possible values: MILLISECONDS, SECONDS, MINUTES, HOURS, DAYS
taskana.jobscheduler.periodtimeunit=HOURS
taskana.jobscheduler.enableTaskCleanupJob=false
taskana.jobscheduler.enableTaskUpdatePriorityJob=false
taskana.jobscheduler.enableWorkbasketCleanupJob=false
taskana.jobscheduler.enableUserInfoRefreshJob=false
taskana.jobscheduler.enableHistorieCleanupJob=false