Closes #2347: Configuration to enable working day calculation instead of working time calculation

* added configuration property for detailed working time calculation
* added WorkingDayCalculatorImpl to replicate pre 6.0.0 behaviour
* bugfix ServiceLevelHandler comparing classificationKeys

---------

Co-authored-by: arolfes <arolfes@users.noreply.github.com>
This commit is contained in:
Mustapha Zorgati 2023-08-01 14:58:49 +02:00 committed by Alex
parent 61b8ba08bf
commit fbd388e6d4
21 changed files with 3033 additions and 188 deletions

View File

@ -29,6 +29,7 @@ INSERT INTO TASK VALUES('TKI:000000000000000000000000000000000021', 'ETI:0000000
INSERT INTO TASK VALUES('TKI:000000000000000000000000000000000022', 'ETI:000000000000000000000000000000000022', '2018-01-29 15:55:22', null , null , '2018-01-29 15:55:22', null , '2018-01-29 15:55:00', '2018-01-30 15:55:00', 'Widerruf' , 'creator_user_id' , 'Widerruf' , null , 2 , -1 , 'READY' , 'EXTERN' , 'L1050' , 'CLI:100000000000000000000000000000000003', 'WBI:100000000000000000000000000000000001' , 'GPK_KSC' , 'DOMAIN_A', 'PI_0000000000022' , 'DOC_0000000000000000022' , null , '00' , 'PASystem' , '00' , 'SDNR' , '11223344' , false , false , null , 'NONE' , null , '' , '' , '' , '' , '' , '' , '' , '' , '' , '' , '' , '' , '' , 'abc' , '' , '' , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 );
INSERT INTO TASK VALUES('TKI:000000000000000000000000000000000023', 'ETI:000000000000000000000000000000000023', '2018-01-29 15:55:23', null , null , '2018-01-29 15:55:23', null , '2018-01-29 15:55:00', '2018-01-30 15:55:00', 'Widerruf' , 'creator_user_id' , 'Widerruf' , null , 2 , -1 , 'READY' , 'EXTERN' , 'L1050' , 'CLI:100000000000000000000000000000000003', 'WBI:100000000000000000000000000000000001' , 'GPK_KSC' , 'DOMAIN_A', 'PI_0000000000023' , 'DOC_0000000000000000023' , null , '00' , 'PASystem' , '00' , 'SDNR' , '11223344' , false , false , null , 'NONE' , null , '' , '' , '' , '' , '' , '' , '' , 'lnp' , '' , '' , '' , '' , '' , 'abc' , '' , '' , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 );
INSERT INTO TASK VALUES('TKI:000000000000000000000000000000000024', 'ETI:000000000000000000000000000000000024', '2018-01-29 15:55:24', null , null , '2018-01-29 15:55:24', null , '2018-01-29 15:55:00', '2018-01-30 15:55:00', 'Widerruf' , 'creator_user_id' , 'Widerruf' , null , 2 , -1 , 'READY' , 'EXTERN' , 'L1050' , 'CLI:100000000000000000000000000000000003', 'WBI:100000000000000000000000000000000001' , 'GPK_KSC' , 'DOMAIN_A', 'PI_0000000000024' , 'DOC_0000000000000000024' , null , '00' , 'PASystem' , '00' , 'SDNR' , '11223344' , false , false , null , 'NONE' , null , '' , '' , '' , '' , '' , '' , '' , '' , null , '' , '' , '' , '' , 'abc' , '' , '' , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 );
INSERT INTO TASK VALUES('TKI:000000000000000000000000000000000201', 'ETI:000000000000000000000000000000000201', '2023-07-31 15:55:01', '2023-07-31 15:55:00', null , '2023-07-31 15:55:01', null , '2023-07-31 15:55:00', '2023-08-02 15:55:00', 'Task201' , 'creator_user_id' , 'Lorem ipsum was n Quatsch dolor sit amet.', 'Some custom Note' , 2 , -1 , 'READY' , 'EXTERN' , 'L110102' , 'CLI:100000000000000000000000000000000002', 'WBI:100000000000000000000000000000000006' , 'USER-1-1' , 'DOMAIN_A', 'BPI21' , 'PBPI21' , 'user-1-1' , 'MyCompany1', 'MySystem1', 'MyInstance1' , 'MyType1', 'MyValue1' , true , false , null , 'NONE' , null , 'pqr' , '' , '' , '' , '' , '' , '' , '' , '' , '' , '' , '' , '' , 'abc' , '' , '' , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 );
-- TASK TABLE (ID , EXTERNAL_ID , CREATED , CLAIMED , COMPLETED , modified , received , planned , due , name , creator , description , note , priority, manual_priority, state , classification_category , classification_key, classification_id , workbasket_id , workbasket_key, domain , business_process_id, parent_business_process_id, owner , por_company , por_system , por_system_instance, por_type , por_value , is_read, is_transferred,callback_info , callback_state , custom_attributes ,custom1 ,custom2, ,custom3, ,custom4 ,custom5 ,custom6 ,custom7 ,custom8 ,custom9 ,custom10 ,custom11, ,custom12 ,custom13 ,custom14 ,custom15 ,custom16 , custom-int-1, custom-int-2, custom-int-3, custom-int-4, custom-int-5, custom-int-6, custom-int-7, custom-int-8
-- Tasks for WorkOnTaskAccTest
INSERT INTO TASK VALUES('TKI:000000000000000000000000000000000025', 'ETI:000000000000000000000000000000000025', '2018-01-29 15:55:24', null , null , '2018-01-29 15:55:24', '2018-01-29 15:55:24', '2018-01-29 15:55:00', '2018-01-30 15:55:00', 'Widerruf' , 'creator_user_id' , 'Widerruf' , null , 2 , -1 , 'READY' , 'EXTERN' , 'L1050' , 'CLI:100000000000000000000000000000000003', 'WBI:100000000000000000000000000000000007' , 'USER-1-2' , 'DOMAIN_A', 'PI_0000000000025' , 'DOC_0000000000000000025' , null , 'abcd00' , 'PASystem' , '00' , 'SDNR' , '98765432' , false , false , null , 'NONE' , null , '' , '' , '' , '' , '' , '' , '' , '' , '' , 'ert' , 'ert' , 'ert' , 'ert' , 'abc' , 'ert' , 'ert' , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 );

View File

@ -0,0 +1,118 @@
package pro.taskana.common.internal.workingtime;
import java.time.DayOfWeek;
import java.time.Duration;
import java.time.Instant;
import java.time.LocalDate;
import java.time.ZoneId;
import java.time.temporal.ChronoUnit;
import java.util.stream.LongStream;
import pro.taskana.common.api.WorkingTimeCalculator;
import pro.taskana.common.api.exceptions.InvalidArgumentException;
import pro.taskana.common.api.exceptions.SystemException;
public class WorkingDayCalculatorImpl implements WorkingTimeCalculator {
private final ZoneId zoneId;
private final HolidaySchedule holidaySchedule;
public WorkingDayCalculatorImpl(HolidaySchedule holidaySchedule, ZoneId zoneId) {
this.holidaySchedule = holidaySchedule;
this.zoneId = zoneId;
}
@Override
public Instant subtractWorkingTime(Instant workStart, Duration workingTime)
throws InvalidArgumentException {
long days = convertWorkingDaysToDays(workStart, -workingTime.toDays(), ZeroDirection.SUB_DAYS);
return workStart.plus(Duration.ofDays(days));
}
@Override
public Instant addWorkingTime(Instant workStart, Duration workingTime)
throws InvalidArgumentException {
long days = convertWorkingDaysToDays(workStart, workingTime.toDays(), ZeroDirection.ADD_DAYS);
return workStart.plus(Duration.ofDays(days));
}
@Override
public Duration workingTimeBetween(Instant first, Instant second)
throws InvalidArgumentException {
long days = Duration.between(first, second).abs().toDays();
Instant firstInstant = first.isBefore(second) ? first : second;
long workingDaysBetween =
LongStream.range(1, days)
.mapToObj(day -> isWorkingDay(firstInstant.plus(day, ChronoUnit.DAYS)))
.filter(t -> t)
.count();
return Duration.ofDays(workingDaysBetween);
}
@Override
public boolean isWorkingDay(Instant instant) {
return !isWeekend(instant) && !isHoliday(instant);
}
@Override
public boolean isWeekend(Instant instant) {
DayOfWeek dayOfWeek = toDayOfWeek(instant);
return dayOfWeek == DayOfWeek.SATURDAY || dayOfWeek == DayOfWeek.SUNDAY;
}
@Override
public boolean isHoliday(Instant instant) {
return holidaySchedule.isHoliday(toLocalDate(instant));
}
@Override
public boolean isGermanHoliday(Instant instant) {
return holidaySchedule.isGermanHoliday(toLocalDate(instant));
}
private long convertWorkingDaysToDays(
final Instant startTime, long numberOfDays, ZeroDirection zeroDirection) {
if (startTime == null) {
throw new SystemException(
"Internal Error: convertWorkingDaysToDays was called with a null startTime");
}
int direction = calculateDirection(numberOfDays, zeroDirection);
long limit = Math.abs(numberOfDays);
return LongStream.iterate(0, i -> i + direction)
.filter(day -> isWorkingDay(startTime.plus(day, ChronoUnit.DAYS)))
.skip(limit)
.findFirst()
.orElse(0);
}
private int calculateDirection(long numberOfDays, ZeroDirection zeroDirection) {
if (numberOfDays == 0) {
return zeroDirection.getDirection();
} else {
return numberOfDays >= 0 ? 1 : -1;
}
}
private LocalDate toLocalDate(Instant instant) {
return LocalDate.ofInstant(instant, zoneId);
}
private DayOfWeek toDayOfWeek(Instant instant) {
return toLocalDate(instant).getDayOfWeek();
}
private enum ZeroDirection {
SUB_DAYS(-1),
ADD_DAYS(1);
private final int direction;
ZeroDirection(int direction) {
this.direction = direction;
}
public int getDirection() {
return direction;
}
}
}

View File

@ -62,6 +62,7 @@ import org.junit.jupiter.params.ParameterizedTest;
import org.junit.platform.commons.support.AnnotationSupport;
import pro.taskana.TaskanaConfiguration;
import pro.taskana.common.api.TaskanaEngine;
import pro.taskana.common.api.WorkingTimeCalculator;
import pro.taskana.common.api.exceptions.ErrorCode;
import pro.taskana.common.api.exceptions.TaskanaException;
import pro.taskana.common.api.exceptions.TaskanaRuntimeException;
@ -71,7 +72,6 @@ import pro.taskana.common.internal.TaskanaEngineImpl;
import pro.taskana.common.internal.jobs.JobScheduler;
import pro.taskana.common.internal.logging.LoggingAspect;
import pro.taskana.common.internal.workingtime.HolidaySchedule;
import pro.taskana.common.internal.workingtime.WorkingTimeCalculatorImpl;
import pro.taskana.testapi.TaskanaIntegrationTest;
/**
@ -437,7 +437,7 @@ class ArchitectureTest {
.that()
.areNotAssignableFrom(ArchitectureTest.class)
.and()
.areNotAssignableTo(WorkingTimeCalculatorImpl.class)
.areNotAssignableTo(WorkingTimeCalculator.class)
.and()
.areNotAssignableTo(TaskanaEngineImpl.class)
.and()

View File

@ -251,6 +251,7 @@ class TaskanaConfigurationTest {
Map<String, List<String>> expectedClassificationCategories =
Map.of("TYPE_A", List.of("CATEGORY_A"), "TYPE_B", List.of("CATEGORY_B"));
// working time configuration
boolean expectedUseDetailedWorkingTimeCalculation = false;
Map<DayOfWeek, Set<LocalTimeInterval>> expectedWorkingTimeSchedule =
Map.of(DayOfWeek.MONDAY, Set.of(new LocalTimeInterval(LocalTime.MIN, LocalTime.NOON)));
ZoneId expectedWorkingTimeScheduleTimeZone = ZoneId.ofOffset("UTC", ZoneOffset.ofHours(4));
@ -308,6 +309,7 @@ class TaskanaConfigurationTest {
.classificationTypes(expectedClassificationTypes)
.classificationCategoriesByType(expectedClassificationCategories)
// working time configuration
.useWorkingTimeCalculation(expectedUseDetailedWorkingTimeCalculation)
.workingTimeSchedule(expectedWorkingTimeSchedule)
.workingTimeScheduleTimeZone(expectedWorkingTimeScheduleTimeZone)
.customHolidays(expectedCustomHolidays)
@ -368,6 +370,8 @@ class TaskanaConfigurationTest {
assertThat(configuration.getClassificationCategoriesByType())
.isEqualTo(expectedClassificationCategories);
// working time configuration
assertThat(configuration.isUseWorkingTimeCalculation())
.isEqualTo(expectedUseDetailedWorkingTimeCalculation);
assertThat(configuration.getWorkingTimeSchedule()).isEqualTo(expectedWorkingTimeSchedule);
assertThat(configuration.getWorkingTimeScheduleTimeZone())
.isEqualTo(expectedWorkingTimeScheduleTimeZone);
@ -442,6 +446,7 @@ class TaskanaConfigurationTest {
.classificationCategoriesByType(
Map.of("typeA", List.of("categoryA"), "typeB", List.of("categoryB")))
// working time configuration
.useWorkingTimeCalculation(false)
.workingTimeSchedule(
Map.of(
DayOfWeek.MONDAY,

View File

@ -46,6 +46,7 @@ import pro.taskana.task.api.models.Task;
import pro.taskana.task.internal.models.TaskImpl;
import pro.taskana.testapi.TaskanaInject;
import pro.taskana.testapi.TaskanaIntegrationTest;
import pro.taskana.testapi.builder.TaskBuilder;
import pro.taskana.testapi.builder.WorkbasketAccessItemBuilder;
import pro.taskana.testapi.security.WithAccessId;
import pro.taskana.workbasket.api.WorkbasketPermission;
@ -185,6 +186,50 @@ class UpdateClassificationAccTest {
@Nested
class UpdatePriorityAndServiceLevelTest {
@WithAccessId(user = "businessadmin")
@Test
void should_ChangeDueDate_When_ServiceLevelOfClassificationHasChanged() throws Exception {
Classification classification =
defaultTestClassification()
.priority(1)
.serviceLevel("P1D")
.buildAndStore(classificationService);
WorkbasketSummary workbasketSummary =
defaultTestWorkbasket().buildAndStoreAsSummary(workbasketService);
WorkbasketAccessItemBuilder.newWorkbasketAccessItem()
.workbasketId(workbasketSummary.getId())
.accessId(currentUserContext.getUserid())
.permission(WorkbasketPermission.OPEN)
.permission(WorkbasketPermission.READ)
.permission(WorkbasketPermission.READTASKS)
.permission(WorkbasketPermission.APPEND)
.buildAndStore(workbasketService, "businessadmin");
Task task = new TaskBuilder()
.classificationSummary(classification.asSummary())
.workbasketSummary(workbasketSummary)
.primaryObjRef(defaultTestObjectReference().build())
.planned(Instant.parse("2021-04-27T15:34:00.000Z"))
.due(null)
.buildAndStore(taskService);
classificationService.updateClassification(classification);
runAssociatedJobs();
// read again the task from DB
task = taskService.getTask(task.getId());
assertThat(task.getClassificationSummary().getServiceLevel()).isEqualTo("P1D");
assertThat(task.getDue()).isAfterOrEqualTo("2021-04-28T15:33:59.999Z");
classification.setServiceLevel("P3D");
classificationService.updateClassification(classification);
runAssociatedJobs();
// read again the task from DB
task = taskService.getTask(task.getId());
assertThat(task.getClassificationSummary().getServiceLevel()).isEqualTo("P3D");
assertThat(task.getDue()).isEqualTo("2021-04-30T15:33:59.999Z");
}
@WithAccessId(user = "businessadmin")
@Test
void should_NotThrowException_When_UpdatingClassificationWithEmptyServiceLevel()

View File

@ -0,0 +1,933 @@
package acceptance.classification.update;
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 static pro.taskana.testapi.DefaultTestEntities.defaultTestClassification;
import static pro.taskana.testapi.DefaultTestEntities.defaultTestObjectReference;
import static pro.taskana.testapi.DefaultTestEntities.defaultTestWorkbasket;
import static pro.taskana.testapi.builder.TaskBuilder.newTask;
import java.time.Duration;
import java.time.Instant;
import java.time.ZoneId;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
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.function.ThrowingConsumer;
import pro.taskana.TaskanaConfiguration.Builder;
import pro.taskana.classification.api.ClassificationCustomField;
import pro.taskana.classification.api.ClassificationService;
import pro.taskana.classification.api.exceptions.ClassificationNotFoundException;
import pro.taskana.classification.api.models.Classification;
import pro.taskana.classification.api.models.ClassificationSummary;
import pro.taskana.classification.internal.models.ClassificationImpl;
import pro.taskana.common.api.TaskanaEngine;
import pro.taskana.common.api.TaskanaRole;
import pro.taskana.common.api.WorkingTimeCalculator;
import pro.taskana.common.api.exceptions.ConcurrencyException;
import pro.taskana.common.api.exceptions.InvalidArgumentException;
import pro.taskana.common.api.exceptions.NotAuthorizedException;
import pro.taskana.common.api.security.CurrentUserContext;
import pro.taskana.common.internal.jobs.JobRunner;
import pro.taskana.common.internal.util.Pair;
import pro.taskana.task.api.TaskService;
import pro.taskana.task.api.models.Attachment;
import pro.taskana.task.api.models.Task;
import pro.taskana.task.internal.models.TaskImpl;
import pro.taskana.testapi.TaskanaConfigurationModifier;
import pro.taskana.testapi.TaskanaInject;
import pro.taskana.testapi.TaskanaIntegrationTest;
import pro.taskana.testapi.builder.TaskBuilder;
import pro.taskana.testapi.builder.WorkbasketAccessItemBuilder;
import pro.taskana.testapi.security.WithAccessId;
import pro.taskana.workbasket.api.WorkbasketPermission;
import pro.taskana.workbasket.api.WorkbasketService;
import pro.taskana.workbasket.api.models.WorkbasketSummary;
@TaskanaIntegrationTest
public class UpdateClassificationWithWorkingDayCalculatorAccTest
implements TaskanaConfigurationModifier {
@TaskanaInject ClassificationService classificationService;
@TaskanaInject TaskanaEngine taskanaEngine;
@TaskanaInject TaskService taskService;
@TaskanaInject WorkbasketService workbasketService;
@TaskanaInject WorkingTimeCalculator workingTimeCalculator;
@TaskanaInject CurrentUserContext currentUserContext;
@Override
public Builder modify(Builder builder) {
return builder
.workingTimeScheduleTimeZone(ZoneId.of("UTC"))
.useWorkingTimeCalculation(false); // switch to WorkingDayCalculatorImpl
}
@WithAccessId(user = "businessadmin")
@Test
void should_SetFieldsCorrectly_When_TryingToUpdateClassification() throws Exception {
Classification parentClassification =
defaultTestClassification().buildAndStore(classificationService);
Classification classification =
defaultTestClassification().type("TASK").buildAndStore(classificationService);
final Instant createdBefore = classification.getCreated();
final Instant modifiedBefore = classification.getModified();
classification.setApplicationEntryPoint("newEntrypoint");
classification.setCategory("PROCESS");
classification.setCustomField(ClassificationCustomField.CUSTOM_1, "newCustom1");
classification.setCustomField(ClassificationCustomField.CUSTOM_2, "newCustom2");
classification.setCustomField(ClassificationCustomField.CUSTOM_3, "newCustom3");
classification.setCustomField(ClassificationCustomField.CUSTOM_4, "newCustom4");
classification.setCustomField(ClassificationCustomField.CUSTOM_5, "newCustom5");
classification.setCustomField(ClassificationCustomField.CUSTOM_6, "newCustom6");
classification.setCustomField(ClassificationCustomField.CUSTOM_7, "newCustom7");
classification.setCustomField(ClassificationCustomField.CUSTOM_8, "newCustom8");
classification.setDescription("newDescription");
classification.setIsValidInDomain(false);
classification.setName("newName");
classification.setParentId(parentClassification.getId());
classification.setParentKey(parentClassification.getKey());
classification.setPriority(1000);
classification.setServiceLevel("P3D");
classificationService.updateClassification(classification);
Classification updatedClassification =
classificationService.getClassification(classification.getKey(), "DOMAIN_A");
ClassificationImpl expectedClassification =
(ClassificationImpl)
defaultTestClassification()
.type("TASK")
.applicationEntryPoint("newEntrypoint")
.category("PROCESS")
.customAttribute(ClassificationCustomField.CUSTOM_1, "newCustom1")
.customAttribute(ClassificationCustomField.CUSTOM_2, "newCustom2")
.customAttribute(ClassificationCustomField.CUSTOM_3, "newCustom3")
.customAttribute(ClassificationCustomField.CUSTOM_4, "newCustom4")
.customAttribute(ClassificationCustomField.CUSTOM_5, "newCustom5")
.customAttribute(ClassificationCustomField.CUSTOM_6, "newCustom6")
.customAttribute(ClassificationCustomField.CUSTOM_7, "newCustom7")
.customAttribute(ClassificationCustomField.CUSTOM_8, "newCustom8")
.description("newDescription")
.isValidInDomain(false)
.name("newName")
.parentId(parentClassification.getId())
.parentKey(parentClassification.getKey())
.priority(1000)
.serviceLevel("P3D")
.created(createdBefore)
.modified(updatedClassification.getModified())
.buildAndStore(classificationService);
expectedClassification.setKey(updatedClassification.getKey());
expectedClassification.setId(updatedClassification.getId());
assertThat(expectedClassification).hasNoNullFieldsOrProperties();
assertThat(modifiedBefore).isBefore(classification.getModified());
assertThat(updatedClassification).isEqualTo(expectedClassification);
}
private String createTaskWithExistingClassification(ClassificationSummary classificationSummary)
throws Exception {
WorkbasketSummary workbasketSummary =
defaultTestWorkbasket().buildAndStoreAsSummary(workbasketService);
WorkbasketAccessItemBuilder.newWorkbasketAccessItem()
.workbasketId(workbasketSummary.getId())
.accessId(currentUserContext.getUserid())
.permission(WorkbasketPermission.OPEN)
.permission(WorkbasketPermission.READ)
.permission(WorkbasketPermission.READTASKS)
.permission(WorkbasketPermission.APPEND)
.buildAndStore(workbasketService, "businessadmin");
return newTask()
.classificationSummary(classificationSummary)
.workbasketSummary(workbasketSummary)
.primaryObjRef(defaultTestObjectReference().build())
.buildAndStore(taskService)
.getId();
}
private List<String> createTasksWithExistingClassificationInAttachment(
ClassificationSummary classificationSummary, String serviceLevel, int priority, int amount)
throws Exception {
List<String> taskList = new ArrayList<>();
WorkbasketSummary workbasketSummary =
defaultTestWorkbasket().buildAndStoreAsSummary(workbasketService);
WorkbasketAccessItemBuilder.newWorkbasketAccessItem()
.workbasketId(workbasketSummary.getId())
.accessId(currentUserContext.getUserid())
.permission(WorkbasketPermission.OPEN)
.permission(WorkbasketPermission.READ)
.permission(WorkbasketPermission.READTASKS)
.permission(WorkbasketPermission.APPEND)
.buildAndStore(workbasketService, "businessadmin");
ClassificationSummary classificationSummaryWithSpecifiedServiceLevel =
defaultTestClassification()
.serviceLevel(serviceLevel)
.priority(priority)
.buildAndStoreAsSummary(classificationService);
for (int i = 0; i < amount; i++) {
Attachment attachment = taskService.newAttachment();
attachment.setClassificationSummary(classificationSummary);
attachment.setObjectReference(defaultTestObjectReference().build());
taskList.add(
newTask()
.classificationSummary(classificationSummaryWithSpecifiedServiceLevel)
.workbasketSummary(workbasketSummary)
.primaryObjRef(defaultTestObjectReference().build())
.attachments(attachment)
.buildAndStore(taskService)
.getId());
}
return taskList;
}
@TestInstance(Lifecycle.PER_CLASS)
@Nested
class UpdatePriorityAndServiceLevelTest {
@WithAccessId(user = "businessadmin")
@Test
void should_ChangeDueDate_When_ClassificationOfTaskHasChanged() throws Exception {
final Instant plannedDate = Instant.parse("2021-04-27T15:34:00.000Z");
final String expectedDue1 = plannedDate.plus(1, ChronoUnit.DAYS).toString();
final String expectedDue3 = plannedDate.plus(3, ChronoUnit.DAYS).toString();
final Classification classificationWithSL1 =
defaultTestClassification()
.priority(1)
.serviceLevel("P1D")
.buildAndStore(classificationService);
final Classification classificationWithSL3 =
defaultTestClassification()
.priority(1)
.serviceLevel("P3D")
.buildAndStore(classificationService);
WorkbasketSummary workbasketSummary =
defaultTestWorkbasket().buildAndStoreAsSummary(workbasketService);
WorkbasketAccessItemBuilder.newWorkbasketAccessItem()
.workbasketId(workbasketSummary.getId())
.accessId(currentUserContext.getUserid())
.permission(WorkbasketPermission.OPEN)
.permission(WorkbasketPermission.READ)
.permission(WorkbasketPermission.READTASKS)
.permission(WorkbasketPermission.EDITTASKS)
.permission(WorkbasketPermission.APPEND)
.buildAndStore(workbasketService, "businessadmin");
Task task =
new TaskBuilder()
.classificationSummary(classificationWithSL1.asSummary())
.workbasketSummary(workbasketSummary)
.primaryObjRef(defaultTestObjectReference().build())
.planned(plannedDate)
.due(null)
.buildAndStore(taskService);
classificationService.updateClassification(classificationWithSL1);
runAssociatedJobs();
// read again the task from DB
task = taskService.getTask(task.getId());
assertThat(task.getClassificationSummary().getServiceLevel()).isEqualTo("P1D");
assertThat(task.getDue()).isAfterOrEqualTo(expectedDue1);
task.setClassificationKey(classificationWithSL3.getKey());
taskService.updateTask(task);
// read again the task from DB
task = taskService.getTask(task.getId());
assertThat(task.getClassificationSummary().getServiceLevel()).isEqualTo("P3D");
assertThat(task.getDue()).isEqualTo(expectedDue3);
}
@WithAccessId(user = "businessadmin")
@Test
void should_ChangeDueDate_When_ServiceLevelOfClassificationHasChanged() throws Exception {
Classification classification =
defaultTestClassification()
.priority(1)
.serviceLevel("P1D")
.buildAndStore(classificationService);
WorkbasketSummary workbasketSummary =
defaultTestWorkbasket().buildAndStoreAsSummary(workbasketService);
WorkbasketAccessItemBuilder.newWorkbasketAccessItem()
.workbasketId(workbasketSummary.getId())
.accessId(currentUserContext.getUserid())
.permission(WorkbasketPermission.OPEN)
.permission(WorkbasketPermission.READ)
.permission(WorkbasketPermission.READTASKS)
.permission(WorkbasketPermission.APPEND)
.buildAndStore(workbasketService, "businessadmin");
Task task =
new TaskBuilder()
.classificationSummary(classification.asSummary())
.workbasketSummary(workbasketSummary)
.primaryObjRef(defaultTestObjectReference().build())
.planned(Instant.parse("2021-04-27T15:34:00.000Z"))
.due(null)
.buildAndStore(taskService);
classificationService.updateClassification(classification);
runAssociatedJobs();
// read again the task from DB
task = taskService.getTask(task.getId());
assertThat(task.getClassificationSummary().getServiceLevel()).isEqualTo("P1D");
assertThat(task.getDue()).isAfterOrEqualTo("2021-04-28T15:34:00.000Z");
classification.setServiceLevel("P3D");
classificationService.updateClassification(classification);
runAssociatedJobs();
// read again the task from DB
task = taskService.getTask(task.getId());
assertThat(task.getClassificationSummary().getServiceLevel()).isEqualTo("P3D");
assertThat(task.getDue()).isEqualTo("2021-04-30T15:34:00.000Z");
}
@WithAccessId(user = "businessadmin")
@Test
void should_NotThrowException_When_UpdatingClassificationWithEmptyServiceLevel()
throws Exception {
Classification classification =
defaultTestClassification().serviceLevel("P1D").buildAndStore(classificationService);
classification.setServiceLevel("");
assertThatCode(() -> classificationService.updateClassification(classification))
.doesNotThrowAnyException();
assertThat(classificationService.getClassification(classification.getId()).getServiceLevel())
.isEqualTo("P0D");
}
@WithAccessId(user = "businessadmin")
@TestFactory
Stream<DynamicTest>
should_SetDefaultServiceLevel_When_TryingToUpdateClassificationWithMissingServiceLevel()
throws Exception {
Classification classification =
defaultTestClassification().serviceLevel("P1D").buildAndStore(classificationService);
List<Pair<Classification, String>> inputList =
List.of(Pair.of(classification, null), Pair.of(classification, ""));
ThrowingConsumer<Pair<Classification, String>> test =
input -> {
input.getLeft().setServiceLevel(input.getRight());
classificationService.updateClassification(input.getLeft());
assertThat(
classificationService
.getClassification(input.getLeft().getId())
.getServiceLevel())
.isEqualTo("P0D");
};
return DynamicTest.stream(
inputList.iterator(), i -> String.format("for %s", i.getRight()), test);
}
@WithAccessId(user = "businessadmin")
@Test
void should_UpdateTaskServiceLevel_When_UpdateClassificationInTask() throws Exception {
final Instant before = Instant.now();
Classification classification =
defaultTestClassification()
.priority(1)
.serviceLevel("P13D")
.buildAndStore(classificationService);
final List<String> directLinkedTask =
List.of(createTaskWithExistingClassification(classification.asSummary()));
classification.setServiceLevel("P15D");
classificationService.updateClassification(classification);
runAssociatedJobs();
validateTaskProperties(before, directLinkedTask, taskService, workingTimeCalculator, 15, 1);
}
@WithAccessId(user = "businessadmin")
@Test
void should_UpdateTaskPriority_When_UpdateClassificationInTask() throws Exception {
final Instant before = Instant.now();
Classification classification =
defaultTestClassification()
.priority(1)
.serviceLevel("P13D")
.buildAndStore(classificationService);
final List<String> directLinkedTask =
List.of(createTaskWithExistingClassification(classification.asSummary()));
classification.setPriority(1000);
classificationService.updateClassification(classification);
runAssociatedJobs();
validateTaskProperties(
before, directLinkedTask, taskService, workingTimeCalculator, 13, 1000);
}
@WithAccessId(user = "businessadmin")
@Test
void should_UpdateTaskPriorityAndServiceLevel_When_UpdateClassificationInTask()
throws Exception {
final Instant before = Instant.now();
Classification classification =
defaultTestClassification()
.priority(1)
.serviceLevel("P13D")
.buildAndStore(classificationService);
final List<String> directLinkedTask =
List.of(createTaskWithExistingClassification(classification.asSummary()));
classification.setServiceLevel("P15D");
classification.setPriority(1000);
classificationService.updateClassification(classification);
runAssociatedJobs();
validateTaskProperties(
before, directLinkedTask, taskService, workingTimeCalculator, 15, 1000);
}
@WithAccessId(user = "businessadmin")
@TestFactory
Stream<DynamicTest> should_UpdateTaskServiceLevel_When_UpdateClassificationInAttachment() {
List<Pair<String, Integer>> inputs =
List.of(Pair.of("P5D", 2), Pair.of("P8D", 3), Pair.of("P16D", 4));
List<Pair<Integer, Integer>> outputs = List.of(Pair.of(1, 2), Pair.of(1, 3), Pair.of(1, 4));
List<Pair<Pair<String, Integer>, Pair<Integer, Integer>>> zippedTestInputList =
IntStream.range(0, inputs.size())
.mapToObj(i -> Pair.of(inputs.get(i), outputs.get(i)))
.collect(Collectors.toList());
ThrowingConsumer<Pair<Pair<String, Integer>, Pair<Integer, Integer>>> test =
input -> {
final Instant before = Instant.now();
Classification classification =
defaultTestClassification()
.priority(1)
.serviceLevel("P15D")
.buildAndStore(classificationService);
ClassificationSummary classificationSummary = classification.asSummary();
final List<String> indirectLinkedTasks =
createTasksWithExistingClassificationInAttachment(
classificationSummary,
input.getLeft().getLeft(),
input.getLeft().getRight(),
5);
classification.setServiceLevel("P1D");
classificationService.updateClassification(classification);
runAssociatedJobs();
validateTaskProperties(
before,
indirectLinkedTasks,
taskService,
workingTimeCalculator,
input.getRight().getLeft(),
input.getRight().getRight());
};
return DynamicTest.stream(
zippedTestInputList.iterator(),
i ->
String.format(
"for Task with ServiceLevel %s and Priority %s",
i.getLeft().getLeft(), i.getLeft().getRight()),
test);
}
@WithAccessId(user = "businessadmin")
@TestFactory
Stream<DynamicTest> should_NotUpdateTaskServiceLevel_When_UpdateClassificationInAttachment() {
List<Pair<String, Integer>> inputs =
List.of(Pair.of("P5D", 2), Pair.of("P8D", 3), Pair.of("P14D", 4));
List<Pair<Integer, Integer>> outputs = List.of(Pair.of(5, 2), Pair.of(8, 3), Pair.of(14, 4));
List<Pair<Pair<String, Integer>, Pair<Integer, Integer>>> zippedTestInputList =
IntStream.range(0, inputs.size())
.mapToObj(i -> Pair.of(inputs.get(i), outputs.get(i)))
.collect(Collectors.toList());
ThrowingConsumer<Pair<Pair<String, Integer>, Pair<Integer, Integer>>> test =
input -> {
final Instant before = Instant.now();
Classification classification =
defaultTestClassification()
.priority(1)
.serviceLevel("P1D")
.buildAndStore(classificationService);
ClassificationSummary classificationSummary = classification.asSummary();
final List<String> indirectLinkedTasks =
createTasksWithExistingClassificationInAttachment(
classificationSummary,
input.getLeft().getLeft(),
input.getLeft().getRight(),
5);
classification.setServiceLevel("P15D");
classificationService.updateClassification(classification);
runAssociatedJobs();
validateTaskProperties(
before,
indirectLinkedTasks,
taskService,
workingTimeCalculator,
input.getRight().getLeft(),
input.getRight().getRight());
};
return DynamicTest.stream(
zippedTestInputList.iterator(),
i ->
String.format(
"for Task with ServiceLevel %s and Priority %s",
i.getLeft().getLeft(), i.getLeft().getRight()),
test);
}
@WithAccessId(user = "businessadmin")
@TestFactory
Stream<DynamicTest> should_UpdateTaskPriority_When_UpdateClassificationInAttachment() {
List<Pair<String, Integer>> inputs =
List.of(Pair.of("P1D", 1), Pair.of("P8D", 2), Pair.of("P14D", 999));
List<Pair<Integer, Integer>> outputs =
List.of(Pair.of(1, 1000), Pair.of(8, 1000), Pair.of(14, 1000));
List<Pair<Pair<String, Integer>, Pair<Integer, Integer>>> zippedTestInputList =
IntStream.range(0, inputs.size())
.mapToObj(i -> Pair.of(inputs.get(i), outputs.get(i)))
.collect(Collectors.toList());
ThrowingConsumer<Pair<Pair<String, Integer>, Pair<Integer, Integer>>> test =
input -> {
final Instant before = Instant.now();
Classification classification =
defaultTestClassification()
.priority(1)
.serviceLevel("P13D")
.buildAndStore(classificationService);
ClassificationSummary classificationSummary = classification.asSummary();
final List<String> indirectLinkedTasks =
createTasksWithExistingClassificationInAttachment(
classificationSummary,
input.getLeft().getLeft(),
input.getLeft().getRight(),
5);
classification.setServiceLevel("P15D");
classification.setPriority(1000);
classificationService.updateClassification(classification);
runAssociatedJobs();
validateTaskProperties(
before,
indirectLinkedTasks,
taskService,
workingTimeCalculator,
input.getRight().getLeft(),
input.getRight().getRight());
};
return DynamicTest.stream(
zippedTestInputList.iterator(),
i ->
String.format(
"for Task with ServiceLevel %s and Priority %s",
i.getLeft().getLeft(), i.getLeft().getRight()),
test);
}
@WithAccessId(user = "businessadmin")
@TestFactory
Stream<DynamicTest> should_NotUpdateTaskPriority_When_UpdateClassificationInAttachment() {
List<Pair<String, Integer>> inputs =
List.of(Pair.of("P1D", 2), Pair.of("P8D", 3), Pair.of("P14D", 999));
List<Pair<Integer, Integer>> outputs =
List.of(Pair.of(1, 2), Pair.of(8, 3), Pair.of(14, 999));
List<Pair<Pair<String, Integer>, Pair<Integer, Integer>>> zippedTestInputList =
IntStream.range(0, inputs.size())
.mapToObj(i -> Pair.of(inputs.get(i), outputs.get(i)))
.collect(Collectors.toList());
ThrowingConsumer<Pair<Pair<String, Integer>, Pair<Integer, Integer>>> test =
input -> {
final Instant before = Instant.now();
Classification classification =
defaultTestClassification()
.priority(1000)
.serviceLevel("P13D")
.buildAndStore(classificationService);
ClassificationSummary classificationSummary = classification.asSummary();
final List<String> indirectLinkedTasks =
createTasksWithExistingClassificationInAttachment(
classificationSummary,
input.getLeft().getLeft(),
input.getLeft().getRight(),
5);
classification.setServiceLevel("P15D");
classification.setPriority(1);
classificationService.updateClassification(classification);
runAssociatedJobs();
validateTaskProperties(
before,
indirectLinkedTasks,
taskService,
workingTimeCalculator,
input.getRight().getLeft(),
input.getRight().getRight());
};
return DynamicTest.stream(
zippedTestInputList.iterator(),
i ->
String.format(
"for Task with ServiceLevel %s and Priority %s",
i.getLeft().getLeft(), i.getLeft().getRight()),
test);
}
@WithAccessId(user = "businessadmin")
@TestFactory
Stream<DynamicTest>
should_UpdateTaskPriorityAndServiceLevel_When_UpdateClassificationInAttachment() {
List<Pair<String, Integer>> inputs = List.of(Pair.of("P1D", 5), Pair.of("P14D", 98));
List<Pair<Integer, Integer>> outputs = List.of(Pair.of(1, 99), Pair.of(1, 99));
List<Pair<Pair<String, Integer>, Pair<Integer, Integer>>> zippedTestInputList =
IntStream.range(0, inputs.size())
.mapToObj(i -> Pair.of(inputs.get(i), outputs.get(i)))
.collect(Collectors.toList());
ThrowingConsumer<Pair<Pair<String, Integer>, Pair<Integer, Integer>>> test =
input -> {
final Instant before = Instant.now();
Classification classification =
defaultTestClassification()
.priority(1)
.serviceLevel("P13D")
.buildAndStore(classificationService);
ClassificationSummary classificationSummary = classification.asSummary();
final List<String> indirectLinkedTasks =
createTasksWithExistingClassificationInAttachment(
classificationSummary,
input.getLeft().getLeft(),
input.getLeft().getRight(),
3);
classification.setServiceLevel("P1D");
classification.setPriority(99);
classificationService.updateClassification(classification);
runAssociatedJobs();
validateTaskProperties(
before,
indirectLinkedTasks,
taskService,
workingTimeCalculator,
input.getRight().getLeft(),
input.getRight().getRight());
};
return DynamicTest.stream(
zippedTestInputList.iterator(),
i ->
String.format(
"for Task with ServiceLevel %s and Priority %s",
i.getLeft().getLeft(), i.getLeft().getRight()),
test);
}
@WithAccessId(user = "businessadmin")
@TestFactory
Stream<DynamicTest>
should_NotUpdateTaskPriorityAndServiceLevel_When_UpdateClassificationInAttachment() {
List<Pair<String, Integer>> inputs = List.of(Pair.of("P1D", 5), Pair.of("P14D", 98));
List<Pair<Integer, Integer>> outputs = List.of(Pair.of(1, 5), Pair.of(14, 98));
List<Pair<Pair<String, Integer>, Pair<Integer, Integer>>> zippedTestInputList =
IntStream.range(0, inputs.size())
.mapToObj(i -> Pair.of(inputs.get(i), outputs.get(i)))
.collect(Collectors.toList());
ThrowingConsumer<Pair<Pair<String, Integer>, Pair<Integer, Integer>>> test =
input -> {
final Instant before = Instant.now();
Classification classification =
defaultTestClassification()
.priority(1000)
.serviceLevel("P1D")
.buildAndStore(classificationService);
ClassificationSummary classificationSummary = classification.asSummary();
final List<String> indirectLinkedTasks =
createTasksWithExistingClassificationInAttachment(
classificationSummary,
input.getLeft().getLeft(),
input.getLeft().getRight(),
3);
classification.setServiceLevel("P15D");
classification.setPriority(1);
classificationService.updateClassification(classification);
runAssociatedJobs();
validateTaskProperties(
before,
indirectLinkedTasks,
taskService,
workingTimeCalculator,
input.getRight().getLeft(),
input.getRight().getRight());
};
return DynamicTest.stream(
zippedTestInputList.iterator(),
i ->
String.format(
"for Task with ServiceLevel %s and Priority %s",
i.getLeft().getLeft(), i.getLeft().getRight()),
test);
}
private void runAssociatedJobs() throws Exception {
Thread.sleep(10);
// run the ClassificationChangedJob
JobRunner runner = new JobRunner(taskanaEngine);
// run the TaskRefreshJob that was scheduled by the ClassificationChangedJob.
runner.runJobs();
Thread.sleep(
10); // otherwise the next runJobs call intermittently doesn't find the Job created
// by the previous step (it searches with DueDate < CurrentTime)
runner.runJobs();
}
private void validateTaskProperties(
Instant before,
List<String> tasksUpdated,
TaskService taskService,
WorkingTimeCalculator workingTimeCalculator,
int serviceLevel,
int priority)
throws Exception {
for (String taskId : tasksUpdated) {
Task task = taskService.getTask(taskId);
Instant expDue =
workingTimeCalculator.addWorkingTime(task.getPlanned(), Duration.ofDays(serviceLevel));
assertThat(task.getModified())
.describedAs("Task " + task.getId() + " has not been refreshed.")
.isAfter(before);
assertThat(task.getDue()).isEqualTo(expDue);
assertThat(task.getPriority()).isEqualTo(priority);
}
}
}
@TestInstance(Lifecycle.PER_CLASS)
@Nested
class UpdateClassificationExceptionTest {
/**
* This BeforeAll method is needed for this {@linkplain
* #should_ThrowException_When_UserIsNotAuthorized test} and {@linkplain
* #should_ThrowException_When_UserRoleIsNotAdminOrBusinessAdmin test} since it can't create an
* own classification.
*
* @throws Exception for errors in the building or reading process of entities.
*/
@WithAccessId(user = "businessadmin")
@BeforeAll
void createClassifications() throws Exception {
defaultTestClassification()
.key("BeforeAllClassification")
.buildAndStore(classificationService);
}
@Test
void should_ThrowException_When_UserIsNotAuthorized() throws Exception {
Classification classification =
classificationService.getClassification("BeforeAllClassification", "DOMAIN_A");
classification.setCustomField(ClassificationCustomField.CUSTOM_1, "newCustom1");
NotAuthorizedException expectedException =
new NotAuthorizedException(
currentUserContext.getUserid(), TaskanaRole.BUSINESS_ADMIN, TaskanaRole.ADMIN);
assertThatThrownBy(() -> classificationService.updateClassification(classification))
.usingRecursiveComparison()
.isEqualTo(expectedException);
}
@WithAccessId(user = "taskadmin")
@WithAccessId(user = "user-1-1")
@TestTemplate
void should_ThrowException_When_UserRoleIsNotAdminOrBusinessAdmin() throws Exception {
Classification classification =
classificationService.getClassification("BeforeAllClassification", "DOMAIN_A");
classification.setApplicationEntryPoint("updated EntryPoint");
classification.setName("updated Name");
NotAuthorizedException expectedException =
new NotAuthorizedException(
currentUserContext.getUserid(), TaskanaRole.BUSINESS_ADMIN, TaskanaRole.ADMIN);
assertThatThrownBy(() -> classificationService.updateClassification(classification))
.usingRecursiveComparison()
.isEqualTo(expectedException);
}
@WithAccessId(user = "businessadmin")
@Test
void should_ThrowException_When_UpdatingClassificationConcurrently() throws Exception {
Classification classification =
defaultTestClassification().buildAndStore(classificationService);
final Classification classificationSecondUpdate =
classificationService.getClassification(
classification.getKey(), classification.getDomain());
classification.setApplicationEntryPoint("Application Entry Point");
classification.setDescription("Description");
classification.setName("Name");
Thread.sleep(20); // to avoid identity of modified timestamps between classification and base
classificationService.updateClassification(classification);
classificationSecondUpdate.setName("Name again");
classificationSecondUpdate.setDescription("Description again");
ConcurrencyException expectedException =
new ConcurrencyException(classificationSecondUpdate.getId());
assertThatThrownBy(
() -> classificationService.updateClassification(classificationSecondUpdate))
.usingRecursiveComparison()
.isEqualTo(expectedException);
}
@WithAccessId(user = "businessadmin")
@Test
void should_ThrowException_When_TryingToUpdateClassificationWithInvalidParentId()
throws Exception {
Classification classification =
defaultTestClassification().buildAndStore(classificationService);
classification.setParentId("NON EXISTING ID");
ClassificationNotFoundException expectedException =
new ClassificationNotFoundException("NON EXISTING ID");
assertThatThrownBy(() -> classificationService.updateClassification(classification))
.usingRecursiveComparison()
.isEqualTo(expectedException);
}
@WithAccessId(user = "businessadmin")
@Test
void should_ThrowException_When_TryingToUpdateClassificationWithInvalidParentKey()
throws Exception {
Classification classification =
defaultTestClassification().buildAndStore(classificationService);
classification.setParentKey("NON EXISTING KEY");
ClassificationNotFoundException expectedException =
new ClassificationNotFoundException("NON EXISTING KEY", "DOMAIN_A");
assertThatThrownBy(() -> classificationService.updateClassification(classification))
.usingRecursiveComparison()
.isEqualTo(expectedException);
}
@WithAccessId(user = "businessadmin")
@Test
void should_ThrowException_When_TryingToUpdateClassificationWithOwnKeyAsParentKey()
throws Exception {
Classification classification =
defaultTestClassification().buildAndStore(classificationService);
classification.setParentKey(classification.getKey());
InvalidArgumentException expectedException =
new InvalidArgumentException(
String.format(
"The Classification '%s' has the same key and parent key",
classification.getName()));
assertThatThrownBy(() -> classificationService.updateClassification(classification))
.usingRecursiveComparison()
.isEqualTo(expectedException);
}
}
@TestInstance(Lifecycle.PER_CLASS)
@Nested
class UpdateClassificationCategoryTest {
Classification classification;
Task task;
Instant createdBefore;
Instant modifiedBefore;
@WithAccessId(user = "businessadmin")
@BeforeEach
void createClassificationAndTask() throws Exception {
classification =
defaultTestClassification()
.category("MANUAL")
.type("TASK")
.buildAndStore(classificationService);
createdBefore = classification.getCreated();
modifiedBefore = classification.getModified();
String taskId = createTaskWithExistingClassification(classification.asSummary());
task = taskService.getTask(taskId);
}
@WithAccessId(user = "businessadmin")
@Test
void should_UpdateTask_When_UpdatingClassificationCategory() throws Exception {
classification.setCategory("PROCESS");
classificationService.updateClassification(classification);
final Task updatedTask = taskService.getTask(task.getId());
TaskImpl expectedUpdatedTask = (TaskImpl) task.copy();
expectedUpdatedTask.setId(task.getId());
expectedUpdatedTask.setClassificationCategory("PROCESS");
expectedUpdatedTask.setClassificationSummary(
classificationService.getClassification(classification.getId()).asSummary());
expectedUpdatedTask.setExternalId(task.getExternalId());
assertThat(expectedUpdatedTask)
.usingRecursiveComparison()
.ignoringFields("modified")
.isEqualTo(updatedTask);
assertThat(expectedUpdatedTask.getModified()).isAfterOrEqualTo(modifiedBefore);
}
@WithAccessId(user = "businessadmin")
@Test
void should_UpdateClassification_When_UpdatingClassificationCategory() throws Exception {
classification.setCategory("PROCESS");
classificationService.updateClassification(classification);
Classification updatedClassification =
classificationService.getClassification(classification.getId());
assertThat(updatedClassification)
.usingRecursiveComparison()
.ignoringFields("modified")
.isEqualTo(classification);
assertThat(updatedClassification.getModified()).isAfterOrEqualTo(modifiedBefore);
}
}
}

View File

@ -5,18 +5,26 @@ import static pro.taskana.testapi.DefaultTestEntities.defaultTestClassification;
import static pro.taskana.testapi.DefaultTestEntities.defaultTestObjectReference;
import static pro.taskana.testapi.DefaultTestEntities.defaultTestWorkbasket;
import java.time.Duration;
import java.time.Instant;
import java.util.List;
import org.junit.jupiter.api.BeforeAll;
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.TaskanaConfiguration.Builder;
import pro.taskana.classification.api.ClassificationService;
import pro.taskana.classification.api.models.ClassificationSummary;
import pro.taskana.common.api.BulkOperationResults;
import pro.taskana.common.api.TaskanaEngine;
import pro.taskana.common.api.WorkingTimeCalculator;
import pro.taskana.common.api.exceptions.TaskanaException;
import pro.taskana.task.api.TaskService;
import pro.taskana.task.api.models.Attachment;
import pro.taskana.task.api.models.ObjectReference;
import pro.taskana.task.api.models.TaskSummary;
import pro.taskana.testapi.TaskanaConfigurationModifier;
import pro.taskana.testapi.TaskanaInject;
import pro.taskana.testapi.TaskanaIntegrationTest;
import pro.taskana.testapi.builder.TaskAttachmentBuilder;
@ -37,172 +45,364 @@ class ServiceLevelOfAllTasksAccTest {
private static final String SMALL_CLASSIFICATION_SERVICE_LEVEL = "P2D";
private static final String GREAT_CLASSIFICATION_SERVICE_LEVEL = "P7D";
@TaskanaInject TaskService taskService;
@TaskanaInject WorkbasketService workbasketService;
@TaskanaInject ClassificationService classificationService;
@Nested
@TestInstance(Lifecycle.PER_CLASS)
class WithWorkingTimeCalculation {
@TaskanaInject TaskService taskService;
@TaskanaInject WorkbasketService workbasketService;
@TaskanaInject ClassificationService classificationService;
ClassificationSummary classificationSummarySmallServiceLevel;
ClassificationSummary classificationSummaryGreatServiceLevel;
Attachment attachmentSummarySmallServiceLevel;
Attachment attachmentSummaryGreatServiceLevel;
WorkbasketSummary defaultWorkbasketSummary;
ObjectReference defaultObjectReference;
ClassificationSummary classificationSummarySmallServiceLevel;
ClassificationSummary classificationSummaryGreatServiceLevel;
Attachment attachmentSummarySmallServiceLevel;
Attachment attachmentSummaryGreatServiceLevel;
WorkbasketSummary defaultWorkbasketSummary;
ObjectReference defaultObjectReference;
@WithAccessId(user = "businessadmin")
@BeforeAll
void setup() throws Exception {
classificationSummarySmallServiceLevel =
defaultTestClassification()
.serviceLevel(SMALL_CLASSIFICATION_SERVICE_LEVEL)
.buildAndStoreAsSummary(classificationService);
classificationSummaryGreatServiceLevel =
defaultTestClassification()
.serviceLevel(GREAT_CLASSIFICATION_SERVICE_LEVEL)
.buildAndStoreAsSummary(classificationService);
@WithAccessId(user = "businessadmin")
@BeforeAll
void setup() throws Exception {
classificationSummarySmallServiceLevel =
defaultTestClassification()
.serviceLevel(SMALL_CLASSIFICATION_SERVICE_LEVEL)
.buildAndStoreAsSummary(classificationService);
classificationSummaryGreatServiceLevel =
defaultTestClassification()
.serviceLevel(GREAT_CLASSIFICATION_SERVICE_LEVEL)
.buildAndStoreAsSummary(classificationService);
defaultObjectReference = defaultTestObjectReference().build();
defaultObjectReference = defaultTestObjectReference().build();
attachmentSummarySmallServiceLevel =
TaskAttachmentBuilder.newAttachment()
.classificationSummary(classificationSummarySmallServiceLevel)
.objectReference(defaultObjectReference)
.build();
attachmentSummaryGreatServiceLevel =
TaskAttachmentBuilder.newAttachment()
.classificationSummary(classificationSummaryGreatServiceLevel)
.objectReference(defaultObjectReference)
.build();
attachmentSummarySmallServiceLevel =
TaskAttachmentBuilder.newAttachment()
.classificationSummary(classificationSummarySmallServiceLevel)
.objectReference(defaultObjectReference)
.build();
attachmentSummaryGreatServiceLevel =
TaskAttachmentBuilder.newAttachment()
.classificationSummary(classificationSummaryGreatServiceLevel)
.objectReference(defaultObjectReference)
.build();
defaultWorkbasketSummary = defaultTestWorkbasket().buildAndStoreAsSummary(workbasketService);
WorkbasketAccessItemBuilder.newWorkbasketAccessItem()
.workbasketId(defaultWorkbasketSummary.getId())
.accessId("user-1-1")
.permission(WorkbasketPermission.OPEN)
.permission(WorkbasketPermission.READ)
.permission(WorkbasketPermission.READTASKS)
.permission(WorkbasketPermission.APPEND)
.buildAndStore(workbasketService);
defaultWorkbasketSummary = defaultTestWorkbasket().buildAndStoreAsSummary(workbasketService);
WorkbasketAccessItemBuilder.newWorkbasketAccessItem()
.workbasketId(defaultWorkbasketSummary.getId())
.accessId("user-1-1")
.permission(WorkbasketPermission.OPEN)
.permission(WorkbasketPermission.READ)
.permission(WorkbasketPermission.READTASKS)
.permission(WorkbasketPermission.APPEND)
.buildAndStore(workbasketService);
}
@WithAccessId(user = "user-1-1")
@Test
void should_SetPlannedOnMultipleTasks() throws Exception {
Instant planned = Instant.parse("2020-05-03T07:00:00.000Z");
TaskSummary task1 =
createDefaultTask()
.classificationSummary(classificationSummarySmallServiceLevel)
.buildAndStoreAsSummary(taskService);
TaskSummary task2 =
createDefaultTask()
.classificationSummary(classificationSummarySmallServiceLevel)
.attachments(attachmentSummaryGreatServiceLevel.copy())
.buildAndStoreAsSummary(taskService);
TaskSummary task3 =
createDefaultTask()
.classificationSummary(classificationSummaryGreatServiceLevel)
.attachments(attachmentSummarySmallServiceLevel.copy())
.buildAndStoreAsSummary(taskService);
List<String> taskIds = List.of(task1.getId(), task2.getId(), task3.getId());
BulkOperationResults<String, TaskanaException> bulkLog =
taskService.setPlannedPropertyOfTasks(planned, taskIds);
assertThat(bulkLog.containsErrors()).isFalse();
List<TaskSummary> result =
taskService.createTaskQuery().idIn(task1.getId(), task2.getId(), task3.getId()).list();
assertThat(result).extracting(TaskSummary::getPlanned).containsOnly(planned);
}
@WithAccessId(user = "user-1-1")
@Test
void should_ChangeDue_When_SettingPlannedAndClassificationHasSmallerServiceLevel()
throws Exception {
Instant planned = Instant.parse("2020-05-04T07:00:00.000Z");
TaskSummary task1 =
createDefaultTask()
.classificationSummary(classificationSummarySmallServiceLevel)
.attachments(attachmentSummaryGreatServiceLevel.copy())
.buildAndStoreAsSummary(taskService);
TaskSummary task2 =
createDefaultTask()
.classificationSummary(classificationSummarySmallServiceLevel)
.buildAndStoreAsSummary(taskService);
List<String> taskIds = List.of(task1.getId(), task2.getId());
BulkOperationResults<String, TaskanaException> bulkLog =
taskService.setPlannedPropertyOfTasks(planned, taskIds);
assertThat(bulkLog.containsErrors()).isFalse();
List<TaskSummary> result =
taskService.createTaskQuery().idIn(task1.getId(), task2.getId()).list();
Instant expectedDue = Instant.parse("2020-05-06T06:59:59.999Z");
assertThat(result).extracting(TaskSummary::getDue).containsOnly(expectedDue);
}
@WithAccessId(user = "user-1-1")
@Test
void should_ChangeDue_When_SettingPlannedAndAttachmentHasSmallerOrEqualServiceLevel()
throws Exception {
Instant planned = Instant.parse("2020-05-04T07:00:00.000Z");
TaskSummary task1 =
createDefaultTask()
.classificationSummary(classificationSummaryGreatServiceLevel)
.attachments(attachmentSummarySmallServiceLevel.copy())
.buildAndStoreAsSummary(taskService);
TaskSummary task2 =
createDefaultTask()
.classificationSummary(classificationSummarySmallServiceLevel)
.attachments(attachmentSummarySmallServiceLevel.copy())
.buildAndStoreAsSummary(taskService);
List<String> taskIds = List.of(task1.getId(), task2.getId());
BulkOperationResults<String, TaskanaException> bulkLog =
taskService.setPlannedPropertyOfTasks(planned, taskIds);
assertThat(bulkLog.containsErrors()).isFalse();
List<TaskSummary> result =
taskService.createTaskQuery().idIn(task1.getId(), task2.getId()).list();
Instant expectedDue = Instant.parse("2020-05-06T06:59:59.999Z");
assertThat(result).extracting(TaskSummary::getDue).containsOnly(expectedDue);
}
@WithAccessId(user = "user-1-1")
@Test
void should_ChangeDue_When_SettingPlannedAndUsingDifferentServiceLevels() throws Exception {
Instant planned = Instant.parse("2020-05-04T07:00:00.000Z");
TaskSummary task1 =
createDefaultTask()
.classificationSummary(classificationSummaryGreatServiceLevel)
.attachments(attachmentSummarySmallServiceLevel.copy())
.buildAndStoreAsSummary(taskService);
TaskSummary task2 =
createDefaultTask()
.classificationSummary(classificationSummarySmallServiceLevel)
.attachments(attachmentSummaryGreatServiceLevel.copy())
.buildAndStoreAsSummary(taskService);
TaskSummary task3 =
createDefaultTask()
.classificationSummary(classificationSummaryGreatServiceLevel)
.attachments(attachmentSummaryGreatServiceLevel.copy())
.buildAndStoreAsSummary(taskService);
List<String> taskIds = List.of(task1.getId(), task2.getId(), task3.getId());
BulkOperationResults<String, TaskanaException> bulkLog =
taskService.setPlannedPropertyOfTasks(planned, taskIds);
assertThat(bulkLog.containsErrors()).isFalse();
List<TaskSummary> result =
taskService.createTaskQuery().idIn(task1.getId(), task2.getId(), task3.getId()).list();
Instant expectedDueSmallServiceLevel = Instant.parse("2020-05-06T06:59:59.999Z");
Instant expectedDueGreatServiceLevel = Instant.parse("2020-05-13T06:59:59.999Z");
assertThat(result)
.extracting(TaskSummary::getDue)
.containsOnly(expectedDueSmallServiceLevel, expectedDueGreatServiceLevel);
}
private TaskBuilder createDefaultTask() {
return (TaskBuilder.newTask()
.workbasketSummary(defaultWorkbasketSummary)
.primaryObjRef(defaultObjectReference));
}
}
@WithAccessId(user = "user-1-1")
@Test
void should_SetPlannedOnMultipleTasks() throws Exception {
Instant planned = Instant.parse("2020-05-03T07:00:00.000Z");
TaskSummary task1 =
createDefaultTask()
.classificationSummary(classificationSummarySmallServiceLevel)
.buildAndStoreAsSummary(taskService);
TaskSummary task2 =
createDefaultTask()
.classificationSummary(classificationSummarySmallServiceLevel)
.attachments(attachmentSummaryGreatServiceLevel.copy())
.buildAndStoreAsSummary(taskService);
TaskSummary task3 =
createDefaultTask()
.classificationSummary(classificationSummaryGreatServiceLevel)
.attachments(attachmentSummarySmallServiceLevel.copy())
.buildAndStoreAsSummary(taskService);
List<String> taskIds = List.of(task1.getId(), task2.getId(), task3.getId());
@Nested
@TestInstance(Lifecycle.PER_CLASS)
class WithWorkingDaysCalculation implements TaskanaConfigurationModifier {
BulkOperationResults<String, TaskanaException> bulkLog =
taskService.setPlannedPropertyOfTasks(planned, taskIds);
@TaskanaInject TaskanaEngine taskanaEngine;
@TaskanaInject TaskService taskService;
@TaskanaInject WorkbasketService workbasketService;
@TaskanaInject ClassificationService classificationService;
ClassificationSummary classificationSummarySmallServiceLevel;
ClassificationSummary classificationSummaryGreatServiceLevel;
Attachment attachmentSummarySmallServiceLevel;
Attachment attachmentSummaryGreatServiceLevel;
WorkbasketSummary defaultWorkbasketSummary;
ObjectReference defaultObjectReference;
WorkingTimeCalculator converter;
assertThat(bulkLog.containsErrors()).isFalse();
List<TaskSummary> result =
taskService.createTaskQuery().idIn(task1.getId(), task2.getId(), task3.getId()).list();
assertThat(result).extracting(TaskSummary::getPlanned).containsOnly(planned);
}
@Override
public Builder modify(Builder builder) {
return builder.useWorkingTimeCalculation(false);
}
@WithAccessId(user = "user-1-1")
@Test
void should_ChangeDue_When_SettingPlannedAndClassificationHasSmallerServiceLevel()
throws Exception {
Instant planned = Instant.parse("2020-05-04T07:00:00.000Z");
TaskSummary task1 =
createDefaultTask()
.classificationSummary(classificationSummarySmallServiceLevel)
.attachments(attachmentSummaryGreatServiceLevel.copy())
.buildAndStoreAsSummary(taskService);
TaskSummary task2 =
createDefaultTask()
.classificationSummary(classificationSummarySmallServiceLevel)
.buildAndStoreAsSummary(taskService);
List<String> taskIds = List.of(task1.getId(), task2.getId());
@WithAccessId(user = "businessadmin")
@BeforeAll
void setup() throws Exception {
classificationSummarySmallServiceLevel =
defaultTestClassification()
.serviceLevel(SMALL_CLASSIFICATION_SERVICE_LEVEL)
.buildAndStoreAsSummary(classificationService);
classificationSummaryGreatServiceLevel =
defaultTestClassification()
.serviceLevel(GREAT_CLASSIFICATION_SERVICE_LEVEL)
.buildAndStoreAsSummary(classificationService);
BulkOperationResults<String, TaskanaException> bulkLog =
taskService.setPlannedPropertyOfTasks(planned, taskIds);
defaultObjectReference = defaultTestObjectReference().build();
assertThat(bulkLog.containsErrors()).isFalse();
List<TaskSummary> result =
taskService.createTaskQuery().idIn(task1.getId(), task2.getId()).list();
Instant expectedDue = Instant.parse("2020-05-06T06:59:59.999Z");
assertThat(result).extracting(TaskSummary::getDue).containsOnly(expectedDue);
}
attachmentSummarySmallServiceLevel =
TaskAttachmentBuilder.newAttachment()
.classificationSummary(classificationSummarySmallServiceLevel)
.objectReference(defaultObjectReference)
.build();
attachmentSummaryGreatServiceLevel =
TaskAttachmentBuilder.newAttachment()
.classificationSummary(classificationSummaryGreatServiceLevel)
.objectReference(defaultObjectReference)
.build();
@WithAccessId(user = "user-1-1")
@Test
void should_ChangeDue_When_SettingPlannedAndAttachmentHasSmallerOrEqualServiceLevel()
throws Exception {
Instant planned = Instant.parse("2020-05-04T07:00:00.000Z");
TaskSummary task1 =
createDefaultTask()
.classificationSummary(classificationSummaryGreatServiceLevel)
.attachments(attachmentSummarySmallServiceLevel.copy())
.buildAndStoreAsSummary(taskService);
TaskSummary task2 =
createDefaultTask()
.classificationSummary(classificationSummarySmallServiceLevel)
.attachments(attachmentSummarySmallServiceLevel.copy())
.buildAndStoreAsSummary(taskService);
List<String> taskIds = List.of(task1.getId(), task2.getId());
defaultWorkbasketSummary = defaultTestWorkbasket().buildAndStoreAsSummary(workbasketService);
WorkbasketAccessItemBuilder.newWorkbasketAccessItem()
.workbasketId(defaultWorkbasketSummary.getId())
.accessId("user-1-1")
.permission(WorkbasketPermission.OPEN)
.permission(WorkbasketPermission.READ)
.permission(WorkbasketPermission.READTASKS)
.permission(WorkbasketPermission.APPEND)
.buildAndStore(workbasketService);
converter = taskanaEngine.getWorkingTimeCalculator();
}
BulkOperationResults<String, TaskanaException> bulkLog =
taskService.setPlannedPropertyOfTasks(planned, taskIds);
@WithAccessId(user = "user-1-1")
@Test
void should_SetPlannedOnMultipleTasks() throws Exception {
Instant planned = Instant.parse("2020-05-03T07:00:00.000Z");
TaskSummary task1 =
createDefaultTask()
.classificationSummary(classificationSummarySmallServiceLevel)
.buildAndStoreAsSummary(taskService);
TaskSummary task2 =
createDefaultTask()
.classificationSummary(classificationSummarySmallServiceLevel)
.attachments(attachmentSummaryGreatServiceLevel.copy())
.buildAndStoreAsSummary(taskService);
TaskSummary task3 =
createDefaultTask()
.classificationSummary(classificationSummaryGreatServiceLevel)
.attachments(attachmentSummarySmallServiceLevel.copy())
.buildAndStoreAsSummary(taskService);
List<String> taskIds = List.of(task1.getId(), task2.getId(), task3.getId());
assertThat(bulkLog.containsErrors()).isFalse();
List<TaskSummary> result =
taskService.createTaskQuery().idIn(task1.getId(), task2.getId()).list();
Instant expectedDue = Instant.parse("2020-05-06T06:59:59.999Z");
assertThat(result).extracting(TaskSummary::getDue).containsOnly(expectedDue);
}
BulkOperationResults<String, TaskanaException> bulkLog =
taskService.setPlannedPropertyOfTasks(planned, taskIds);
@WithAccessId(user = "user-1-1")
@Test
void should_ChangeDue_When_SettingPlannedAndUsingDifferentServiceLevels() throws Exception {
Instant planned = Instant.parse("2020-05-04T07:00:00.000Z");
TaskSummary task1 =
createDefaultTask()
.classificationSummary(classificationSummaryGreatServiceLevel)
.attachments(attachmentSummarySmallServiceLevel.copy())
.buildAndStoreAsSummary(taskService);
TaskSummary task2 =
createDefaultTask()
.classificationSummary(classificationSummarySmallServiceLevel)
.attachments(attachmentSummaryGreatServiceLevel.copy())
.buildAndStoreAsSummary(taskService);
TaskSummary task3 =
createDefaultTask()
.classificationSummary(classificationSummaryGreatServiceLevel)
.attachments(attachmentSummaryGreatServiceLevel.copy())
.buildAndStoreAsSummary(taskService);
List<String> taskIds = List.of(task1.getId(), task2.getId(), task3.getId());
assertThat(bulkLog.containsErrors()).isFalse();
List<TaskSummary> result =
taskService.createTaskQuery().idIn(task1.getId(), task2.getId(), task3.getId()).list();
assertThat(result).extracting(TaskSummary::getPlanned).containsOnly(planned);
}
BulkOperationResults<String, TaskanaException> bulkLog =
taskService.setPlannedPropertyOfTasks(planned, taskIds);
@WithAccessId(user = "user-1-1")
@Test
void should_ChangeDue_When_SettingPlannedAndClassificationHasSmallerServiceLevel()
throws Exception {
Instant planned = Instant.parse("2020-05-03T07:00:00.000Z");
TaskSummary task1 =
createDefaultTask()
.classificationSummary(classificationSummarySmallServiceLevel)
.attachments(attachmentSummaryGreatServiceLevel.copy())
.buildAndStoreAsSummary(taskService);
TaskSummary task2 =
createDefaultTask()
.classificationSummary(classificationSummarySmallServiceLevel)
.buildAndStoreAsSummary(taskService);
List<String> taskIds = List.of(task1.getId(), task2.getId());
assertThat(bulkLog.containsErrors()).isFalse();
List<TaskSummary> result =
taskService.createTaskQuery().idIn(task1.getId(), task2.getId(), task3.getId()).list();
Instant expectedDueSmallServiceLevel = Instant.parse("2020-05-06T06:59:59.999Z");
Instant expectedDueGreatServiceLevel = Instant.parse("2020-05-13T06:59:59.999Z");
assertThat(result)
.extracting(TaskSummary::getDue)
.containsOnly(expectedDueSmallServiceLevel, expectedDueGreatServiceLevel);
}
BulkOperationResults<String, TaskanaException> bulkLog =
taskService.setPlannedPropertyOfTasks(planned, taskIds);
private TaskBuilder createDefaultTask() {
return (TaskBuilder.newTask()
.workbasketSummary(defaultWorkbasketSummary)
.primaryObjRef(defaultObjectReference));
assertThat(bulkLog.containsErrors()).isFalse();
List<TaskSummary> result =
taskService.createTaskQuery().idIn(task1.getId(), task2.getId()).list();
assertThat(result)
.extracting(TaskSummary::getDue)
.containsOnly(
converter.addWorkingTime(
planned, Duration.parse(SMALL_CLASSIFICATION_SERVICE_LEVEL)));
}
@WithAccessId(user = "user-1-1")
@Test
void should_ChangeDue_When_SettingPlannedAndAttachmentHasSmallerOrEqualServiceLevel()
throws Exception {
Instant planned = Instant.parse("2020-05-03T07:00:00.000Z");
TaskSummary task1 =
createDefaultTask()
.classificationSummary(classificationSummaryGreatServiceLevel)
.attachments(attachmentSummarySmallServiceLevel.copy())
.buildAndStoreAsSummary(taskService);
TaskSummary task2 =
createDefaultTask()
.classificationSummary(classificationSummarySmallServiceLevel)
.attachments(attachmentSummarySmallServiceLevel.copy())
.buildAndStoreAsSummary(taskService);
List<String> taskIds = List.of(task1.getId(), task2.getId());
BulkOperationResults<String, TaskanaException> bulkLog =
taskService.setPlannedPropertyOfTasks(planned, taskIds);
assertThat(bulkLog.containsErrors()).isFalse();
List<TaskSummary> result =
taskService.createTaskQuery().idIn(task1.getId(), task2.getId()).list();
assertThat(result)
.extracting(TaskSummary::getDue)
.containsOnly(
converter.addWorkingTime(
planned, Duration.parse(SMALL_CLASSIFICATION_SERVICE_LEVEL)));
}
@WithAccessId(user = "user-1-1")
@Test
void should_ChangeDue_When_SettingPlannedAndUsingDifferentServiceLevels() throws Exception {
Instant planned = Instant.parse("2020-05-03T07:00:00.000Z");
TaskSummary task1 =
createDefaultTask()
.classificationSummary(classificationSummaryGreatServiceLevel)
.attachments(attachmentSummarySmallServiceLevel.copy())
.buildAndStoreAsSummary(taskService);
TaskSummary task2 =
createDefaultTask()
.classificationSummary(classificationSummarySmallServiceLevel)
.attachments(attachmentSummaryGreatServiceLevel.copy())
.buildAndStoreAsSummary(taskService);
TaskSummary task3 =
createDefaultTask()
.classificationSummary(classificationSummaryGreatServiceLevel)
.attachments(attachmentSummaryGreatServiceLevel.copy())
.buildAndStoreAsSummary(taskService);
List<String> taskIds = List.of(task1.getId(), task2.getId(), task3.getId());
BulkOperationResults<String, TaskanaException> bulkLog =
taskService.setPlannedPropertyOfTasks(planned, taskIds);
assertThat(bulkLog.containsErrors()).isFalse();
List<TaskSummary> result =
taskService.createTaskQuery().idIn(task1.getId(), task2.getId(), task3.getId()).list();
assertThat(result)
.extracting(TaskSummary::getDue)
.containsOnly(
converter.addWorkingTime(planned, Duration.parse(SMALL_CLASSIFICATION_SERVICE_LEVEL)),
converter.addWorkingTime(
planned, Duration.parse(GREAT_CLASSIFICATION_SERVICE_LEVEL)));
}
private TaskBuilder createDefaultTask() {
return (TaskBuilder.newTask()
.workbasketSummary(defaultWorkbasketSummary)
.primaryObjRef(defaultObjectReference));
}
}
}

View File

@ -13,6 +13,7 @@ taskana.classification.types=TASK | document
taskana.classification.categories.task=EXTERNAL| manual| autoMAtic| Process
taskana.classification.categories.document=EXTERNAL
# working time configuration
taskana.workingTime.useWorkingTimeCalculation=false
taskana.workingTime.schedule.MONDAY=09:00-18:00
taskana.workingTime.schedule.TUESDAY=09:00-18:00
taskana.workingTime.schedule.WEDNESDAY=09:00-18:00

View File

@ -73,6 +73,7 @@ public class TaskanaConfiguration {
// endregion
// region working time configuration
private final boolean useWorkingTimeCalculation;
private final Map<DayOfWeek, Set<LocalTimeInterval>> workingTimeSchedule;
private final ZoneId workingTimeScheduleTimeZone;
private final Set<CustomHoliday> customHolidays;
@ -153,6 +154,7 @@ public class TaskanaConfiguration {
Collectors.toUnmodifiableMap(
Entry::getKey, e -> Collections.unmodifiableList(e.getValue())));
// working time configuration
this.useWorkingTimeCalculation = builder.useWorkingTimeCalculation;
this.workingTimeSchedule =
builder.workingTimeSchedule.entrySet().stream()
.collect(
@ -262,6 +264,10 @@ public class TaskanaConfiguration {
return classificationTypes;
}
public boolean isUseWorkingTimeCalculation() {
return useWorkingTimeCalculation;
}
public Map<DayOfWeek, Set<LocalTimeInterval>> getWorkingTimeSchedule() {
return workingTimeSchedule;
}
@ -424,6 +430,7 @@ public class TaskanaConfiguration {
roleMap,
classificationTypes,
classificationCategoriesByType,
useWorkingTimeCalculation,
workingTimeSchedule,
workingTimeScheduleTimeZone,
customHolidays,
@ -473,6 +480,7 @@ public class TaskanaConfiguration {
return useManagedTransactions == other.useManagedTransactions
&& securityEnabled == other.securityEnabled
&& enforceServiceLevel == other.enforceServiceLevel
&& useWorkingTimeCalculation == other.useWorkingTimeCalculation
&& germanPublicHolidaysEnabled == other.germanPublicHolidaysEnabled
&& germanPublicHolidaysCorpusChristiEnabled
== other.germanPublicHolidaysCorpusChristiEnabled
@ -542,6 +550,8 @@ public class TaskanaConfiguration {
+ classificationTypes
+ ", classificationCategoriesByType="
+ classificationCategoriesByType
+ ", useWorkingTimeCalculation="
+ useWorkingTimeCalculation
+ ", workingTimeSchedule="
+ workingTimeSchedule
+ ", workingTimeScheduleTimeZone="
@ -650,6 +660,10 @@ public class TaskanaConfiguration {
// endregion
// region working time configuration
@TaskanaProperty("taskana.workingTime.useWorkingTimeCalculation")
private boolean useWorkingTimeCalculation = true;
@TaskanaProperty("taskana.workingTime.schedule")
private Map<DayOfWeek, Set<LocalTimeInterval>> workingTimeSchedule =
initDefaultWorkingTimeSchedule();
@ -826,6 +840,7 @@ public class TaskanaConfiguration {
this.classificationTypes = conf.classificationTypes;
this.classificationCategoriesByType = conf.classificationCategoriesByType;
// working time configuration
this.useWorkingTimeCalculation = conf.useWorkingTimeCalculation;
this.workingTimeSchedule = conf.workingTimeSchedule;
this.workingTimeScheduleTimeZone = conf.workingTimeScheduleTimeZone;
this.customHolidays = conf.customHolidays;
@ -964,6 +979,11 @@ public class TaskanaConfiguration {
// region working time configuration
public Builder useWorkingTimeCalculation(boolean useWorkingTimeCalculation) {
this.useWorkingTimeCalculation = useWorkingTimeCalculation;
return this;
}
public Builder workingTimeSchedule(Map<DayOfWeek, Set<LocalTimeInterval>> workingTimeSchedule) {
this.workingTimeSchedule = workingTimeSchedule;
return this;

View File

@ -51,6 +51,7 @@ import pro.taskana.common.internal.persistence.MapTypeHandler;
import pro.taskana.common.internal.persistence.StringTypeHandler;
import pro.taskana.common.internal.security.CurrentUserContextImpl;
import pro.taskana.common.internal.workingtime.HolidaySchedule;
import pro.taskana.common.internal.workingtime.WorkingDayCalculatorImpl;
import pro.taskana.common.internal.workingtime.WorkingTimeCalculatorImpl;
import pro.taskana.monitor.api.MonitorService;
import pro.taskana.monitor.internal.MonitorMapper;
@ -128,11 +129,18 @@ public class TaskanaEngineImpl implements TaskanaEngine {
taskanaConfiguration.isGermanPublicHolidaysEnabled(),
taskanaConfiguration.isGermanPublicHolidaysCorpusChristiEnabled(),
taskanaConfiguration.getCustomHolidays());
workingTimeCalculator =
new WorkingTimeCalculatorImpl(
holidaySchedule,
taskanaConfiguration.getWorkingTimeSchedule(),
taskanaConfiguration.getWorkingTimeScheduleTimeZone());
if (taskanaConfiguration.isUseWorkingTimeCalculation()) {
workingTimeCalculator =
new WorkingTimeCalculatorImpl(
holidaySchedule,
taskanaConfiguration.getWorkingTimeSchedule(),
taskanaConfiguration.getWorkingTimeScheduleTimeZone());
} else {
workingTimeCalculator =
new WorkingDayCalculatorImpl(
holidaySchedule, taskanaConfiguration.getWorkingTimeScheduleTimeZone());
}
currentUserContext =
new CurrentUserContextImpl(TaskanaConfiguration.shouldUseLowerCaseForAccessIds());
createTransactionFactory(taskanaConfiguration.isUseManagedTransactions());

View File

@ -303,7 +303,8 @@ class ServiceLevelHandler {
private Instant calculateDue(Instant planned, Duration duration) {
Instant dueExclusive = workingTimeCalculator.addWorkingTime(planned, duration);
if (!planned.equals(dueExclusive)) {
if (taskanaEngine.getEngine().getConfiguration().isUseWorkingTimeCalculation()
&& !planned.equals(dueExclusive)) {
// Calculation is exclusive, but we want due date to be inclusive. Hence, we subtract a
// millisecond
// If planned and dueExclusive are the same values, we don't want due to be before planned.
@ -320,7 +321,11 @@ class ServiceLevelHandler {
return due;
} else {
// due is inclusive, but calculation happens exclusive.
return workingTimeCalculator.subtractWorkingTime(due.plusMillis(1), duration);
Instant normalize =
taskanaEngine.getEngine().getConfiguration().isUseWorkingTimeCalculation()
? due.plusMillis(1)
: due;
return workingTimeCalculator.subtractWorkingTime(normalize, duration);
}
}
@ -328,9 +333,13 @@ class ServiceLevelHandler {
// plusMillis since due is inclusive, but calculation happens exclusive.
// minusMillis since we calculated a due date
// Without that some edge case fail (e.g. due is exactly the start of weekend)
return workingTimeCalculator
.subtractWorkingTime(due.plusMillis(1), Duration.ZERO)
.minusMillis(1);
if (taskanaEngine.getEngine().getConfiguration().isUseWorkingTimeCalculation()) {
return workingTimeCalculator
.subtractWorkingTime(due.plusMillis(1), Duration.ZERO)
.minusMillis(1);
}
return workingTimeCalculator.subtractWorkingTime(due, Duration.ZERO);
}
private Instant normalizePlanned(Instant instant) {
@ -625,9 +634,9 @@ class ServiceLevelHandler {
}
// TODO Do we need to compare Key and Id or could we simply compare ClassificationSummary only?
final boolean isClassificationKeyChanged =
Objects.equals(newTaskImpl.getClassificationKey(), oldTaskImpl.getClassificationKey());
!Objects.equals(newTaskImpl.getClassificationKey(), oldTaskImpl.getClassificationKey());
final boolean isClassificationIdChanged =
Objects.equals(newTaskImpl.getClassificationId(), oldTaskImpl.getClassificationId());
!Objects.equals(newTaskImpl.getClassificationId(), oldTaskImpl.getClassificationId());
final boolean isManualPriorityChanged =
newTaskImpl.getManualPriority() != oldTaskImpl.getManualPriority();

View File

@ -1,6 +1,7 @@
package acceptance;
import java.lang.reflect.Field;
import java.sql.SQLException;
import java.time.Duration;
import java.time.Instant;
import java.time.LocalDate;
@ -56,6 +57,14 @@ public abstract class AbstractAccTest {
.ifPresent(JobScheduler::stop);
}
protected static void initTaskanaEngine(TaskanaConfiguration configuration) throws SQLException {
taskanaConfiguration = configuration;
taskanaEngine =
TaskanaEngine.buildTaskanaEngine(taskanaConfiguration, ConnectionManagementMode.AUTOCOMMIT);
taskService = (TaskServiceImpl) taskanaEngine.getTaskService();
workingTimeCalculator = taskanaEngine.getWorkingTimeCalculator();
}
protected static void resetDb(boolean dropTables) throws Exception {
DataSource dataSource = DataSourceGenerator.getDataSource();

View File

@ -0,0 +1,223 @@
package acceptance.persistence;
import static org.assertj.core.api.Assertions.assertThat;
import static pro.taskana.common.api.SharedConstants.MASTER_DOMAIN;
import acceptance.AbstractAccTest;
import java.time.Duration;
import java.time.Instant;
import java.util.List;
import java.util.Map;
import java.util.TimeZone;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import pro.taskana.TaskanaConfiguration;
import pro.taskana.TaskanaConfiguration.Builder;
import pro.taskana.classification.api.ClassificationService;
import pro.taskana.classification.api.models.Classification;
import pro.taskana.common.api.JobService;
import pro.taskana.common.api.ScheduledJob;
import pro.taskana.common.internal.JobServiceImpl;
import pro.taskana.common.test.security.JaasExtension;
import pro.taskana.common.test.security.WithAccessId;
import pro.taskana.task.api.TaskCustomField;
import pro.taskana.task.api.TaskService;
import pro.taskana.task.api.models.Task;
import pro.taskana.task.internal.jobs.TaskCleanupJob;
import pro.taskana.task.internal.models.TaskImpl;
import pro.taskana.workbasket.api.WorkbasketCustomField;
import pro.taskana.workbasket.api.WorkbasketPermission;
import pro.taskana.workbasket.api.WorkbasketService;
import pro.taskana.workbasket.api.WorkbasketType;
import pro.taskana.workbasket.api.models.Workbasket;
import pro.taskana.workbasket.api.models.WorkbasketAccessItem;
@ExtendWith(JaasExtension.class)
public class UpdateObjectsUseUtcTimeStampsWithWorkingDaysCalculationAccTest
extends AbstractAccTest {
@BeforeAll
protected static void setupTest() throws Exception {
resetDb(false);
TaskanaConfiguration config =
new Builder(taskanaConfiguration).useWorkingTimeCalculation(false).build();
initTaskanaEngine(config);
}
@WithAccessId(user = "admin")
@Test
void testTimestampsOnTaskUpdate() throws Exception {
TaskService taskService = taskanaEngine.getTaskService();
Task task = taskService.getTask("TKI:000000000000000000000000000000000000");
Instant now = Instant.now();
task.setPlanned(now.plus(Duration.ofHours(17)));
// associated Classification has ServiceLevel 'P1D'
task.setDue(workingTimeCalculator.addWorkingTime(task.getPlanned(), Duration.ofDays(1)));
TaskImpl ti = (TaskImpl) task;
ti.setCompleted(now.plus(Duration.ofHours(27)));
TimeZone originalZone = TimeZone.getDefault();
Task updatedTask = taskService.updateTask(task);
TimeZone.setDefault(TimeZone.getTimeZone("EST"));
Task retrievedTask = taskService.getTask(updatedTask.getId());
TimeZone.setDefault(originalZone);
assertThat(retrievedTask).isEqualTo(updatedTask);
}
@WithAccessId(user = "user-1-1")
@Test
void testCreatedTaskObjectEqualsReadTaskObjectInNewTimezone() throws Exception {
TaskService taskService = taskanaEngine.getTaskService();
Task newTask = taskService.newTask("USER-1-1", "DOMAIN_A");
newTask.setClassificationKey("T2100");
newTask.setPrimaryObjRef(
createObjectReference("COMPANY_A", "SYSTEM_A", "INSTANCE_A", "VNR", "1234567"));
for (TaskCustomField taskCustomField : TaskCustomField.values()) {
newTask.setCustomField(taskCustomField, taskCustomField.name());
}
newTask.setCustomAttributeMap(createSimpleCustomPropertyMap(5));
newTask.setDescription("Description of test task");
newTask.setNote("My note");
newTask.addAttachment(
createExampleAttachment(
"DOCTYPE_DEFAULT",
createObjectReference(
"COMPANY_A",
"SYSTEM_B",
"INSTANCE_B",
"ArchiveId",
"12345678901234567890123456789012345678901234567890"),
"E-MAIL",
Instant.parse("2018-01-15T00:00:00Z"),
createSimpleCustomPropertyMap(3)));
TimeZone originalZone = TimeZone.getDefault();
Task createdTask = taskService.createTask(newTask);
TimeZone.setDefault(TimeZone.getTimeZone("EST"));
Task readTask = taskService.getTask(createdTask.getId());
TimeZone.setDefault(originalZone);
assertThat(readTask).isEqualTo(createdTask);
}
@WithAccessId(user = "admin")
@Test
void testTimestampsOnClassificationUpdate() throws Exception {
ClassificationService classificationService = taskanaEngine.getClassificationService();
Classification classification =
classificationService.getClassification("CLI:000000000000000000000000000000000001");
classification.setPriority(17);
TimeZone originalZone = TimeZone.getDefault();
Classification updatedClassification =
classificationService.updateClassification(classification);
TimeZone.setDefault(TimeZone.getTimeZone("EST"));
Classification retrievedClassification =
classificationService.getClassification(updatedClassification.getId());
TimeZone.setDefault(originalZone);
assertThat(retrievedClassification).isEqualTo(updatedClassification);
}
@WithAccessId(user = "businessadmin")
@Test
void testTimestampsOnCreateMasterClassification() throws Exception {
ClassificationService classificationService = taskanaEngine.getClassificationService();
final long amountOfClassificationsBefore =
classificationService.createClassificationQuery().count();
Classification classification =
classificationService.newClassification("Key0", MASTER_DOMAIN, "TASK");
classification.setIsValidInDomain(true);
classification.setServiceLevel("P1D");
classification = classificationService.createClassification(classification);
// check only 1 created
long amountOfClassificationsAfter = classificationService.createClassificationQuery().count();
assertThat(amountOfClassificationsAfter).isEqualTo(amountOfClassificationsBefore + 1);
TimeZone originalZone = TimeZone.getDefault();
TimeZone.setDefault(TimeZone.getTimeZone("EST"));
Classification retrievedClassification =
classificationService.getClassification(classification.getId());
TimeZone.setDefault(originalZone);
assertThat(retrievedClassification).isEqualTo(classification);
}
@WithAccessId(user = "admin")
@Test
void testTimestampsOnWorkbasketUpdate() throws Exception {
WorkbasketService workbasketService = taskanaEngine.getWorkbasketService();
Workbasket workbasket =
workbasketService.getWorkbasket("WBI:100000000000000000000000000000000001");
workbasket.setCustomField(WorkbasketCustomField.CUSTOM_1, "bla");
TimeZone originalZone = TimeZone.getDefault();
Workbasket updatedWorkbasket = workbasketService.updateWorkbasket(workbasket);
TimeZone.setDefault(TimeZone.getTimeZone("EST"));
Workbasket retrievedWorkbasket = workbasketService.getWorkbasket(updatedWorkbasket.getId());
TimeZone.setDefault(originalZone);
assertThat(retrievedWorkbasket).isEqualTo(updatedWorkbasket);
}
@WithAccessId(user = "businessadmin")
@Test
void testTimestampsOnCreateWorkbasket() throws Exception {
WorkbasketService workbasketService = taskanaEngine.getWorkbasketService();
final int before = workbasketService.createWorkbasketQuery().domainIn("DOMAIN_A").list().size();
Workbasket workbasket = workbasketService.newWorkbasket("NT1234", "DOMAIN_A");
workbasket.setName("Megabasket");
workbasket.setType(WorkbasketType.GROUP);
workbasket.setOrgLevel1("company");
workbasket = workbasketService.createWorkbasket(workbasket);
WorkbasketAccessItem wbai =
workbasketService.newWorkbasketAccessItem(workbasket.getId(), "user-1-2");
wbai.setPermission(WorkbasketPermission.READ, true);
workbasketService.createWorkbasketAccessItem(wbai);
int after = workbasketService.createWorkbasketQuery().domainIn("DOMAIN_A").list().size();
assertThat(after).isEqualTo(before + 1);
TimeZone originalZone = TimeZone.getDefault();
TimeZone.setDefault(TimeZone.getTimeZone("EST"));
Workbasket retrievedWorkbasket = workbasketService.getWorkbasket("NT1234", "DOMAIN_A");
TimeZone.setDefault(originalZone);
assertThat(retrievedWorkbasket).isEqualTo(workbasket);
}
@WithAccessId(user = "user-1-2")
@Test
void testTimestampsOnCreateScheduledJob() throws Exception {
resetDb(true);
ScheduledJob job = new ScheduledJob();
job.setArguments(Map.of("keyBla", "valueBla"));
job.setType(TaskCleanupJob.class.getName());
job.setDue(Instant.now().minus(Duration.ofHours(5)));
job.setLockExpires(Instant.now().minus(Duration.ofHours(5)));
JobService jobService = taskanaEngine.getJobService();
job = jobService.createJob(job);
TimeZone originalZone = TimeZone.getDefault();
TimeZone.setDefault(TimeZone.getTimeZone("EST"));
JobServiceImpl jobServiceImpl = (JobServiceImpl) jobService;
List<ScheduledJob> jobs = jobServiceImpl.findJobsToRun();
final ScheduledJob jobForLambda = job;
ScheduledJob retrievedJob =
jobs.stream()
.filter(
j ->
j.getJobId().equals(jobForLambda.getJobId())
&& j.getArguments() != null
&& "valueBla".equals(j.getArguments().get("keyBla")))
.findFirst()
.orElse(null);
TimeZone.setDefault(originalZone);
assertThat(retrievedJob).isEqualTo(job);
}
}

View File

@ -0,0 +1,616 @@
package acceptance.task;
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;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.List;
import org.assertj.core.api.ThrowableAssert.ThrowingCallable;
import org.assertj.core.data.TemporalUnitWithinOffset;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import pro.taskana.TaskanaConfiguration;
import pro.taskana.TaskanaConfiguration.Builder;
import pro.taskana.classification.api.ClassificationService;
import pro.taskana.classification.api.models.Classification;
import pro.taskana.common.api.BulkOperationResults;
import pro.taskana.common.api.exceptions.InvalidArgumentException;
import pro.taskana.common.api.exceptions.TaskanaException;
import pro.taskana.common.test.security.JaasExtension;
import pro.taskana.common.test.security.WithAccessId;
import pro.taskana.task.api.TaskService;
import pro.taskana.task.api.exceptions.TaskNotFoundException;
import pro.taskana.task.api.models.Task;
import pro.taskana.workbasket.api.exceptions.NotAuthorizedOnWorkbasketException;
@ExtendWith(JaasExtension.class)
public class ServiceLevelPriorityWithWorkingDaysCalculationAccTest extends AbstractAccTest {
private static ClassificationService classificationService;
@BeforeAll
protected static void setupTest() throws Exception {
resetDb(false);
TaskanaConfiguration config =
new Builder(taskanaConfiguration).useWorkingTimeCalculation(false).build();
initTaskanaEngine(config);
classificationService = taskanaEngine.getClassificationService();
}
/* CREATE TASK */
@WithAccessId(user = "user-1-1")
@Test
void should_CalculatePlannedDateAtCreate() throws Exception {
// P16D
Classification classification = classificationService.getClassification("L110105", "DOMAIN_A");
long serviceLevelDays = Duration.parse(classification.getServiceLevel()).toDays();
assertThat(serviceLevelDays).isEqualTo(16);
Task newTask = taskService.newTask("USER-1-1", classification.getDomain());
newTask.setClassificationKey(classification.getKey());
newTask.setPrimaryObjRef(
createObjectReference("COMPANY_A", "SYSTEM_A", "INSTANCE_A", "VNR", "1234567"));
Instant due =
moveBackToWorkingDay(
Instant.now().truncatedTo(ChronoUnit.MILLIS).plus(40, ChronoUnit.DAYS));
newTask.setDue(due);
Task createdTask = taskService.createTask(newTask);
assertThat(createdTask.getId()).isNotNull();
Task readTask = taskService.getTask(createdTask.getId());
assertThat(readTask).isNotNull();
assertThat(readTask.getDue()).isEqualTo(due);
Instant expectedPlanned =
workingTimeCalculator.subtractWorkingTime(due, Duration.ofDays(serviceLevelDays));
assertThat(readTask.getPlanned()).isEqualTo(expectedPlanned);
}
@WithAccessId(user = "user-1-1")
@Test
void should_CalculateDueDateAtCreate() throws Exception {
// P16D
Classification classification = classificationService.getClassification("L110105", "DOMAIN_A");
long serviceLevelDays = Duration.parse(classification.getServiceLevel()).toDays();
assertThat(serviceLevelDays).isEqualTo(16);
Task newTask = taskService.newTask("USER-1-1", classification.getDomain());
newTask.setClassificationKey(classification.getKey());
newTask.setPrimaryObjRef(
createObjectReference("COMPANY_A", "SYSTEM_A", "INSTANCE_A", "VNR", "1234567"));
Instant planned = moveForwardToWorkingDay(Instant.now().truncatedTo(ChronoUnit.MILLIS));
newTask.setPlanned(planned);
Task createdTask = taskService.createTask(newTask);
assertThat(createdTask.getId()).isNotNull();
Task readTask = taskService.getTask(createdTask.getId());
assertThat(readTask).isNotNull();
assertThat(readTask.getPlanned()).isEqualTo(planned);
Instant expectedDue =
workingTimeCalculator.addWorkingTime(
readTask.getPlanned(), Duration.ofDays(serviceLevelDays));
assertThat(readTask.getDue()).isEqualTo(expectedDue);
}
@WithAccessId(user = "user-1-1")
@Test
void should_NotThrowException_When_DueAndPlannedAreConsistent() throws Exception {
Classification classification = classificationService.getClassification("T2100", "DOMAIN_A");
long duration = Duration.parse(classification.getServiceLevel()).toDays();
Task newTask = taskService.newTask("USER-1-1", "DOMAIN_A");
newTask.setPlanned(moveForwardToWorkingDay(Instant.now()));
newTask.setClassificationKey(classification.getKey());
newTask.setPrimaryObjRef(
createObjectReference("COMPANY_A", "SYSTEM_A", "INSTANCE_A", "VNR", "1234567"));
newTask.setOwner("user-1-1");
// due date according to service level
Instant expectedDue =
workingTimeCalculator.addWorkingTime(newTask.getPlanned(), Duration.ofDays(duration));
newTask.setDue(expectedDue);
ThrowingCallable call = () -> taskService.createTask(newTask);
assertThatCode(call).doesNotThrowAnyException();
}
@WithAccessId(user = "user-1-1")
@Test
void should_ThrowException_When_DueAndPlannedAreInconsistent() {
Task newTask = taskService.newTask("USER-1-1", "DOMAIN_A");
Instant planned = moveForwardToWorkingDay(Instant.now().plus(2, ChronoUnit.HOURS));
newTask.setClassificationKey("T2100"); // P10D
newTask.setPrimaryObjRef(
createObjectReference("COMPANY_A", "SYSTEM_A", "INSTANCE_A", "VNR", "1234567"));
newTask.setOwner("user-1-1");
newTask.setPlanned(planned);
newTask.setDue(planned); // due date not according to service level
ThrowingCallable call = () -> taskService.createTask(newTask);
assertThatThrownBy(call).isInstanceOf(InvalidArgumentException.class);
}
@WithAccessId(user = "user-1-1")
@Test
void should_VerifyThatCreateAndPlannedAreClose() throws Exception {
Instant now = Instant.now().truncatedTo(ChronoUnit.MILLIS);
Instant inTwoHours = now.plus(2, ChronoUnit.HOURS);
Task newTask = taskService.newTask("USER-1-1", "DOMAIN_A");
Instant planned = moveForwardToWorkingDay(inTwoHours);
newTask.setClassificationKey("T2100");
newTask.setPrimaryObjRef(
createObjectReference("COMPANY_A", "SYSTEM_A", "INSTANCE_A", "VNR", "1234567"));
newTask.setOwner("user-1-1");
newTask.setPlanned(planned);
Task createdTask = taskService.createTask(newTask);
assertThat(createdTask).isNotNull();
assertThat(createdTask.getPlanned()).isEqualTo(planned);
assertThat(createdTask.getCreated()).isBefore(createdTask.getPlanned());
assertThat(createdTask.getPlanned())
.isCloseTo(
moveForwardToWorkingDay(createdTask.getCreated()),
new TemporalUnitWithinOffset(2L, ChronoUnit.HOURS));
}
/* UPDATE TASK */
@WithAccessId(user = "user-1-1")
@Test
void should_ThrowException_When_DueAndPlannedAreChangedInconsistently() throws Exception {
TaskService taskService = taskanaEngine.getTaskService();
Task task = taskService.getTask("TKI:000000000000000000000000000000000000"); // P1D
task.setDue(Instant.parse("2020-07-02T00:00:00Z"));
task.setPlanned(Instant.parse("2020-07-07T00:00:00Z"));
assertThatThrownBy(() -> taskService.updateTask(task))
.isInstanceOf(InvalidArgumentException.class)
.hasMessage(
"Cannot update a task with given planned 2020-07-07T00:00:00Z and due "
+ "date 2020-07-02T00:00:00Z not matching the service level PT24H.");
}
@WithAccessId(user = "user-b-2")
@Test
void should_SetPlanned_When_SetPlannedRequestContainsDuplicateTaskIds() throws Exception {
// This test works with the following tasks (w/o attachments) and classifications
//
// +-----------------------------------------+------------------------------------------+------+
// | TaskId | ClassificationId | SL |
// +-----------------------------------------+------------------------------------------+------+
// |TKI:000000000000000000000000000000000058 | CLI:200000000000000000000000000000000017 | P1D |
// |TKI:000000000000000000000000000000000059 | CLI:200000000000000000000000000000000017 | P1D |
// |TKI:000000000000000000000000000000000060 | CLI:200000000000000000000000000000000017 | P1D |
// +-----------------------------------------+------------------------------------------+------+
String tkId1 = "TKI:000000000000000000000000000000000058";
String tkId2 = "TKI:000000000000000000000000000000000059";
String tkId3 = "TKI:000000000000000000000000000000000058";
String tkId4 = "TKI:000000000000000000000000000000000060";
List<String> taskIds = List.of(tkId1, tkId2, tkId3, tkId4);
Instant planned = getInstant("2020-02-11T07:00:00");
BulkOperationResults<String, TaskanaException> results =
taskService.setPlannedPropertyOfTasks(planned, taskIds);
assertThat(results.containsErrors()).isFalse();
Instant dueExpected = getInstant("2020-02-12T07:00:00");
Instant due1 = taskService.getTask(tkId1).getDue();
assertThat(due1).isEqualTo(dueExpected);
Instant due2 = taskService.getTask(tkId2).getDue();
assertThat(due2).isEqualTo(dueExpected);
Instant due3 = taskService.getTask(tkId3).getDue();
assertThat(due3).isEqualTo(dueExpected);
Instant due4 = taskService.getTask(tkId4).getDue();
assertThat(due4).isEqualTo(dueExpected);
}
@WithAccessId(user = "user-b-2")
@Test
void should_SetPlanned_When_RequestContainsDuplicatesAndNotExistingTaskIds() throws Exception {
String tkId1 = "TKI:000000000000000000000000000000000058";
String tkId2 = "TKI:000000000000000000000000000047110059";
String tkId3 = "TKI:000000000000000000000000000000000059";
String tkId4 = "TKI:000000000000000000000000000000000058";
String tkId5 = "TKI:000000000000000000000000000000000060";
List<String> taskIds = List.of(tkId1, tkId2, tkId3, tkId4, tkId5);
Instant planned = getInstant("2020-04-20T07:00:00");
BulkOperationResults<String, TaskanaException> results =
taskService.setPlannedPropertyOfTasks(planned, taskIds);
assertThat(results.containsErrors()).isTrue();
assertThat(results.getErrorMap()).hasSize(1);
assertThat(results.getErrorForId("TKI:000000000000000000000000000047110059"))
.isInstanceOf(TaskNotFoundException.class);
Instant dueExpected = getInstant("2020-04-21T07:00:00");
Instant due1 = taskService.getTask(tkId1).getDue();
assertThat(due1).isEqualTo(dueExpected);
Instant due3 = taskService.getTask(tkId3).getDue();
assertThat(due3).isEqualTo(dueExpected);
Instant due5 = taskService.getTask(tkId5).getDue();
assertThat(due5).isEqualTo(dueExpected);
}
@WithAccessId(user = "user-1-1")
@Test
void should_SetPlanned_When_RequestContainsTasksWithAttachments() throws Exception {
// This test works with the following tasks, attachments and classifications
//
// +-----------------------------------------+------------------------------------------+------+
// | Task / associated attachment | Classification | SL |
// +-----------------------------------------+------------------------------------------+------+
// |TKI:000000000000000000000000000000000000 | CLI:100000000000000000000000000000000016 | P1D |
// |TAI:000000000000000000000000000000000000 | CLI:100000000000000000000000000000000003 | P13D |
// |TAI:000000000000000000000000000000000009 | CLI:100000000000000000000000000000000003 | P13D |
// +-----------------------------------------+------------------------------------------+------+
// |TKI:000000000000000000000000000000000001 | CLI:100000000000000000000000000000000005 | P15D |
// |TAI:000000000000000000000000000000000001 | CLI:100000000000000000000000000000000002 | P2D |
// |TAI:000000000000000000000000000000000002 | CLI:000000000000000000000000000000000003 | P3D |
// +-----------------------------------------+------------------------------------------+------+
// |TKI:000000000000000000000000000000000002 | CLI:100000000000000000000000000000000016 | P1D |
// |TAI:000000000000000000000000000000000003 | CLI:000000000000000000000000000000000004 | P4D |
// |TAI:000000000000000000000000000000000004 | CLI:000000000000000000000000000000000005 | P5D |
// |TAI:000000000000000000000000000000000005 | CLI:000000000000000000000000000000000006 | P5D |
// |TAI:000000000000000000000000000000000006 | CLI:000000000000000000000000000000000007 | P6D |
// |TAI:000000000000000000000000000000000007 | CLI:100000000000000000000000000000000008 | P1D |
// +-----------------------------------------+------------------------------------------+------+
String tkId0 = "TKI:000000000000000000000000000000000000";
String tkId1 = "TKI:000000000000000000000000000000000001";
String tkId2 = "TKI:000000000000000000000000000000000002";
// get due dates by updating the tasks individually
Task task0 = taskService.getTask(tkId0);
Task task1 = taskService.getTask(tkId1);
Task task2 = taskService.getTask(tkId2);
Instant planned = getInstant("2020-04-21T13:00:00");
task0.setPlanned(planned);
task1.setPlanned(planned);
task2.setPlanned(planned);
final Instant due0 = taskService.updateTask(task0).getDue();
final Instant due1 = taskService.updateTask(task1).getDue();
final Instant due2 = taskService.updateTask(task2).getDue();
// now check that bulk update gives the same due dates
List<String> taskIds = List.of(tkId0, tkId1, tkId2);
BulkOperationResults<String, TaskanaException> results =
taskService.setPlannedPropertyOfTasks(planned, taskIds);
Instant dueBulk0 = taskService.getTask(tkId0).getDue();
Instant dueBulk1 = taskService.getTask(tkId1).getDue();
Instant dueBulk2 = taskService.getTask(tkId2).getDue();
assertThat(dueBulk0).isEqualTo(planned.plus(1, ChronoUnit.DAYS));
assertThat(dueBulk1).isEqualTo(planned.plus(2, ChronoUnit.DAYS));
assertThat(dueBulk2).isEqualTo(planned.plus(1, ChronoUnit.DAYS));
assertThat(results.containsErrors()).isFalse();
assertThat(dueBulk0).isEqualTo(due0);
assertThat(dueBulk1).isEqualTo(due1);
assertThat(dueBulk2).isEqualTo(due2);
}
// the following tests use these tasks, attachments and classifications
// +-----------------------------------------+------------------------------------------+------+
// | Task / associated attachment | Classification | SL |
// +-----------------------------------------+------------------------------------------+------+
// |TKI:000000000000000000000000000000000008 | CLI:100000000000000000000000000000000003 | P13D |
// |TAI:000000000000000000000000000000000008 | CLI:000000000000000000000000000000000009 | P8D |
// +---------------------------------------- + -----------------------------------------+ -----+
// |TKI:000000000000000000000000000000000009 | CLI:100000000000000000000000000000000003 | P13D |
// +---------------------------------------- + -----------------------------------------+ -----+
// |TKI:000000000000000000000000000000000010 | CLI:100000000000000000000000000000000003 | P13D |
// |TAI:000000000000000000000000000000000014 | CLI:100000000000000000000000000000000004,| P14D |
// +-----------------------------------------+------------------------------------------+------+
@WithAccessId(user = "user-b-2")
@Test
void should_ReturnBulkLog_When_UserIsNotAuthorizedForTasks() {
String tkId1 = "TKI:000000000000000000000000000000000008";
String tkId2 = "TKI:000000000000000000000000000000000009";
String tkId3 = "TKI:000000000000000000000000000000000008";
String tkId4 = "TKI:000000000000000000000000000000000010";
List<String> taskIds = List.of(tkId1, tkId2, tkId3, tkId4);
Instant planned = getInstant("2020-02-25T07:00:00");
BulkOperationResults<String, TaskanaException> results =
taskService.setPlannedPropertyOfTasks(planned, taskIds);
assertThat(results.containsErrors()).isTrue();
assertThat(results.getFailedIds()).hasSize(3).containsAnyElementsOf(taskIds);
assertThat(results.getErrorMap().values())
.hasOnlyElementsOfType(NotAuthorizedOnWorkbasketException.class);
}
@WithAccessId(user = "admin")
@Test
void should_SetPlannedPropertyOfTasks_When_RequestedByAdminUser() throws Exception {
String tkId1 = "TKI:000000000000000000000000000000000008";
String tkId2 = "TKI:000000000000000000000000000000000009";
String tkId3 = "TKI:000000000000000000000000000000000008";
String tkId4 = "TKI:000000000000000000000000000000000010"; // all three have P13D
List<String> taskIds = List.of(tkId1, tkId2, tkId3, tkId4);
Instant planned = getInstant("2020-05-03T07:00:00");
BulkOperationResults<String, TaskanaException> results =
taskService.setPlannedPropertyOfTasks(planned, taskIds);
assertThat(results.containsErrors()).isFalse();
Instant dueBulk1 = taskService.getTask(tkId1).getDue();
Instant dueBulk2 = taskService.getTask(tkId2).getDue();
Instant dueBulk3 = taskService.getTask(tkId3).getDue();
Instant dueBulk4 = taskService.getTask(tkId4).getDue();
assertThat(dueBulk1).isEqualTo(getInstant("2020-05-14T07:00:00"));
assertThat(dueBulk2).isEqualTo(getInstant("2020-05-22T07:00:00"));
assertThat(dueBulk3).isEqualTo(getInstant("2020-05-14T07:00:00"));
assertThat(dueBulk4).isEqualTo(getInstant("2020-05-22T07:00:00"));
}
@WithAccessId(user = "admin")
@Test
void should_DoNothing_When_SetPlannedIsCalledWithEmptyTasksList() {
Instant planned = getInstant("2020-05-03T07:00:00");
BulkOperationResults<String, TaskanaException> results =
taskService.setPlannedPropertyOfTasks(planned, new ArrayList<>());
assertThat(results.containsErrors()).isFalse();
}
// +-----------------------------------------+------------------------------------------+------+
// |TKI:000000000000000000000000000000000002 | CLI:100000000000000000000000000000000016 | P1D |
// |TAI:000000000000000000000000000000000003 | CLI:000000000000000000000000000000000004 | P4D |
// |TAI:000000000000000000000000000000000004 | CLI:000000000000000000000000000000000005 | P5D |
// |TAI:000000000000000000000000000000000005 | CLI:000000000000000000000000000000000006 | P5D |
// |TAI:000000000000000000000000000000000006 | CLI:000000000000000000000000000000000007 | P6D |
// |TAI:000000000000000000000000000000000007 | CLI:100000000000000000000000000000000008 | P1D |
// |TKI:000000000000000000000000000000000066 | CLI:100000000000000000000000000000000024 | P0D |
// +-----------------------------------------+------------------------------------------+------+
@WithAccessId(user = "admin")
@Test
void should_SetPlannedPropertyWithBulkUpdate_When_RequestContainsASingleTask() throws Exception {
String taskId = "TKI:000000000000000000000000000000000002";
Instant planned = getInstant("2020-05-03T07:00:00");
// test bulk operation setPlanned...
BulkOperationResults<String, TaskanaException> results =
taskService.setPlannedPropertyOfTasks(planned, List.of(taskId));
Task task = taskService.getTask(taskId);
assertThat(results.containsErrors()).isFalse();
Instant expectedDue =
workingTimeCalculator.addWorkingTime(task.getPlanned(), Duration.ofDays(1));
assertThat(task.getDue()).isEqualTo(expectedDue);
}
@WithAccessId(user = "admin")
@Test
void should_SetPlannedPropertyOnSingle_When_UpdateTaskWasCalled() throws Exception {
String taskId = "TKI:000000000000000000000000000000000002";
Task task = taskService.getTask(taskId);
// test update of planned date via updateTask()
task.setPlanned(task.getPlanned().plus(Duration.ofDays(3)));
task = taskService.updateTask(task);
Instant expectedDue =
workingTimeCalculator.addWorkingTime(task.getPlanned(), Duration.ofDays(1));
assertThat(task.getDue()).isEqualTo(expectedDue);
}
@WithAccessId(user = "admin")
@Test
void should_SetPlanned_When_OnlyDueWasChanged() throws Exception {
String taskId = "TKI:000000000000000000000000000000000002"; // P1D
Instant planned = getInstant("2020-05-03T07:00:00");
Task task = taskService.getTask(taskId);
// test update of due with unchanged planned
task.setDue(planned.plus(Duration.ofDays(8))); // Monday
task = taskService.updateTask(task);
assertThat(task.getPlanned()).isEqualTo(getInstant("2020-05-08T07:00:00")); // Friday
}
@WithAccessId(user = "admin")
@Test
void should_SetDue_When_OnlyPlannedWasChanged() throws Exception {
String taskId = "TKI:000000000000000000000000000000000002"; // P1D ServiceLevel
Task task = taskService.getTask(taskId);
Instant planned = getInstant("2020-05-10T07:00:00"); // Sunday
task.setPlanned(planned);
task = taskService.updateTask(task);
assertThat(task.getPlanned()).isEqualTo(getInstant("2020-05-11T07:00:00")); // Monday
assertThat(task.getDue()).isEqualTo(getInstant("2020-05-12T07:00:00")); // Tuesday
}
@WithAccessId(user = "admin")
@Test
void should_SetPlanned_When_DueIsChangedAndPlannedIsNulled() throws Exception {
String taskId = "TKI:000000000000000000000000000000000002";
Instant due = getInstant("2020-05-06T07:00:00");
Task task = taskService.getTask(taskId);
task.setDue(due);
task.setPlanned(null);
task = taskService.updateTask(task);
String serviceLevel = task.getClassificationSummary().getServiceLevel();
Instant expPlanned =
workingTimeCalculator.subtractWorkingTime(task.getDue(), Duration.parse(serviceLevel));
assertThat(task.getPlanned()).isEqualTo(expPlanned);
assertThat(task.getDue()).isEqualTo(due);
}
@WithAccessId(user = "admin")
@Test
void should_SetDue_When_TaskUpdateIsCalled() throws Exception {
String taskId = "TKI:000000000000000000000000000000000002";
final Instant planned = getInstant("2020-05-03T07:00:00"); // Sunday
Task task = taskService.getTask(taskId);
task.setPlanned(null);
task = taskService.updateTask(task);
Instant expectedDue =
workingTimeCalculator.addWorkingTime(task.getPlanned(), Duration.ofDays(1));
assertThat(task.getDue()).isEqualTo(expectedDue);
task.setDue(null);
task = taskService.updateTask(task);
expectedDue = workingTimeCalculator.addWorkingTime(task.getPlanned(), Duration.ofDays(1));
assertThat(task.getDue()).isEqualTo(expectedDue);
task.setPlanned(planned.plus(Duration.ofDays(13))); // Saturday
task.setDue(null);
task = taskService.updateTask(task);
expectedDue = workingTimeCalculator.addWorkingTime(task.getPlanned(), Duration.ofDays(1));
assertThat(task.getDue()).isEqualTo(expectedDue);
task.setDue(planned.plus(Duration.ofDays(13))); // Saturday
task.setPlanned(null);
task = taskService.updateTask(task);
assertThat(task.getPlanned()).isEqualTo(getInstant("2020-05-14T07:00:00"));
assertThat(task.getDue()).isEqualTo(getInstant("2020-05-15T07:00:00"));
}
@WithAccessId(user = "user-1-2")
@Test
void should_UpdateTaskPlannedOrDue_When_PlannedOrDueAreWeekendDays() throws Exception {
Task task = taskService.getTask("TKI:000000000000000000000000000000000030"); // SL=P13D
task.setPlanned(getInstant("2020-03-21T07:00:00")); // planned = saturday
task = taskService.updateTask(task);
assertThat(task.getDue()).isEqualTo(getInstant("2020-04-09T07:00:00"));
task.setDue(getInstant("2020-04-11T07:00:00")); // due = saturday
task.setPlanned(null);
task = taskService.updateTask(task);
assertThat(task.getPlanned()).isEqualTo(getInstant("2020-03-23T07:00:00"));
task.setDue(getInstant("2020-04-12T07:00:00")); // due = sunday
task = taskService.updateTask(task);
assertThat(task.getPlanned()).isEqualTo(getInstant("2020-03-23T07:00:00"));
task.setPlanned(getInstant("2020-03-21T07:00:00")); // planned = saturday
task.setDue(getInstant("2020-04-09T07:00:00")); // thursday
task = taskService.updateTask(task);
assertThat(task.getPlanned()).isEqualTo(getInstant("2020-03-23T07:00:00"));
task.setPlanned(getInstant("2020-03-03T07:00:00")); // planned on tuesday
task.setDue(getInstant("2020-03-22T07:00:00")); // due = sunday
task = taskService.updateTask(task);
assertThat(task.getDue()).isEqualTo(getInstant("2020-03-20T07:00:00")); // friday
task.setPlanned(getInstant("2024-03-29T07:00:00")); // Good Friday
task.setDue(null);
task = taskService.updateTask(task);
assertThat(task.getPlanned()).isEqualTo(getInstant("2024-04-02T07:00:00")); // Tuesday
assertThat(task.getDue()).isEqualTo(getInstant("2024-04-19T07:00:00")); // Friday
}
@WithAccessId(user = "user-1-1")
@Test
void should_UpdateTaskPlannedAndDueDate_When_PlannedDateIsNotWorkingDay() throws Exception {
Task task = taskService.getTask("TKI:000000000000000000000000000000000201"); // SL=P2D
assertThat(task.getClassificationSummary().getServiceLevel()).isEqualTo("P2D");
task.setPlanned(getInstant("2024-03-29T07:00:00")); // planned = Good Friday
task = taskService.updateTask(task);
assertThat(task.getPlanned()).isEqualTo(getInstant("2024-04-02T07:00:00")); // Tuesday
assertThat(task.getDue()).isEqualTo(getInstant("2024-04-04T07:00:00")); // Thursday
}
@WithAccessId(user = "user-1-1")
@Test
void should_UpdateTaskPlannedOrDue_When_PlannedOrDueAreOnWeekends_ServiceLevel_P0D()
throws Exception {
Task task = taskService.getTask("TKI:000000000000000000000000000000000066"); // P0D
// nothing changed
task = taskService.updateTask(task);
assertThat(task.getDue()).isEqualTo(getInstant("2018-01-29T15:55:00")); // Monday
assertThat(task.getPlanned()).isEqualTo(getInstant("2018-01-29T15:55:00")); // Monday
// planned changed, due did not change
task.setPlanned(getInstant("2020-03-21T07:00:00")); // Saturday
task = taskService.updateTask(task);
assertThat(task.getDue()).isEqualTo(getInstant("2020-03-23T07:00:00")); // Monday
assertThat(task.getPlanned()).isEqualTo(getInstant("2020-03-23T07:00:00")); // Monday
// due changed, planned did not change
task.setDue(getInstant("2020-04-12T07:00:00")); // Sunday
task = taskService.updateTask(task);
assertThat(task.getPlanned())
.isEqualTo(getInstant("2020-04-09T07:00:00")); // Thursday (skip Good Friday)
assertThat(task.getDue()).isEqualTo(getInstant("2020-04-09T07:00:00"));
// due changed, planned is null
task.setDue(getInstant("2020-04-11T07:00:00")); // Saturday
task.setPlanned(null);
task = taskService.updateTask(task);
assertThat(task.getPlanned())
.isEqualTo(getInstant("2020-04-09T07:00:00")); // Thursday (skip Good Friday)
assertThat(task.getDue()).isEqualTo(getInstant("2020-04-09T07:00:00"));
// planned changed, due is null
task.setPlanned(getInstant("2020-03-22T07:00:00")); // Sunday
task.setDue(null);
task = taskService.updateTask(task);
assertThat(task.getDue()).isEqualTo(getInstant("2020-03-23T07:00:00")); // Monday
assertThat(task.getPlanned()).isEqualTo(getInstant("2020-03-23T07:00:00")); // Monday
// both changed, not null (due at weekend)
task.setPlanned(getInstant("2020-03-20T07:00:00")); // Friday
task.setDue(getInstant("2020-03-22T07:00:00")); // Sunday
task = taskService.updateTask(task);
assertThat(task.getPlanned()).isEqualTo(getInstant("2020-03-20T07:00:00")); // Friday
assertThat(task.getDue()).isEqualTo(getInstant("2020-03-20T07:00:00")); // Friday
// both changed, not null (planned at weekend)
task.setPlanned(getInstant("2020-03-22T07:00:00")); // Sunday
task.setDue(getInstant("2020-03-23T07:00:00")); // Monday
task = taskService.updateTask(task);
assertThat(task.getDue()).isEqualTo(getInstant("2020-03-23T07:00:00")); // Monday
assertThat(task.getPlanned()).isEqualTo(getInstant("2020-03-23T07:00:00")); // Monday
// both changed, not null (both at weekend) within SLA
task.setPlanned(getInstant("2020-03-22T07:00:00")); // Sunday
task.setDue(getInstant("2020-03-22T07:00:00")); // Sunday
task = taskService.updateTask(task);
assertThat(task.getDue()).isEqualTo(getInstant("2020-03-20T07:00:00")); // Friday
assertThat(task.getPlanned()).isEqualTo(getInstant("2020-03-20T07:00:00")); // Friday
// both changed, not null (planned > due)
task.setPlanned(getInstant("2020-03-24T07:00:00")); // Tuesday
task.setDue(getInstant("2020-03-23T07:00:00")); // Monday
task = taskService.updateTask(task);
assertThat(task.getDue()).isEqualTo(getInstant("2020-03-23T07:00:00")); // Monday
assertThat(task.getPlanned()).isEqualTo(getInstant("2020-03-23T07:00:00")); // Monday
}
@WithAccessId(user = "user-1-1")
@Test
void should_NotThrowServiceLevelViolation_IfWeekendOrHolidaysBetweenDates() throws Exception {
Task task = taskService.getTask("TKI:000000000000000000000000000000000002"); // P1D
// SLA is broken but only with holidays in between
task.setDue(getInstant("2020-04-14T07:00:00")); // Tuesday after Easter
task.setPlanned(getInstant("2020-04-09T07:00:00")); // Thursday before Easter
task = taskService.updateTask(task);
assertThat(task.getDue()).isEqualTo(getInstant("2020-04-14T07:00:00")); // Tuesday
assertThat(task.getPlanned()).isEqualTo(getInstant("2020-04-09T07:00:00")); // Thursday
}
}

View File

@ -346,7 +346,7 @@ class QueryTasksAccTest extends AbstractAccTest {
List<Triplet<TaskCustomField, String[], Integer>> list =
List.of(
Triplet.of(
TaskCustomField.CUSTOM_1, new String[] {"custom%", "p%", "%xyz%", "efg"}, 3),
TaskCustomField.CUSTOM_1, new String[] {"custom%", "p%", "%xyz%", "efg"}, 4),
Triplet.of(TaskCustomField.CUSTOM_2, new String[] {"custom%", "a%"}, 2),
Triplet.of(TaskCustomField.CUSTOM_3, new String[] {"ffg"}, 1),
Triplet.of(TaskCustomField.CUSTOM_4, new String[] {"%ust%", "%ty"}, 2),
@ -359,7 +359,7 @@ class QueryTasksAccTest extends AbstractAccTest {
Triplet.of(TaskCustomField.CUSTOM_11, new String[] {"%ert"}, 3),
Triplet.of(TaskCustomField.CUSTOM_12, new String[] {"dd%"}, 1),
Triplet.of(TaskCustomField.CUSTOM_13, new String[] {"%dd_"}, 1),
Triplet.of(TaskCustomField.CUSTOM_14, new String[] {"%"}, 99),
Triplet.of(TaskCustomField.CUSTOM_14, new String[] {"%"}, 100),
Triplet.of(TaskCustomField.CUSTOM_15, new String[] {"___"}, 4),
Triplet.of(TaskCustomField.CUSTOM_16, new String[] {"___"}, 4));
assertThat(list).hasSameSizeAs(TaskCustomField.values());
@ -391,22 +391,22 @@ class QueryTasksAccTest extends AbstractAccTest {
// carefully constructed to always return exactly 2 results
List<Triplet<TaskCustomField, String[], Integer>> list =
List.of(
Triplet.of(TaskCustomField.CUSTOM_1, new String[] {"custom1"}, 98),
Triplet.of(TaskCustomField.CUSTOM_1, new String[] {"custom1"}, 99),
Triplet.of(TaskCustomField.CUSTOM_2, new String[] {""}, 2),
Triplet.of(TaskCustomField.CUSTOM_3, new String[] {"custom3"}, 98),
Triplet.of(TaskCustomField.CUSTOM_3, new String[] {"custom3"}, 99),
Triplet.of(TaskCustomField.CUSTOM_4, new String[] {""}, 2),
Triplet.of(TaskCustomField.CUSTOM_5, new String[] {"ew", "al", "el"}, 92),
Triplet.of(TaskCustomField.CUSTOM_6, new String[] {"11", "vvg"}, 95),
Triplet.of(TaskCustomField.CUSTOM_7, new String[] {"custom7", "ijk"}, 97),
Triplet.of(TaskCustomField.CUSTOM_8, new String[] {"not_existing"}, 99),
Triplet.of(TaskCustomField.CUSTOM_9, new String[] {"custom9"}, 98),
Triplet.of(TaskCustomField.CUSTOM_10, new String[] {"custom10"}, 98),
Triplet.of(TaskCustomField.CUSTOM_11, new String[] {"custom11"}, 98),
Triplet.of(TaskCustomField.CUSTOM_12, new String[] {"custom12"}, 98),
Triplet.of(TaskCustomField.CUSTOM_13, new String[] {"custom13"}, 98),
Triplet.of(TaskCustomField.CUSTOM_5, new String[] {"ew", "al", "el"}, 93),
Triplet.of(TaskCustomField.CUSTOM_6, new String[] {"11", "vvg"}, 96),
Triplet.of(TaskCustomField.CUSTOM_7, new String[] {"custom7", "ijk"}, 98),
Triplet.of(TaskCustomField.CUSTOM_8, new String[] {"not_existing"}, 100),
Triplet.of(TaskCustomField.CUSTOM_9, new String[] {"custom9"}, 99),
Triplet.of(TaskCustomField.CUSTOM_10, new String[] {"custom10"}, 99),
Triplet.of(TaskCustomField.CUSTOM_11, new String[] {"custom11"}, 99),
Triplet.of(TaskCustomField.CUSTOM_12, new String[] {"custom12"}, 99),
Triplet.of(TaskCustomField.CUSTOM_13, new String[] {"custom13"}, 99),
Triplet.of(TaskCustomField.CUSTOM_14, new String[] {"abc"}, 0),
Triplet.of(TaskCustomField.CUSTOM_15, new String[] {"custom15"}, 98),
Triplet.of(TaskCustomField.CUSTOM_16, new String[] {"custom16"}, 98));
Triplet.of(TaskCustomField.CUSTOM_15, new String[] {"custom15"}, 99),
Triplet.of(TaskCustomField.CUSTOM_16, new String[] {"custom16"}, 99));
assertThat(list).hasSameSizeAs(TaskCustomField.values());
return DynamicTest.stream(
@ -455,7 +455,7 @@ class QueryTasksAccTest extends AbstractAccTest {
throws InvalidArgumentException {
List<TaskSummary> results =
taskService.createTaskQuery().customAttributeIn(TaskCustomField.CUSTOM_9, "").list();
assertThat(results).hasSize(97);
assertThat(results).hasSize(98);
}
@WithAccessId(user = "admin")
@ -467,7 +467,7 @@ class QueryTasksAccTest extends AbstractAccTest {
.createTaskQuery()
.customAttributeIn(TaskCustomField.CUSTOM_9, "", null)
.list();
assertThat(results).hasSize(98);
assertThat(results).hasSize(99);
}
@WithAccessId(user = "admin")
@ -479,14 +479,14 @@ class QueryTasksAccTest extends AbstractAccTest {
.createTaskQuery()
.customAttributeNotIn(TaskCustomField.CUSTOM_9, new String[] {null})
.list();
assertThat(results).hasSize(98);
assertThat(results).hasSize(99);
results =
taskService
.createTaskQuery()
.customAttributeNotIn(TaskCustomField.CUSTOM_9, null, "custom9")
.list();
assertThat(results).hasSize(97);
assertThat(results).hasSize(98);
results =
taskService
@ -494,7 +494,7 @@ class QueryTasksAccTest extends AbstractAccTest {
.customAttributeNotIn(TaskCustomField.CUSTOM_9, new String[] {null})
.customAttributeNotIn(TaskCustomField.CUSTOM_10, "custom10")
.list();
assertThat(results).hasSize(97);
assertThat(results).hasSize(98);
}
@WithAccessId(user = "admin")

View File

@ -47,7 +47,7 @@ class QueryTasksByRoleAccTest extends AbstractAccTest {
switch (taskanaEngine.getCurrentUserContext().getUserid()) {
case "admin":
case "taskadmin":
expectedSize = 99;
expectedSize = 100;
break;
case "businessadmin":
case "monitor":
@ -57,7 +57,7 @@ class QueryTasksByRoleAccTest extends AbstractAccTest {
expectedSize = 26;
break;
case "user-1-1":
expectedSize = 9;
expectedSize = 10;
break;
case "user-taskrouter":
expectedSize = 0;

View File

@ -92,7 +92,7 @@ class QueryTasksByTimeIntervalsAccTest extends AbstractAccTest {
List<TaskSummary> results =
taskService.createTaskQuery().createdWithin(interval1).orderByCreated(asc).list();
assertThat(results).hasSize(61);
assertThat(results).hasSize(62);
TaskSummary previousSummary = null;
for (TaskSummary taskSummary : results) {
Instant cr = taskSummary.getCreated();

View File

@ -62,7 +62,7 @@ class QueryTasksListValuesAccTest extends AbstractAccTest {
.ownerLike("%user%")
.orderByClassificationName(ASCENDING)
.listValues(TaskQueryColumnName.CLASSIFICATION_NAME, null);
assertThat(columnValueList).hasSize(5);
assertThat(columnValueList).hasSize(6);
}
@WithAccessId(user = "admin")

View File

@ -30,9 +30,9 @@ class QueryTasksWithPaginationAccTest extends AbstractAccTest {
void testQueryAllPaged() {
TaskQuery taskQuery = taskanaEngine.getTaskService().createTaskQuery();
long numberOfTasks = taskQuery.count();
assertThat(numberOfTasks).isEqualTo(99);
assertThat(numberOfTasks).isEqualTo(100);
List<TaskSummary> tasks = taskQuery.orderByDue(DESCENDING).list();
assertThat(tasks).hasSize(99);
assertThat(tasks).hasSize(100);
List<TaskSummary> tasksp = taskQuery.orderByDue(DESCENDING).listPage(4, 5);
assertThat(tasksp).hasSize(5);
tasksp = taskQuery.orderByDue(DESCENDING).listPage(5, 5);

View File

@ -0,0 +1,655 @@
package acceptance.task.update;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import acceptance.AbstractAccTest;
import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList;
import org.assertj.core.api.Condition;
import org.assertj.core.api.ThrowableAssert.ThrowingCallable;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import pro.taskana.TaskanaConfiguration;
import pro.taskana.TaskanaConfiguration.Builder;
import pro.taskana.classification.api.ClassificationService;
import pro.taskana.classification.api.exceptions.ClassificationNotFoundException;
import pro.taskana.classification.api.models.Classification;
import pro.taskana.classification.api.models.ClassificationSummary;
import pro.taskana.classification.internal.models.ClassificationSummaryImpl;
import pro.taskana.common.api.exceptions.InvalidArgumentException;
import pro.taskana.common.test.security.JaasExtension;
import pro.taskana.common.test.security.WithAccessId;
import pro.taskana.task.api.TaskService;
import pro.taskana.task.api.exceptions.AttachmentPersistenceException;
import pro.taskana.task.api.models.Attachment;
import pro.taskana.task.api.models.AttachmentSummary;
import pro.taskana.task.api.models.Task;
import pro.taskana.task.api.models.TaskSummary;
import pro.taskana.task.internal.models.AttachmentImpl;
import pro.taskana.task.internal.models.TaskImpl;
@ExtendWith(JaasExtension.class)
public class UpdateTaskAttachmentWithWorkingDaysCalculationAccTest extends AbstractAccTest {
private Task task;
private Attachment attachment;
private ClassificationService classificationService;
@BeforeEach
@WithAccessId(user = "admin")
void setUp() throws Exception {
resetDb(false);
TaskanaConfiguration config =
new Builder(taskanaConfiguration).useWorkingTimeCalculation(false).build();
initTaskanaEngine(config);
classificationService = taskanaEngine.getClassificationService();
task =
taskService.getTask(
"TKI:000000000000000000000000000000000000"); // class T2000, prio 1, SL P1D
task.setClassificationKey("T2000");
attachment =
createExampleAttachment(
"DOCTYPE_DEFAULT", // prio 99, SL P2000D
createObjectReference(
"COMPANY_A",
"SYSTEM_B",
"INSTANCE_B",
"ArchiveId",
"12345678901234567890123456789012345678901234567890"),
"E-MAIL",
Instant.parse("2018-01-15T00:00:00Z"),
createSimpleCustomPropertyMap(3));
task.getAttachments().clear();
taskService.updateTask(task);
assertThat(task).isNotNull();
}
@WithAccessId(user = "user-1-1")
@Test
void should_UpdateTaskCorrectlyInDatabase_When_AddingAnAttachment() throws Exception {
final int attachmentCount = task.getAttachments().size();
assertThat(task.getPriority()).isEqualTo(1);
assertThat(task.getPlanned().plus(Duration.ofDays(1))).isEqualTo(task.getDue());
task.addAttachment(attachment);
task = taskService.updateTask(task);
task = taskService.getTask(task.getId());
assertThat(task.getAttachments())
.hasSize(attachmentCount + 1)
.contains(attachment)
.extracting(Attachment::getModified)
.containsExactlyInAnyOrder(task.getModified());
}
@WithAccessId(user = "user-1-1")
@Test
void should_UpdateTaskReceived_When_AddingAnAttachment() throws Exception {
task.addAttachment(attachment);
task = taskService.updateTask(task);
task = taskService.getTask(task.getId());
assertThat(task)
.extracting(TaskSummary::getReceived)
.isEqualTo(Instant.parse("2018-01-15T00:00:00Z"));
}
@WithAccessId(user = "user-1-1")
@Test
void should_NotUpdateTaskReceived_When_TaskAlreadyHasAReceived() throws Exception {
task.setReceived(Instant.parse("2019-09-13T08:44:17.588Z"));
task.addAttachment(attachment);
task = taskService.updateTask(task);
task = taskService.getTask(task.getId());
assertThat(task).extracting(TaskSummary::getReceived).isNotEqualTo(attachment.getReceived());
}
@WithAccessId(user = "user-1-1")
@Test
void should_UpdateTaskReceived_When_AddingTwoAttachments() throws Exception {
task.addAttachment(attachment);
Attachment attachment2 =
createExampleAttachment(
"L10303",
createObjectReference(
"COMPANY_B",
"SYSTEM_C",
"INSTANCE_C",
"ArchiveId",
"ABC45678901234567890123456789012345678901234567890"),
"ROHRPOST",
Instant.parse("2018-01-12T00:00:00Z"),
createSimpleCustomPropertyMap(4));
task.addAttachment(attachment2);
task = taskService.updateTask(task);
task = taskService.getTask(task.getId());
assertThat(task)
.extracting(TaskSummary::getReceived)
.isEqualTo(Instant.parse("2018-01-12T00:00:00Z"));
}
@WithAccessId(user = "user-1-1")
@Test
void should_NotAddSameAttachmentAgain_When_AddingToTaskSummary() throws Exception {
task.getAttachments().clear();
task = taskService.updateTask(task);
task = taskService.getTask(task.getId());
assertThat(task.getAttachments()).isEmpty();
AttachmentImpl attachment = (AttachmentImpl) this.attachment;
attachment.setId("TAI:000017");
task.addAttachment(attachment);
task.addAttachment(attachment);
task = taskService.updateTask(task);
assertThat(task.getAttachments())
.hasSize(1)
.extracting(AttachmentSummary::getModified)
.containsExactlyInAnyOrder(task.getModified());
}
@WithAccessId(user = "user-1-1")
@Test
void should_ThrowAttachmentPersistenceException_When_UpdatingTaskWithTwoIdenticalAttachments()
throws Exception {
final int attachmentCount = 0;
task.getAttachments().clear();
task = taskService.updateTask(task);
task = taskService.getTask(task.getId());
assertThat(task.getAttachments()).hasSize(attachmentCount);
AttachmentImpl attachment = (AttachmentImpl) this.attachment;
attachment.setId("TAI:000017");
task.getAttachments().add(attachment);
task.getAttachments().add(attachment);
ThrowingCallable call = () -> taskService.updateTask(task);
assertThatThrownBy(call).isInstanceOf(AttachmentPersistenceException.class);
}
@WithAccessId(user = "user-1-1")
@Test
void should_UpdateExistingAttachment_When_AddingSameButNotEqualAttachmentAgain()
throws Exception {
// Add attachment before
task = taskService.getTask(task.getId());
final int attachmentCount = task.getAttachments().size();
task.addAttachment(attachment);
task = taskService.updateTask(task);
task = taskService.getTask(task.getId());
assertThat(task.getAttachments()).hasSize(attachmentCount + 1);
// Change sth. and add same (id) again - override/update
String newChannel = "UPDATED EXTERNAL SINCE LAST ADD";
final int attachmentCount2 = task.getAttachments().size();
Attachment updatedAttachment = task.getAttachments().get(0);
updatedAttachment.setChannel(newChannel);
Classification newClassification =
taskanaEngine
.getClassificationService()
.getClassification("CLI:100000000000000000000000000000000013"); // Prio 99, P2000D
updatedAttachment.setClassificationSummary(newClassification.asSummary());
task.addAttachment(updatedAttachment);
task = taskService.updateTask(task);
task = taskService.getTask(task.getId());
assertThat(task.getAttachments()).hasSize(attachmentCount2);
assertThat(task.getAttachments().get(0).getChannel()).isEqualTo(newChannel);
assertThat(task.getPriority()).isEqualTo(99);
Instant expDue = workingTimeCalculator.addWorkingTime(task.getPlanned(), Duration.ofDays(1));
assertThat(task.getDue()).isEqualTo(expDue);
}
@WithAccessId(user = "user-1-1")
@Test
void should_NotUpdateExistingAttachment_When_AddingIdenticalAttachmentAgain() throws Exception {
// Add Attachment before
final int attachmentCount = task.getAttachments().size();
((AttachmentImpl) attachment).setId("TAI:0001");
task.addAttachment(attachment);
task.addAttachment(attachment); // overwrite, same id
task.addAttachment(attachment); // overwrite, same id
task = taskService.updateTask(task);
task = taskService.getTask(task.getId());
assertThat(task.getAttachments()).hasSize(attachmentCount + 1);
// Add same again - ignored
final int attachmentCount2 = task.getAttachments().size();
Attachment redundantAttachment = task.getAttachments().get(0);
task.addAttachment(redundantAttachment);
task = taskService.updateTask(task);
assertThat(task.getAttachments()).hasSize(attachmentCount2);
}
@WithAccessId(user = "user-1-1")
@Test
void testAddAttachmentAsNullValueWillBeIgnored() throws Exception {
// Try to add a single NULL-Element
final int attachmentCount = task.getAttachments().size();
task.addAttachment(null);
task = taskService.updateTask(task);
task = taskService.getTask(task.getId());
assertThat(task.getAttachments()).hasSize(attachmentCount);
// Try to set the Attachments to NULL and update it
((TaskImpl) task).setAttachments(null);
task = taskService.updateTask(task);
assertThat(task.getAttachments()).hasSize(attachmentCount); // locally, not inserted
task = taskService.getTask(task.getId());
assertThat(task.getAttachments()).hasSize(attachmentCount); // inserted values not changed
// Test no NullPointer on NULL-Value and removing it on current data.
// New loading can do this, but returned value should got this "function", too.
final int attachmentCount2 = task.getAttachments().size();
task.getAttachments().add(null);
task.getAttachments().add(null);
task.getAttachments().add(null);
task = taskService.updateTask(task);
assertThat(task.getAttachments()).hasSize(attachmentCount2); // locally, not inserted
task = taskService.getTask(task.getId());
assertThat(task.getAttachments()).hasSize(attachmentCount2); // inserted values not changed
assertThat(task.getPriority()).isEqualTo(1);
assertThat(task.getPlanned().plus(Duration.ofDays(1))).isEqualTo(task.getDue());
}
@WithAccessId(user = "user-1-1")
@Test
void testRemoveAttachment() throws Exception {
task.addAttachment(attachment);
task = taskService.updateTask(task);
assertThat(task.getPriority()).isEqualTo(99);
assertThat(task.getPlanned().plus(Duration.ofDays(1))).isEqualTo(task.getDue());
int attachmentCount = task.getAttachments().size();
Attachment attachmentToRemove = task.getAttachments().get(0);
task.removeAttachment(attachmentToRemove.getId());
task = taskService.updateTask(task);
assertThat(task.getAttachments())
.hasSize(attachmentCount - 1); // locally, removed and not inserted
task = taskService.getTask(task.getId());
assertThat(task.getAttachments()).hasSize(attachmentCount - 1); // inserted, values removed
assertThat(task.getPriority()).isEqualTo(1);
assertThat(task.getPlanned().plus(Duration.ofDays(1))).isEqualTo(task.getDue());
}
@WithAccessId(user = "user-1-1")
@Test
void testRemoveAttachmentWithNullAndNotAddedId() throws Exception {
task.addAttachment(attachment);
task = taskService.updateTask(task);
int attachmentCount = task.getAttachments().size();
task.removeAttachment(null);
task = taskService.updateTask(task);
assertThat(task.getAttachments()).hasSize(attachmentCount); // locally, nothing changed
task = taskService.getTask(task.getId());
assertThat(task.getAttachments()).hasSize(attachmentCount); // inserted, still same
task.removeAttachment("INVALID ID HERE");
task = taskService.updateTask(task);
assertThat(task.getAttachments()).hasSize(attachmentCount); // locally, nothing changed
task = taskService.getTask(task.getId());
assertThat(task.getAttachments()).hasSize(attachmentCount); // inserted, still same
}
@WithAccessId(user = "user-1-1")
@Test
void testUpdateAttachment() throws Exception {
((TaskImpl) task).setAttachments(new ArrayList<>());
task = taskService.updateTask(task);
assertThat(task.getPriority()).isEqualTo(1);
assertThat(task.getPlanned().plus(Duration.ofDays(1))).isEqualTo(task.getDue());
Attachment attachment = this.attachment;
task.addAttachment(attachment);
task = taskService.updateTask(task);
assertThat(task.getPriority()).isEqualTo(99);
assertThat(task.getPlanned().plus(Duration.ofDays(1))).isEqualTo(task.getDue());
final int attachmentCount = task.getAttachments().size();
String newChannel = attachment.getChannel() + "-X";
task.getAttachments().get(0).setChannel(newChannel);
Classification newClassification =
taskanaEngine
.getClassificationService()
.getClassification("CLI:100000000000000000000000000000000013"); // Prio 99, P2000D
task.getAttachments().get(0).setClassificationSummary(newClassification.asSummary());
task = taskService.updateTask(task);
task = taskService.getTask(task.getId());
assertThat(task.getAttachments()).hasSize(attachmentCount);
assertThat(task.getAttachments().get(0).getChannel()).isEqualTo(newChannel);
assertThat(task.getPriority()).isEqualTo(99);
Instant expDue = workingTimeCalculator.addWorkingTime(task.getPlanned(), Duration.ofDays(1));
assertThat(task.getDue()).isEqualTo(expDue);
}
@WithAccessId(user = "user-1-1")
@Test
void modifyExistingAttachment() throws Exception {
// setup test
assertThat(task.getAttachments()).isEmpty();
task.addAttachment(attachment);
Attachment attachment2 =
createExampleAttachment(
"L10303", // prio 101, SL PT7H
createObjectReference(
"COMPANY_B",
"SYSTEM_C",
"INSTANCE_C",
"ArchiveId",
"ABC45678901234567890123456789012345678901234567890"),
"ROHRPOST",
Instant.parse("2018-01-15T00:00:00Z"),
createSimpleCustomPropertyMap(4));
task.addAttachment(attachment2);
task = taskService.updateTask(task);
task = taskService.getTask(task.getId());
assertThat(task.getPriority()).isEqualTo(101);
Instant expDue = workingTimeCalculator.addWorkingTime(task.getPlanned(), Duration.ofDays(1));
assertThat(task.getDue()).isEqualTo(expDue);
assertThat(task.getAttachments())
.hasSize(2)
.areExactly(
1,
new Condition<>(
e -> "E-MAIL".equals(e.getChannel()) && e.getCustomAttributeMap().size() == 3,
"E-MAIL with 3 custom attributes"))
.areExactly(
1,
new Condition<>(
e -> "ROHRPOST".equals(e.getChannel()) && e.getCustomAttributeMap().size() == 4,
"ROHRPOST with 4 custom attributes"));
ClassificationSummary newClassificationSummary =
taskanaEngine
.getClassificationService()
.getClassification("CLI:100000000000000000000000000000000006") // Prio 5, SL P16D
.asSummary();
// modify existing attachment
for (Attachment att : task.getAttachments()) {
att.setClassificationSummary(newClassificationSummary);
if (att.getCustomAttributeMap().size() == 3) {
att.setChannel("FAX");
}
}
// modify existing attachment and task classification
task.setClassificationKey("DOCTYPE_DEFAULT"); // Prio 99, SL P2000D
task = taskService.updateTask(task);
task = taskService.getTask(task.getId());
assertThat(task.getPriority()).isEqualTo(99);
expDue = workingTimeCalculator.addWorkingTime(task.getPlanned(), Duration.ofDays(16));
assertThat(task.getDue()).isEqualTo(expDue);
assertThat(task.getAttachments())
.hasSize(2)
.areExactly(
1,
new Condition<>(
e -> "FAX".equals(e.getChannel()) && e.getCustomAttributeMap().size() == 3,
"FAX with 3 custom attributes"))
.areExactly(
1,
new Condition<>(
e -> "ROHRPOST".equals(e.getChannel()) && e.getCustomAttributeMap().size() == 4,
"ROHRPOST with 4 custom attributes"));
}
@WithAccessId(user = "user-1-1")
@Test
void replaceExistingAttachments() throws Exception {
// setup test
assertThat(task.getAttachments()).isEmpty();
task.addAttachment(attachment);
Attachment attachment2 =
createExampleAttachment(
"DOCTYPE_DEFAULT",
createObjectReference(
"COMPANY_B",
"SYSTEM_C",
"INSTANCE_C",
"ArchiveId",
"ABC45678901234567890123456789012345678901234567890"),
"E-MAIL",
Instant.parse("2018-01-15T00:00:00Z"),
createSimpleCustomPropertyMap(4));
task.addAttachment(attachment2);
task = taskService.updateTask(task);
task = taskService.getTask(task.getId());
assertThat(task.getAttachments()).hasSize(2);
assertThat(task.getAttachments().get(0).getClassificationSummary().getKey())
.isEqualTo("DOCTYPE_DEFAULT");
Attachment attachment3 =
createExampleAttachment(
"DOCTYPE_DEFAULT",
createObjectReference(
"COMPANY_C",
"SYSTEM_7",
"INSTANCE_7",
"ArchiveId",
"ABC4567890123456789012345678901234567890DEF"),
"DHL",
Instant.parse("2018-01-15T00:00:00Z"),
createSimpleCustomPropertyMap(4));
// replace existing attachments by new via addAttachment call
task.getAttachments().clear();
task.addAttachment(attachment3);
task = taskService.updateTask(task);
assertThat(task.getAttachments()).hasSize(1);
assertThat(task.getAttachments().get(0).getChannel()).isEqualTo("DHL");
task.getAttachments().forEach(at -> assertThat(task.getModified()).isEqualTo(at.getModified()));
// setup environment for 2nd version of replacement (list.add call)
task.getAttachments().add(attachment2);
task = taskService.updateTask(task);
assertThat(task.getAttachments()).hasSize(2);
assertThat(task.getAttachments().get(1).getChannel()).isEqualTo("E-MAIL");
// replace attachments
task.getAttachments().clear();
task.getAttachments().add(attachment3);
task = taskService.updateTask(task);
assertThat(task.getAttachments()).hasSize(1);
assertThat(task.getAttachments().get(0).getChannel()).isEqualTo("DHL");
}
@WithAccessId(user = "user-1-1")
@Test
void testPrioDurationOfTaskFromAttachmentsAtUpdate() throws Exception {
TaskService taskService = taskanaEngine.getTaskService();
Task newTask = taskService.newTask("USER-1-1", "DOMAIN_A");
newTask.setClassificationKey("L12010"); // prio 8, SL P7D
newTask.setPrimaryObjRef(
createObjectReference("COMPANY_A", "SYSTEM_A", "INSTANCE_A", "VNR", "1234567"));
newTask.addAttachment(
createExampleAttachment(
"DOCTYPE_DEFAULT", // prio 99, SL P2000D
createObjectReference(
"COMPANY_A",
"SYSTEM_B",
"INSTANCE_B",
"ArchiveId",
"12345678901234567890123456789012345678901234567890"),
"E-MAIL",
Instant.parse("2018-01-15T00:00:00Z"),
createSimpleCustomPropertyMap(3)));
newTask.addAttachment(
createExampleAttachment(
"L1060", // prio 1, SL P1D
createObjectReference(
"COMPANY_A",
"SYSTEM_B",
"INSTANCE_B",
"ArchiveId",
"12345678901234567890123456789012345678901234567890"),
"E-MAIL",
Instant.parse("2018-01-15T00:00:00Z"),
createSimpleCustomPropertyMap(3)));
Task createdTask = taskService.createTask(newTask);
assertThat(createdTask.getId()).isNotNull();
assertThat(createdTask.getCreator())
.isEqualTo(taskanaEngine.getCurrentUserContext().getUserid());
createdTask
.getAttachments()
.forEach(at -> assertThat(createdTask.getModified()).isEqualTo(at.getModified()));
Task readTask = taskService.getTask(createdTask.getId());
assertThat(readTask).isNotNull();
assertThat(createdTask.getCreator())
.isEqualTo(taskanaEngine.getCurrentUserContext().getUserid());
assertThat(readTask.getAttachments()).isNotNull();
assertThat(readTask.getAttachments()).hasSize(2);
assertThat(readTask.getAttachments().get(1).getCreated()).isNotNull();
assertThat(readTask.getAttachments().get(1).getModified()).isNotNull();
assertThat(readTask.getAttachments().get(0).getCreated())
.isEqualTo(readTask.getAttachments().get(1).getModified());
assertThat(readTask.getAttachments().get(0).getObjectReference()).isNotNull();
assertThat(readTask.getPriority()).isEqualTo(99);
Instant expDue =
workingTimeCalculator.addWorkingTime(readTask.getPlanned(), Duration.ofDays(1));
assertThat(readTask.getDue()).isEqualTo(expDue);
}
@WithAccessId(user = "user-1-1")
@Test
void testAddCustomAttributeToAttachment() throws Exception {
TaskService taskService = taskanaEngine.getTaskService();
task =
taskService.getTask(
"TKI:000000000000000000000000000000000000"); // class T2000, prio 1, SL P1D
attachment =
createExampleAttachment(
"DOCTYPE_DEFAULT", // prio 99, SL P2000D
createObjectReference(
"COMPANY_A",
"SYSTEM_B",
"INSTANCE_B",
"ArchiveId",
"12345678901234567890123456789012345678901234567890"),
"E-MAIL",
Instant.parse("2018-01-15T00:00:00Z"),
null);
attachment.getCustomAttributeMap().put("TEST_KEY", "TEST_VALUE");
task.addAttachment(attachment);
taskService.updateTask(task);
Task updatedTask = taskService.getTask("TKI:000000000000000000000000000000000000");
Attachment updatedAttachment =
updatedTask.getAttachments().stream()
.filter(a -> attachment.getId().equals(a.getId()))
.findFirst()
.orElse(null);
assertThat(updatedAttachment).isNotNull();
assertThat(updatedTask.getModified()).isEqualTo(updatedAttachment.getModified());
assertThat(updatedAttachment.getCustomAttributeMap()).containsEntry("TEST_KEY", "TEST_VALUE");
}
@WithAccessId(user = "user-1-1")
@Test
void should_ThrowException_When_UpdatingTaskWithNewAttachmentClassificationNull() {
attachment.setClassificationSummary(null);
task.addAttachment(attachment);
assertThatThrownBy(() -> taskService.updateTask(task))
.isInstanceOf(InvalidArgumentException.class)
.hasMessage("Classification of Attachment must not be null.");
}
@WithAccessId(user = "user-1-1")
@Test
void should_ThrowException_When_UpdatingTaskWithChangedAttachmentClassificationNull()
throws Exception {
task.addAttachment(attachment);
taskService.updateTask(task);
attachment.setClassificationSummary(null);
assertThatThrownBy(() -> taskService.updateTask(task))
.isInstanceOf(InvalidArgumentException.class)
.hasMessage("Classification of Attachment must not be null.");
}
@WithAccessId(user = "user-1-1")
@Test
void should_ThrowException_When_UpdatingTaskWithNewAttachmentObjectReferenceNull()
throws Exception {
task.addAttachment(attachment);
taskService.updateTask(task);
task.removeAttachment(attachment.getId());
attachment.setObjectReference(null);
task.addAttachment(attachment);
assertThatThrownBy(() -> taskService.updateTask(task))
.isInstanceOf(InvalidArgumentException.class)
.hasMessage("ObjectReference of Attachment must not be null.");
}
@WithAccessId(user = "user-1-1")
@Test
void should_ThrowException_When_UpdatingTaskWithChangedAttachmentObjectReferenceNull()
throws Exception {
task.addAttachment(attachment);
taskService.updateTask(task);
task.removeAttachment(attachment.getId());
attachment.setObjectReference(null);
task.addAttachment(attachment);
assertThatThrownBy(() -> taskService.updateTask(task))
.isInstanceOf(InvalidArgumentException.class)
.hasMessage("ObjectReference of Attachment must not be null.");
}
@WithAccessId(user = "user-1-1")
@Test
void should_ThrowException_When_UpdatingTaskWithMissingAttachmentClassificationKey() {
ClassificationSummaryImpl classification = new ClassificationSummaryImpl();
attachment.setClassificationSummary(classification);
task.addAttachment(attachment);
assertThatThrownBy(() -> taskService.updateTask(task))
.isInstanceOf(InvalidArgumentException.class)
.hasMessageContaining("ClassificationKey of Attachment must not be empty.");
}
@WithAccessId(user = "user-1-1")
@Test
void should_ThrowException_When_UpdatingTaskWithNotExistingAttachmentClassification() {
Classification classification =
classificationService.newClassification("NOT_EXISTING", "DOMAIN_A", "");
attachment.setClassificationSummary(classification);
task.addAttachment(attachment);
assertThatThrownBy(() -> taskService.updateTask(task))
.isInstanceOf(ClassificationNotFoundException.class);
}
@WithAccessId(user = "user-1-1")
@Test
void should_FetchAttachmentClassification_When_UpdatingTaskWithAttachments() throws Exception {
ClassificationSummary classification =
classificationService.newClassification("T2000", "DOMAIN_A", "").asSummary();
attachment.setClassificationSummary(classification);
task.addAttachment(attachment);
assertThat(classification.getServiceLevel()).isNull();
TaskImpl updatedTask = (TaskImpl) taskService.updateTask(task);
classification = updatedTask.getAttachments().get(0).getClassificationSummary();
assertThat(classification.getId()).isNotNull();
assertThat(classification.getDomain()).isNotNull();
assertThat(classification.getServiceLevel()).isNotNull();
}
}

View File

@ -185,6 +185,8 @@ class DeleteWorkbasketAccTest extends AbstractAccTest {
taskService.forceCompleteTask(task.getId());
task = (TaskImpl) taskService.getTask("TKI:200000000000000000000000000000000066");
taskService.forceCompleteTask(task.getId());
task = (TaskImpl) taskService.getTask("TKI:000000000000000000000000000000000201");
taskService.forceCompleteTask(task.getId());
boolean canBeDeletedNow = workbasketService.deleteWorkbasket(wb.getId());
assertThat(canBeDeletedNow).isFalse();