From ea23e8c363280498ff49708337bc97ba7862e7bf Mon Sep 17 00:00:00 2001 From: SebastianRoseneck <55637012+SebastianRoseneck@users.noreply.github.com> Date: Mon, 27 Dec 2021 11:07:34 +0100 Subject: [PATCH] TSK-1783: Updated CompleteTaskAccTest to use test-API --- .../task/complete/CompleteTaskAccTest.java | 643 ++++++++++++++++++ .../task/complete/CompleteTaskAccTest.java | 617 ----------------- 2 files changed, 643 insertions(+), 617 deletions(-) create mode 100644 lib/taskana-core-test/src/test/java/acceptance/task/complete/CompleteTaskAccTest.java delete mode 100644 lib/taskana-core/src/test/java/acceptance/task/complete/CompleteTaskAccTest.java diff --git a/lib/taskana-core-test/src/test/java/acceptance/task/complete/CompleteTaskAccTest.java b/lib/taskana-core-test/src/test/java/acceptance/task/complete/CompleteTaskAccTest.java new file mode 100644 index 000000000..087cfcd6e --- /dev/null +++ b/lib/taskana-core-test/src/test/java/acceptance/task/complete/CompleteTaskAccTest.java @@ -0,0 +1,643 @@ +package acceptance.task.complete; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.assertj.core.api.Assertions.catchThrowableOfType; +import static pro.taskana.testapi.DefaultTestEntities.defaultTestClassification; +import static pro.taskana.testapi.DefaultTestEntities.defaultTestWorkbasket; + +import java.time.Instant; +import java.time.temporal.ChronoUnit; +import java.util.Arrays; +import java.util.List; +import org.assertj.core.api.ThrowableAssert.ThrowingCallable; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestTemplate; + +import pro.taskana.classification.api.ClassificationService; +import pro.taskana.classification.api.models.ClassificationSummary; +import pro.taskana.common.api.BulkOperationResults; +import pro.taskana.common.api.exceptions.InvalidArgumentException; +import pro.taskana.common.api.exceptions.TaskanaException; +import pro.taskana.common.api.security.CurrentUserContext; +import pro.taskana.common.internal.util.EnumUtil; +import pro.taskana.task.api.TaskService; +import pro.taskana.task.api.TaskState; +import pro.taskana.task.api.exceptions.InvalidOwnerException; +import pro.taskana.task.api.exceptions.InvalidStateException; +import pro.taskana.task.api.exceptions.InvalidTaskStateException; +import pro.taskana.task.api.exceptions.TaskNotFoundException; +import pro.taskana.task.api.models.ObjectReference; +import pro.taskana.task.api.models.Task; +import pro.taskana.task.internal.builder.TaskBuilder; +import pro.taskana.testapi.DefaultTestEntities; +import pro.taskana.testapi.TaskanaInject; +import pro.taskana.testapi.TaskanaIntegrationTest; +import pro.taskana.testapi.security.WithAccessId; +import pro.taskana.workbasket.api.WorkbasketPermission; +import pro.taskana.workbasket.api.WorkbasketService; +import pro.taskana.workbasket.api.exceptions.MismatchedWorkbasketPermissionException; +import pro.taskana.workbasket.api.models.WorkbasketSummary; +import pro.taskana.workbasket.internal.builder.WorkbasketAccessItemBuilder; + +@TaskanaIntegrationTest +class CompleteTaskAccTest { + + @TaskanaInject TaskService taskService; + @TaskanaInject CurrentUserContext currentUserContext; + + ClassificationSummary defaultClassificationSummary; + WorkbasketSummary defaultWorkbasketSummary; + ObjectReference defaultObjectReference; + + @WithAccessId(user = "businessadmin") + @BeforeAll + void setup(ClassificationService classificationService, WorkbasketService workbasketService) + throws Exception { + defaultClassificationSummary = + defaultTestClassification().buildAndStoreAsSummary(classificationService); + defaultWorkbasketSummary = defaultTestWorkbasket().buildAndStoreAsSummary(workbasketService); + + WorkbasketAccessItemBuilder.newWorkbasketAccessItem() + .workbasketId(defaultWorkbasketSummary.getId()) + .accessId("user-1-1") + .permission(WorkbasketPermission.READ) + .permission(WorkbasketPermission.APPEND) + .buildAndStore(workbasketService); + + defaultObjectReference = DefaultTestEntities.defaultTestObjectReference().build(); + } + + @WithAccessId(user = "user-1-1") + @Test + void should_CompleteClaimedTask() throws Exception { + Task claimedTask = createTaskClaimedByUser_1_1().buildAndStore(taskService); + Instant before = Instant.now().truncatedTo(ChronoUnit.MILLIS); + + Task completedTask = taskService.completeTask(claimedTask.getId()); + + assertTaskIsComplete(before, completedTask); + } + + @WithAccessId(user = "admin") + @Test + void should_CompleteClaimedTaskByAnotherUser_When_UserIsAdmin() throws Exception { + Task claimedTask = createTaskClaimedByUser_1_1().buildAndStore(taskService); + Instant before = Instant.now().truncatedTo(ChronoUnit.MILLIS); + + Task completedTask = taskService.completeTask(claimedTask.getId()); + + assertTaskIsComplete(before, completedTask); + } + + @WithAccessId(user = "admin") + @WithAccessId(user = "taskadmin") + @TestTemplate + void should_ForceCompleteClaimedTask_When_NoExplicitPermissionsButUserIsInAdministrativeRole() + throws Exception { + Task claimedTask = createTaskClaimedByUser_1_1().buildAndStore(taskService); + Instant before = Instant.now().truncatedTo(ChronoUnit.MILLIS); + + Task completedTask = taskService.forceCompleteTask(claimedTask.getId()); + + assertTaskIsComplete(before, completedTask); + } + + @WithAccessId(user = "user-1-1") + @Test + void should_CompleteTaskTwice() throws Exception { + Task claimedTask = createTaskClaimedByUser_1_1().buildAndStore(taskService); + + Task completedTask = taskService.completeTask(claimedTask.getId()); + Task completedTask2 = taskService.completeTask(claimedTask.getId()); + + assertThat(completedTask2).isEqualTo(completedTask); + } + + @WithAccessId(user = "user-1-1") + @Test + void should_ForceCompleteClaimedTask_When_TaskCallerIsNotTheOwner() throws Exception { + Task task = createTaskClaimedByUser_1_1().owner("other").buildAndStore(taskService); + Instant before = Instant.now().truncatedTo(ChronoUnit.MILLIS); + + Task completedTask = taskService.forceCompleteTask(task.getId()); + + assertTaskIsComplete(before, completedTask); + } + + @WithAccessId(user = "user-1-1") + @Test + void should_ForceCompleteTask_When_TaskIsNotClaimed() throws Exception { + Task task = createDefaultTask().owner("other").buildAndStore(taskService); + Instant before = Instant.now().truncatedTo(ChronoUnit.MILLIS); + + Task completedTask = taskService.forceCompleteTask(task.getId()); + + assertTaskIsComplete(before, completedTask); + } + + @WithAccessId(user = "user-1-1") + @Test + void should_ThrowException_When_CompletingNonExistingTask() { + ThrowingCallable call = () -> taskService.completeTask("NOT_EXISTING"); + + assertThatThrownBy(call) + .isInstanceOf(TaskNotFoundException.class) + .extracting(TaskNotFoundException.class::cast) + .extracting(TaskNotFoundException::getTaskId) + .isEqualTo("NOT_EXISTING"); + } + + @WithAccessId(user = "user-1-2") + @WithAccessId(user = "user-taskrouter") + @TestTemplate + void should_ThrowException_When_UserIsNotAuthorizedOnTask() throws Exception { + Task claimedTask = createTaskClaimedByUser_1_1().buildAndStore(taskService, "admin"); + + ThrowingCallable call = () -> taskService.completeTask(claimedTask.getId()); + + MismatchedWorkbasketPermissionException e = + catchThrowableOfType(call, MismatchedWorkbasketPermissionException.class); + assertThat(e.getCurrentUserId()).isEqualTo(currentUserContext.getUserid()); + WorkbasketSummary workbasket = claimedTask.getWorkbasketSummary(); + assertThat(e.getWorkbasketId()).isEqualTo(workbasket.getId()); + assertThat(e.getRequiredPermissions()).containsExactly(WorkbasketPermission.READ); + } + + @WithAccessId(user = "user-1-1") + @Test + void should_ThrowException_When_TaskIsInStateReady() throws Exception { + Task task = createDefaultTask().buildAndStore(taskService); + + ThrowingCallable call = () -> taskService.completeTask(task.getId()); + + InvalidTaskStateException e = catchThrowableOfType(call, InvalidTaskStateException.class); + assertThat(e.getTaskId()).isEqualTo(task.getId()); + assertThat(e.getTaskState()).isEqualTo(task.getState()); + assertThat(e.getRequiredTaskStates()).containsExactlyInAnyOrder(TaskState.CLAIMED); + } + + @WithAccessId(user = "user-1-1") + @Test + void should_ThrowException_When_TaskCallerIsNotTheOwner() throws Exception { + Task task = createTaskClaimedByUser_1_1().owner("other").buildAndStore(taskService); + + ThrowingCallable call = () -> taskService.completeTask(task.getId()); + + InvalidOwnerException e = catchThrowableOfType(call, InvalidOwnerException.class); + assertThat(e.getCurrentUserId()).isEqualTo("user-1-1"); + assertThat(e.getTaskId()).isEqualTo(task.getId()); + } + + @WithAccessId(user = "user-1-1") + @Test + void should_ClaimTask_When_UsingDefaultFlag() throws Exception { + Task task = createDefaultTask().owner(null).buildAndStore(taskService); + Instant before = Instant.now().truncatedTo(ChronoUnit.MILLIS); + + Task claimedTask = taskService.claim(task.getId()); + + assertThat(claimedTask).isNotNull(); + assertThat(claimedTask.getOwner()).isEqualTo("user-1-1"); + assertThat(claimedTask.getState()).isEqualTo(TaskState.CLAIMED); + assertThat(claimedTask.isRead()).isTrue(); + assertThat(claimedTask.getClaimed()) + .isNotNull() + .isAfterOrEqualTo(before) + .isBeforeOrEqualTo(Instant.now()) + .isAfterOrEqualTo(claimedTask.getCreated()) + .isEqualTo(claimedTask.getModified()); + } + + @WithAccessId(user = "user-1-1") + @Test + void should_ForceClaimTask_When_TaskCallerIsNotTheOwner() throws Exception { + Task task = createDefaultTask().owner("other_user").buildAndStore(taskService); + Instant before = Instant.now().truncatedTo(ChronoUnit.MILLIS); + + Task claimedTask = taskService.forceClaim(task.getId()); + + assertThat(claimedTask).isNotNull(); + assertThat(claimedTask.getOwner()).isEqualTo("user-1-1"); + assertThat(claimedTask.getState()).isEqualTo(TaskState.CLAIMED); + assertThat(claimedTask.isRead()).isTrue(); + assertThat(claimedTask.getClaimed()) + .isNotNull() + .isAfterOrEqualTo(before) + .isBeforeOrEqualTo(Instant.now()) + .isAfterOrEqualTo(claimedTask.getCreated()) + .isEqualTo(claimedTask.getModified()); + } + + @WithAccessId(user = "user-1-1") + @Test + void should_ThrowException_When_ClaimingNonExistingTask() { + ThrowingCallable call = () -> taskService.claim("NOT_EXISTING"); + + assertThatThrownBy(call) + .isInstanceOf(TaskNotFoundException.class) + .extracting(TaskNotFoundException.class::cast) + .extracting(TaskNotFoundException::getTaskId) + .isEqualTo("NOT_EXISTING"); + } + + @WithAccessId(user = "user-1-1") + @Test + void should_ThrowException_When_ClaimingTaskInInvalidState() throws Exception { + Task task = + createDefaultTask() + .state(TaskState.COMPLETED) + .completed(Instant.now()) + .buildAndStore(taskService); + + ThrowingCallable call = () -> taskService.forceClaim(task.getId()); + + InvalidTaskStateException e = catchThrowableOfType(call, InvalidTaskStateException.class); + assertThat(e.getTaskId()).isEqualTo(task.getId()); + assertThat(e.getTaskState()).isEqualTo(task.getState()); + assertThat(e.getRequiredTaskStates()) + .containsExactlyInAnyOrder(EnumUtil.allValuesExceptFor(TaskState.END_STATES)); + } + + @WithAccessId(user = "user-1-1") + @Test + void should_ThrowException_When_ClaimingTaskWithInvalidOwner() throws Exception { + Task task = createTaskClaimedByUser_1_1().owner("user-1-2").buildAndStore(taskService); + + ThrowingCallable call = () -> taskService.claim(task.getId()); + + InvalidOwnerException e = catchThrowableOfType(call, InvalidOwnerException.class); + assertThat(e.getCurrentUserId()).isEqualTo("user-1-1"); + assertThat(e.getTaskId()).isEqualTo(task.getId()); + } + + @WithAccessId(user = "user-1-1") + @Test + void should_ThrowException_When_ForceCancelClaimTaskWithInvalidState() throws Exception { + Task task = + createDefaultTask() + .state(TaskState.COMPLETED) + .completed(Instant.now()) + .buildAndStore(taskService); + + ThrowingCallable call = () -> taskService.forceCancelClaim(task.getId()); + + InvalidTaskStateException e = catchThrowableOfType(call, InvalidTaskStateException.class); + assertThat(e.getTaskId()).isEqualTo(task.getId()); + assertThat(e.getTaskState()).isEqualTo(task.getState()); + assertThat(e.getRequiredTaskStates()) + .containsExactlyInAnyOrder(EnumUtil.allValuesExceptFor(TaskState.END_STATES)); + } + + @WithAccessId(user = "user-1-1") + @Test + void should_CancelClaim_When_UsingDefaultFlag() throws Exception { + Task task = createDefaultTask().buildAndStore(taskService); + + task = taskService.cancelClaim(task.getId()); + + assertThat(task).isNotNull(); + assertThat(task.getState()).isSameAs(TaskState.READY); + } + + @WithAccessId(user = "admin") + @Test + void should_ForceCancelClaimTask() throws Exception { + Task claimedTask = createTaskClaimedByUser_1_1().buildAndStore(taskService); + Instant before = Instant.now().truncatedTo(ChronoUnit.MILLIS); + + Task taskAfter = taskService.forceCancelClaim(claimedTask.getId()); + + assertThat(taskAfter).isNotNull(); + assertThat(taskAfter.getState()).isEqualTo(TaskState.READY); + assertThat(taskAfter.getClaimed()).isNull(); + assertThat(taskAfter.getModified()).isAfter(before); + assertThat(taskAfter.getOwner()).isNull(); + assertThat(taskAfter.isRead()).isTrue(); + } + + @WithAccessId(user = "user-1-1") + @Test + void should_ThrowException_When_CancelClaimWithInvalidOwner() throws Exception { + Task task = createTaskClaimedByUser_1_1().owner("user-1-2").buildAndStore(taskService); + + ThrowingCallable call = () -> taskService.cancelClaim(task.getId()); + + InvalidOwnerException e = catchThrowableOfType(call, InvalidOwnerException.class); + assertThat(e.getTaskId()).isEqualTo(task.getId()); + assertThat(e.getCurrentUserId()).isEqualTo("user-1-1"); + } + + @Test + void should_ThrowException_When_BulkCompleteWithNullList() { + ThrowingCallable call = () -> taskService.completeTasks(null); + + assertThatThrownBy(call).isInstanceOf(InvalidArgumentException.class); + } + + @WithAccessId(user = "user-1-1") + @Test + void should_CompleteAllTasks_When_BulkCompletingTasks() throws Exception { + Task claimedTask1 = createTaskClaimedByUser_1_1().buildAndStore(taskService); + Task claimedTask2 = createTaskClaimedByUser_1_1().buildAndStore(taskService); + List taskIdList = List.of(claimedTask1.getId(), claimedTask2.getId()); + Instant beforeBulkComplete = Instant.now().truncatedTo(ChronoUnit.MILLIS); + + BulkOperationResults results = taskService.completeTasks(taskIdList); + + assertThat(results.containsErrors()).isFalse(); + Task completedTask1 = taskService.getTask(claimedTask1.getId()); + assertTaskIsComplete(beforeBulkComplete, completedTask1); + Task completedTask2 = taskService.getTask(claimedTask2.getId()); + assertTaskIsComplete(beforeBulkComplete, completedTask2); + } + + @WithAccessId(user = "user-1-1") + @Test + void should_CompleteValidTasksEvenIfErrorsExist_When_BulkCompletingTasks() throws Exception { + Task claimedTask = createTaskClaimedByUser_1_1().buildAndStore(taskService); + List taskIdList = List.of("invalid-id", claimedTask.getId()); + final Instant beforeBulkComplete = Instant.now().truncatedTo(ChronoUnit.MILLIS); + + BulkOperationResults results = taskService.completeTasks(taskIdList); + + assertThat(results.containsErrors()).isTrue(); + assertThat(results.getErrorMap()).hasSize(1); + assertThat(results.getErrorForId("invalid-id")).isOfAnyClassIn(TaskNotFoundException.class); + Task completedTask = taskService.getTask(claimedTask.getId()); + assertTaskIsComplete(beforeBulkComplete, completedTask); + } + + @WithAccessId(user = "user-1-2") + @Test + void should_AddErrorsForInvalidTaskIds_When_BulkCompletingTasks() throws Exception { + String invalid1 = ""; + String invalid2 = null; + String invalid3 = "invalid-id"; + Task claimedTask = createTaskClaimedByUser_1_1().buildAndStore(taskService, "admin"); + String notAuthorized = claimedTask.getId(); + // we can't use List.of because of the null value we insert + List taskIdList = Arrays.asList(invalid1, invalid2, invalid3, notAuthorized); + + BulkOperationResults results = taskService.completeTasks(taskIdList); + + assertThat(results.containsErrors()).isTrue(); + assertThat(results.getFailedIds()) + .containsExactlyInAnyOrder(invalid1, invalid2, invalid3, notAuthorized); + assertThat(results.getErrorMap().values()).hasOnlyElementsOfType(TaskNotFoundException.class); + } + + @WithAccessId(user = "user-1-1") + @Test + void should_AddErrorForTaskWhichIsNotClaimed_When_BulkCompletingTasks() throws Exception { + Task task = createDefaultTask().buildAndStore(taskService); + List taskIdList = List.of(task.getId()); + + BulkOperationResults results = taskService.completeTasks(taskIdList); + + assertThat(results.containsErrors()).isTrue(); + assertThat(results.getFailedIds()).containsExactlyInAnyOrder(task.getId()); + assertThat(results.getErrorMap().values()).hasOnlyElementsOfType(InvalidStateException.class); + assertThat(results.getErrorForId(task.getId())) + .hasMessage( + "Task with id '%s' is in state: '%s', but must be in one of these states: '[%s]'", + task.getId(), TaskState.READY, TaskState.CLAIMED); + } + + @WithAccessId(user = "user-1-1") + @Test + void should_AddErrorForTasksInEndState_When_BulkCompletingTasks() throws Exception { + Task task1 = createDefaultTask().state(TaskState.CANCELLED).buildAndStore(taskService); + Task task2 = createDefaultTask().state(TaskState.TERMINATED).buildAndStore(taskService); + List taskIdList = List.of(task1.getId(), task2.getId()); + TaskState[] requiredStates = + EnumUtil.allValuesExceptFor(TaskState.TERMINATED, TaskState.CANCELLED); + + BulkOperationResults results = taskService.completeTasks(taskIdList); + + assertThat(results.containsErrors()).isTrue(); + assertThat(results.getFailedIds()).containsExactlyInAnyOrder(task1.getId(), task2.getId()); + assertThat(results.getErrorMap().values()).hasOnlyElementsOfType(InvalidStateException.class); + assertThat(results.getErrorForId(task1.getId())) + .hasMessage( + "Task with id '%s' is in state: '%s', but must be in one of these states: '%s'", + task1.getId(), TaskState.CANCELLED, Arrays.toString(requiredStates)); + assertThat(results.getErrorForId(task2.getId())) + .hasMessage( + "Task with id '%s' is in state: '%s', but must be in one of these states: '%s'", + task2.getId(), TaskState.TERMINATED, Arrays.toString(requiredStates)); + } + + @WithAccessId(user = "user-1-1") + @Test + void should_DoNothingForCompletedTask_When_BulkCompletingTasks() throws Exception { + Task task = + createDefaultTask() + .state(TaskState.COMPLETED) + .completed(Instant.now()) + .buildAndStore(taskService); + List taskIdList = List.of(task.getId()); + + Task before = taskService.getTask(task.getId()); + BulkOperationResults results = taskService.completeTasks(taskIdList); + Task after = taskService.getTask(task.getId()); + + assertThat(results.containsErrors()).isFalse(); + assertThat(before).isEqualTo(after); + } + + @WithAccessId(user = "user-1-1") + @Test + void should_AddErrorForTaskIfOwnerDoesNotMach_When_BulkCompletingTasks() throws Exception { + Task task = createTaskClaimedByUser_1_1().owner("user-1-2").buildAndStore(taskService); + List taskIdList = List.of(task.getId()); + + BulkOperationResults results = taskService.completeTasks(taskIdList); + + assertThat(results.containsErrors()).isTrue(); + assertThat(results.getFailedIds()).containsExactlyInAnyOrder(task.getId()); + assertThat(results.getErrorForId(task.getId())).isInstanceOf(InvalidOwnerException.class); + } + + @WithAccessId(user = "user-1-1") + @Test + void should_CompleteAllTasks_When_BulkForceCompletingTasks() throws Exception { + Task claimedTask1 = createTaskClaimedByUser_1_1().buildAndStore(taskService); + Task claimedTask2 = createTaskClaimedByUser_1_1().buildAndStore(taskService); + List taskIdList = List.of(claimedTask1.getId(), claimedTask2.getId()); + Instant beforeBulkComplete = Instant.now().truncatedTo(ChronoUnit.MILLIS); + + BulkOperationResults results = + taskService.forceCompleteTasks(taskIdList); + + assertThat(results.containsErrors()).isFalse(); + Task completedTask1 = taskService.getTask(claimedTask1.getId()); + assertTaskIsComplete(beforeBulkComplete, completedTask1); + Task completedTask2 = taskService.getTask(claimedTask2.getId()); + assertTaskIsComplete(beforeBulkComplete, completedTask2); + } + + @WithAccessId(user = "user-1-1") + @Test + void should_CompleteValidTasksEvenIfErrorsExist_When_BulkForceCompletingTasks() throws Exception { + Task claimedTask = createTaskClaimedByUser_1_1().buildAndStore(taskService); + List taskIdList = List.of("invalid-id", claimedTask.getId()); + final Instant beforeBulkComplete = Instant.now().truncatedTo(ChronoUnit.MILLIS); + + BulkOperationResults results = + taskService.forceCompleteTasks(taskIdList); + + assertThat(results.containsErrors()).isTrue(); + assertThat(results.getErrorMap()).hasSize(1); + assertThat(results.getErrorForId("invalid-id")).isOfAnyClassIn(TaskNotFoundException.class); + Task completedTask = taskService.getTask(claimedTask.getId()); + assertTaskIsComplete(beforeBulkComplete, completedTask); + } + + @WithAccessId(user = "user-1-2") + @Test + void should_AddErrorsForInvalidTaskIds_When_BulkForceCompletingTasks() throws Exception { + String invalid1 = ""; + String invalid2 = null; + String invalid3 = "invalid-id"; + Task claimedTask = createTaskClaimedByUser_1_1().buildAndStore(taskService, "admin"); + String notAuthorized = claimedTask.getId(); + // we can't use List.of because of the null value we insert + List taskIdList = Arrays.asList(invalid1, invalid2, invalid3, notAuthorized); + + BulkOperationResults results = + taskService.forceCompleteTasks(taskIdList); + + assertThat(results.containsErrors()).isTrue(); + assertThat(results.getFailedIds()) + .containsExactlyInAnyOrder(invalid1, invalid2, invalid3, notAuthorized); + assertThat(results.getErrorMap().values()).hasOnlyElementsOfType(TaskNotFoundException.class); + } + + @WithAccessId(user = "user-1-1") + @Test + void should_AddErrorForTasksInEndState_When_BulkForceCompletingTasks() throws Exception { + Task task1 = createDefaultTask().state(TaskState.CANCELLED).buildAndStore(taskService); + Task task2 = createDefaultTask().state(TaskState.TERMINATED).buildAndStore(taskService); + List taskIdList = List.of(task1.getId(), task2.getId()); + TaskState[] requiredStates = + EnumUtil.allValuesExceptFor(TaskState.TERMINATED, TaskState.CANCELLED); + + BulkOperationResults results = + taskService.forceCompleteTasks(taskIdList); + + assertThat(results.containsErrors()).isTrue(); + assertThat(results.getFailedIds()).containsExactlyInAnyOrder(task1.getId(), task2.getId()); + assertThat(results.getErrorMap().values()).hasOnlyElementsOfType(InvalidStateException.class); + assertThat(results.getErrorForId(task1.getId())) + .hasMessage( + "Task with id '%s' is in state: '%s', but must be in one of these states: '%s'", + task1.getId(), TaskState.CANCELLED, Arrays.toString(requiredStates)); + assertThat(results.getErrorForId(task2.getId())) + .hasMessage( + "Task with id '%s' is in state: '%s', but must be in one of these states: '%s'", + task2.getId(), TaskState.TERMINATED, Arrays.toString(requiredStates)); + } + + @WithAccessId(user = "user-1-1") + @Test + void should_DoNothingForCompletedTask_When_BulkForceCompletingTasks() throws Exception { + Task task = + createDefaultTask() + .state(TaskState.COMPLETED) + .completed(Instant.now()) + .buildAndStore(taskService); + List taskIdList = List.of(task.getId()); + + Task before = taskService.getTask(task.getId()); + BulkOperationResults results = + taskService.forceCompleteTasks(taskIdList); + Task after = taskService.getTask(task.getId()); + + assertThat(results.containsErrors()).isFalse(); + assertThat(before).isEqualTo(after); + } + + @WithAccessId(user = "user-1-2", groups = "user-1-1") // to read task + @Test + void should_CompleteTaskWhenAlreadyClaimedByDifferentUser_When_BulkForceCompletingTasks() + throws Exception { + Task claimedTask = createTaskClaimedByUser_1_1().buildAndStore(taskService); + List taskIdList = List.of(claimedTask.getId()); + final Instant beforeBulkComplete = Instant.now().truncatedTo(ChronoUnit.MILLIS); + + Task beforeComplete = taskService.getTask(claimedTask.getId()); + BulkOperationResults results = + taskService.forceCompleteTasks(taskIdList); + Task afterComplete = taskService.getTask(claimedTask.getId()); + + assertThat(results.containsErrors()).isFalse(); + assertTaskIsComplete(beforeBulkComplete, afterComplete); + assertThat(afterComplete.getClaimed()).isEqualTo(beforeComplete.getClaimed()); + } + + @WithAccessId(user = "user-1-1") + @Test + void should_ClaimTaskWhenNotClaimed_When_BulkForceCompletingTasks() throws Exception { + Task task = createDefaultTask().buildAndStore(taskService); + List taskIdList = List.of(task.getId()); + Instant beforeBulkComplete = Instant.now().truncatedTo(ChronoUnit.MILLIS); + + BulkOperationResults results = + taskService.forceCompleteTasks(taskIdList); + + assertThat(results.containsErrors()).isFalse(); + Task completedTask = taskService.getTask(task.getId()); + assertTaskIsComplete(beforeBulkComplete, completedTask); + } + + @WithAccessId(user = "user-1-1") + @Test + void should_OnlyClaimTasksWhichAreNotClaimed_When_BulkForceCompletingTasks() throws Exception { + Task task1 = createTaskClaimedByUser_1_1().owner("other").buildAndStore(taskService); + Task task2 = createDefaultTask().buildAndStore(taskService); + List taskIdList = List.of(task1.getId(), task2.getId()); + final Instant beforeBulkComplete = Instant.now().truncatedTo(ChronoUnit.MILLIS); + + BulkOperationResults results = + taskService.forceCompleteTasks(taskIdList); + + assertThat(results.containsErrors()).isFalse(); + + Task completedTask1 = taskService.getTask(task1.getId()); + + assertTaskIsComplete(beforeBulkComplete, completedTask1); + // do not update claimed timestamp for already claimed task + assertThat(completedTask1.getClaimed()).isBefore(beforeBulkComplete); + + Task completedTask2 = taskService.getTask(task2.getId()); + assertTaskIsComplete(beforeBulkComplete, completedTask2); + } + + private void assertTaskIsComplete(Instant before, Task completedTask) { + assertThat(completedTask).isNotNull(); + assertThat(completedTask.getState()).isEqualTo(TaskState.COMPLETED); + assertThat(completedTask.getOwner()).isEqualTo(currentUserContext.getUserid()); + assertThat(completedTask.getCompleted()) + .isNotNull() + .isEqualTo(completedTask.getModified()) + .isNotEqualTo(completedTask.getCreated()) + .isAfterOrEqualTo(before); + } + + private TaskBuilder createTaskClaimedByUser_1_1() { + return createDefaultTask() + .owner("user-1-1") + .created(Instant.parse("2018-01-29T15:55:00Z")) + .state(TaskState.CLAIMED) + .claimed(Instant.now()); + } + + private TaskBuilder createDefaultTask() { + return TaskBuilder.newTask() + .classificationSummary(defaultClassificationSummary) + .workbasketSummary(defaultWorkbasketSummary) + .primaryObjRef(defaultObjectReference); + } +} diff --git a/lib/taskana-core/src/test/java/acceptance/task/complete/CompleteTaskAccTest.java b/lib/taskana-core/src/test/java/acceptance/task/complete/CompleteTaskAccTest.java deleted file mode 100644 index 82ef9787c..000000000 --- a/lib/taskana-core/src/test/java/acceptance/task/complete/CompleteTaskAccTest.java +++ /dev/null @@ -1,617 +0,0 @@ -package acceptance.task.complete; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; - -import acceptance.AbstractAccTest; -import java.time.Instant; -import java.time.temporal.ChronoUnit; -import java.util.Arrays; -import java.util.List; -import org.assertj.core.api.ThrowableAssert.ThrowingCallable; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.TestTemplate; -import org.junit.jupiter.api.extension.ExtendWith; - -import pro.taskana.common.api.BulkOperationResults; -import pro.taskana.common.api.exceptions.InvalidArgumentException; -import pro.taskana.common.api.exceptions.NotAuthorizedException; -import pro.taskana.common.api.exceptions.TaskanaException; -import pro.taskana.common.internal.util.EnumUtil; -import pro.taskana.common.test.security.JaasExtension; -import pro.taskana.common.test.security.WithAccessId; -import pro.taskana.task.api.TaskState; -import pro.taskana.task.api.exceptions.InvalidOwnerException; -import pro.taskana.task.api.exceptions.InvalidStateException; -import pro.taskana.task.api.exceptions.TaskNotFoundException; -import pro.taskana.task.api.models.Task; -import pro.taskana.task.internal.models.TaskImpl; - -/** Acceptance tests for all claim and complete scenarios. */ -@ExtendWith(JaasExtension.class) -class CompleteTaskAccTest extends AbstractAccTest { - - @WithAccessId(user = "user-1-1") - @Test - void testCompleteTask() throws Exception { - assertThat(taskService.getTask("TKI:000000000000000000000000000000000001").getState()) - .isEqualTo(TaskState.CLAIMED); - Task completedTask = taskService.completeTask("TKI:000000000000000000000000000000000001"); - assertThat(completedTask).isNotNull(); - assertThat(completedTask.getCompleted()).isNotNull(); - assertThat(completedTask.getState()).isEqualTo(TaskState.COMPLETED); - assertThat(completedTask.getModified()).isNotEqualTo(completedTask.getCreated()); - } - - @WithAccessId(user = "admin") - @Test - void should_completeClaimedTaskByAnotherUser_When_UserIsAdmin() throws Exception { - assertThat(taskService.getTask("TKI:000000000000000000000000000000000029").getState()) - .isEqualTo(TaskState.CLAIMED); - Task completedTask = taskService.completeTask("TKI:000000000000000000000000000000000029"); - assertThat(completedTask).isNotNull(); - assertThat(completedTask.getCompleted()).isNotNull(); - assertThat(completedTask.getState()).isEqualTo(TaskState.COMPLETED); - assertThat(completedTask.getModified()).isNotEqualTo(completedTask.getCreated()); - } - - @WithAccessId(user = "admin") - @WithAccessId(user = "taskadmin") - @TestTemplate - void should_ForceCompleteTask_When_NoExplicitPermissionsButUserIsInAdministrativeRole() - throws Exception { - resetDb(false); - - assertThat(taskService.getTask("TKI:000000000000000000000000000000000000").getState()) - .isEqualTo(TaskState.CLAIMED); - Task completedTask = taskService.forceCompleteTask("TKI:000000000000000000000000000000000000"); - assertThat(completedTask).isNotNull(); - assertThat(completedTask.getCompleted()).isNotNull(); - assertThat(completedTask.getState()).isEqualTo(TaskState.COMPLETED); - assertThat(completedTask.getModified()).isNotEqualTo(completedTask.getCreated()); - } - - @WithAccessId(user = "user-1-1") - @Test - void testCompleteTaskTwice() throws Exception { - Task completedTask = taskService.completeTask("TKI:000000000000000000000000000000000002"); - Task completedTask2 = taskService.completeTask("TKI:000000000000000000000000000000000002"); - assertThat(completedTask2).isEqualTo(completedTask); - } - - @WithAccessId(user = "user-1-1") - @Test - void testForceCompleteAlreadyClaimed() throws Exception { - Task newTask = taskService.newTask("USER-1-1", "DOMAIN_A"); - newTask.setClassificationKey("T2100"); - newTask.setOwner("other"); - newTask.setPrimaryObjRef( - createObjectReference("COMPANY_A", "SYSTEM_A", "INSTANCE_A", "VNR", "1234567")); - TaskImpl newTaskImpl = (TaskImpl) newTask; - newTaskImpl.setState(TaskState.CLAIMED); - newTaskImpl.setClaimed(Instant.now()); - - Task createdTask = taskService.createTask(newTaskImpl); - Task completedTask = taskService.forceCompleteTask(createdTask.getId()); - - assertThat(completedTask.getState()).isEqualTo(TaskState.COMPLETED); - assertThat(completedTask.getCompleted()).isNotNull(); - assertThat(completedTask.getCreated()).isBeforeOrEqualTo(completedTask.getModified()); - assertThat(completedTask.getCompleted()).isEqualTo(completedTask.getModified()); - } - - @WithAccessId(user = "user-1-1") - @Test - void testForceCompleteNotClaimed() throws Exception { - Task newTask = taskService.newTask("USER-1-1", "DOMAIN_A"); - newTask.setClassificationKey("T2100"); - newTask.setOwner("other"); - newTask.setPrimaryObjRef( - createObjectReference("COMPANY_A", "SYSTEM_A", "INSTANCE_A", "VNR", "1234567")); - TaskImpl newTaskImpl = (TaskImpl) newTask; - newTaskImpl.setClaimed(Instant.now()); - - Task createdTask = taskService.createTask(newTaskImpl); - Task completedTask = taskService.forceCompleteTask(createdTask.getId()); - - assertThat(completedTask.getState()).isEqualTo(TaskState.COMPLETED); - assertThat(completedTask.getCompleted()).isNotNull(); - assertThat(completedTask.getCreated()).isBeforeOrEqualTo(completedTask.getModified()); - assertThat(completedTask.getCompleted()).isEqualTo(completedTask.getModified()); - } - - @WithAccessId(user = "user-1-1") - @Test - void should_ThrowException_When_TaskIsNotFound() { - ThrowingCallable call = - () -> taskService.completeTask("TKI:0000000000000000000000000000000000xx"); - assertThatThrownBy(call).isInstanceOf(TaskNotFoundException.class); - } - - @WithAccessId(user = "user-taskrouter") - @Test - void should_ThrowException_When_UserIsNotAuthorized() { - ThrowingCallable call = - () -> taskService.completeTask("TKI:000000000000000000000000000000000001"); - assertThatThrownBy(call).isInstanceOf(NotAuthorizedException.class); - } - - @WithAccessId(user = "user-1-1") - @Test - void should_ThrowException_When_UserIsNotAuthorizedOnTask() { - ThrowingCallable call = - () -> taskService.completeTask("TKI:000000000000000000000000000000000004"); - assertThatThrownBy(call).isInstanceOf(NotAuthorizedException.class); - } - - @WithAccessId(user = "user-1-2") - @Test - void should_ThrowException_When_TaskIsInStateReady() { - ThrowingCallable call = - () -> taskService.completeTask("TKI:000000000000000000000000000000000025"); - assertThatThrownBy(call).isInstanceOf(InvalidStateException.class); - } - - @WithAccessId(user = "user-1-2") - @Test - void should_ThrowException_When_TaskCallerIsNotTheOwner() { - ThrowingCallable call = - () -> taskService.completeTask("TKI:000000000000000000000000000000000026"); - assertThatThrownBy(call).isInstanceOf(InvalidOwnerException.class); - } - - @WithAccessId(user = "user-1-1") - @Test - void testClaimTaskWithDefaultFlag() throws Exception { - Task newTask = taskService.newTask("USER-1-1", "DOMAIN_A"); - newTask.setClassificationKey("T2100"); - newTask.setPrimaryObjRef( - createObjectReference("COMPANY_A", "SYSTEM_A", "INSTANCE_A", "VNR", "1234567")); - newTask.setOwner(null); - Task createdTask = taskService.createTask(newTask); - - assertThat(createdTask).isNotNull(); - assertThat(createdTask.getClaimed()).isNull(); - - final Instant before = createdTask.getCreated(); - Task claimedTask = taskService.claim(createdTask.getId()); - - assertThat(claimedTask.getOwner()).isNotNull(); - assertThat(taskanaEngine.getCurrentUserContext().getUserid()).isEqualTo(claimedTask.getOwner()); - assertThat(claimedTask.getClaimed()).isNotNull(); - assertThat(before).isBeforeOrEqualTo(claimedTask.getClaimed()); - assertThat(claimedTask.getCreated()).isBeforeOrEqualTo(claimedTask.getClaimed()); - assertThat(claimedTask.getClaimed()).isBeforeOrEqualTo(Instant.now()); - assertThat(claimedTask.getModified()).isEqualTo(claimedTask.getClaimed()); - } - - @WithAccessId(user = "user-1-1") - @Test - void testForceClaimTaskFromOtherUser() throws Exception { - Task newTask = taskService.newTask("USER-1-1", "DOMAIN_A"); - newTask.setClassificationKey("T2100"); - newTask.setPrimaryObjRef( - createObjectReference("COMPANY_A", "SYSTEM_A", "INSTANCE_A", "VNR", "1234567")); - newTask.setOwner("other_user"); - Task createdTask = taskService.createTask(newTask); - - assertThat(createdTask).isNotNull(); - assertThat(createdTask.getOwner()).isEqualTo("other_user"); - - Instant beforeForceClaim = Instant.now().truncatedTo(ChronoUnit.MILLIS); - Task taskAfterClaim = taskService.forceClaim(createdTask.getId()); - - assertThat(taskAfterClaim.getOwner()) - .isEqualTo(taskanaEngine.getCurrentUserContext().getUserid()); - assertThat(beforeForceClaim) - .isBeforeOrEqualTo(taskAfterClaim.getModified()) - .isBeforeOrEqualTo(taskAfterClaim.getClaimed()); - assertThat(taskAfterClaim.getCreated()).isBeforeOrEqualTo(taskAfterClaim.getModified()); - - assertThat(taskAfterClaim.getState()).isEqualTo(TaskState.CLAIMED); - assertThat(taskAfterClaim.isRead()).isTrue(); - } - - @WithAccessId(user = "user-1-1") - @Test - void testClaimTaskNotExisting() { - assertThatThrownBy(() -> taskService.claim("NOT_EXISTING")) - .isInstanceOf(TaskNotFoundException.class); - } - - @WithAccessId(user = "user-1-2") - @Test - void testClaimTaskWithInvalidState() { - assertThatThrownBy(() -> taskService.forceClaim("TKI:000000000000000000000000000000000036")) - .isInstanceOf(InvalidStateException.class); - } - - @WithAccessId(user = "user-1-2") - @Test - void testClaimTaskWithInvalidOwner() { - assertThatThrownBy(() -> taskService.claim("TKI:000000000000000000000000000000000035")) - .isInstanceOf(InvalidOwnerException.class); - } - - @WithAccessId(user = "user-1-2") - @Test - void testCancelClaimForcedWithInvalidState() { - ThrowingCallable call = - () -> taskService.forceCancelClaim("TKI:000000000000000000000000000000000036"); - assertThatThrownBy(call).isInstanceOf(InvalidStateException.class); - } - - @WithAccessId(user = "user-1-1") - @Test - void testCancelClaimDefaultFlag() throws Exception { - Task newTask = taskService.newTask("USER-1-1", "DOMAIN_A"); - newTask.setClassificationKey("T2100"); - newTask.setPrimaryObjRef( - createObjectReference("COMPANY_A", "SYSTEM_A", "INSTANCE_A", "VNR", "1234567")); - Task createdTask = taskService.createTask(newTask); - - assertThat(createdTask).isNotNull(); - assertThat(createdTask.getState()).isSameAs(TaskState.READY); - - createdTask = taskService.cancelClaim(createdTask.getId()); - - assertThat(createdTask).isNotNull(); - assertThat(createdTask.getState()).isSameAs(TaskState.READY); - } - - @WithAccessId(user = "admin") - @Test - void testForceCancelClaimSuccessfull() throws Exception { - Task taskBefore = taskService.getTask("TKI:000000000000000000000000000000000043"); - - assertThat(taskBefore).isNotNull(); - assertThat(taskBefore.getState()).isEqualTo(TaskState.CLAIMED); - - final Instant before = Instant.now(); - Thread.sleep(1); - Task taskAfter = taskService.forceCancelClaim("TKI:000000000000000000000000000000000043"); - - assertThat(taskAfter).isNotNull(); - assertThat(taskAfter.getState()).isEqualTo(TaskState.READY); - assertThat(taskAfter.getClaimed()).isNull(); - assertThat(taskAfter.getModified().isAfter(before)).isTrue(); - assertThat(taskAfter.getOwner()).isNull(); - assertThat(taskAfter.isRead()).isTrue(); - } - - @WithAccessId(user = "user-1-2") - @Test - void testCancelClaimWithInvalidOwner() { - assertThatThrownBy(() -> taskService.cancelClaim("TKI:000000000000000000000000000000000035")) - .isInstanceOf(InvalidOwnerException.class); - } - - @Test - void should_ThrowException_When_BulkCompleteWithNullList() { - assertThatThrownBy(() -> taskService.completeTasks(null)) - .isInstanceOf(InvalidArgumentException.class); - } - - @WithAccessId(user = "user-1-2") - @Test - void should_CompleteAllTasks_When_BulkCompletingTasks() throws Exception { - String id1 = "TKI:000000000000000000000000000000000102"; - String id2 = "TKI:000000000000000000000000000000000101"; - List taskIdList = List.of(id1, id2); - - Instant beforeBulkComplete = Instant.now().truncatedTo(ChronoUnit.MILLIS); - BulkOperationResults results = taskService.completeTasks(taskIdList); - - assertThat(results.containsErrors()).isFalse(); - - Task completedTask1 = taskService.getTask(id1); - assertThat(completedTask1.getState()).isEqualTo(TaskState.COMPLETED); - assertThat(completedTask1.getCompleted()) - .isEqualTo(completedTask1.getModified()) - .isAfterOrEqualTo(beforeBulkComplete); - assertThat(completedTask1.getOwner()).isEqualTo("user-1-2"); - - Task completedTask2 = taskService.getTask(id2); - assertThat(completedTask2.getState()).isEqualTo(TaskState.COMPLETED); - assertThat(completedTask2.getCompleted()) - .isEqualTo(completedTask2.getModified()) - .isAfterOrEqualTo(beforeBulkComplete); - assertThat(completedTask2.getOwner()).isEqualTo("user-1-2"); - } - - @WithAccessId(user = "user-1-2") - @Test - void should_CompleteValidTasksEvenIfErrorsExist_When_BulkCompletingTasks() throws Exception { - String invalid = "invalid-id"; - String validId = "TKI:000000000000000000000000000000000103"; - List taskIdList = List.of(invalid, validId); - - Instant beforeBulkComplete = Instant.now().truncatedTo(ChronoUnit.MILLIS); - BulkOperationResults results = taskService.completeTasks(taskIdList); - - assertThat(results.containsErrors()).isTrue(); - Task completedTask = taskService.getTask(validId); - assertThat(completedTask.getState()).isEqualTo(TaskState.COMPLETED); - assertThat(completedTask.getCompleted()) - .isEqualTo(completedTask.getModified()) - .isAfterOrEqualTo(beforeBulkComplete); - assertThat(completedTask.getOwner()).isEqualTo("user-1-2"); - } - - @WithAccessId(user = "user-1-2") - @Test - void should_AddErrorsForInvalidTaskIds_When_BulkCompletingTasks() throws Exception { - String invalid1 = ""; - String invalid2 = null; - String invalid3 = "invalid-id"; - String notAuthorized = "TKI:000000000000000000000000000000000002"; - // we can't use List.of because of the null value we insert - List taskIdList = Arrays.asList(invalid1, invalid2, invalid3, notAuthorized); - - BulkOperationResults results = taskService.completeTasks(taskIdList); - - assertThat(results.containsErrors()).isTrue(); - assertThat(results.getFailedIds()) - .containsExactlyInAnyOrder(invalid1, invalid2, invalid3, notAuthorized); - assertThat(results.getErrorMap().values()).hasOnlyElementsOfType(TaskNotFoundException.class); - } - - @WithAccessId(user = "user-1-2") - @Test - void should_AddErrorForTaskWhichIsNotClaimed_When_BulkCompletingTasks() throws Exception { - String id = "TKI:000000000000000000000000000000000025"; // task is not claimed - List taskIdList = List.of(id); - - BulkOperationResults results = taskService.completeTasks(taskIdList); - - assertThat(results.containsErrors()).isTrue(); - assertThat(results.getFailedIds()).containsExactlyInAnyOrder(id); - assertThat(results.getErrorMap().values()).hasOnlyElementsOfType(InvalidStateException.class); - assertThat(results.getErrorForId(id)) - .hasMessage( - "Task with id '%s' is in state: '%s', but must be in one of these states: '[%s]'", - id, TaskState.READY, TaskState.CLAIMED); - } - - @WithAccessId(user = "user-1-2") - @Test - void should_AddErrorForTasksInEndState_When_BulkCompletingTasks() throws Exception { - String id1 = "TKI:300000000000000000000000000000000000"; // task is canceled - String id2 = "TKI:300000000000000000000000000000000010"; // task is terminated - List taskIdList = List.of(id1, id2); - TaskState[] requiredStates = - EnumUtil.allValuesExceptFor(TaskState.TERMINATED, TaskState.CANCELLED); - - BulkOperationResults results = taskService.completeTasks(taskIdList); - - assertThat(results.containsErrors()).isTrue(); - assertThat(results.getFailedIds()).containsExactlyInAnyOrder(id1, id2); - assertThat(results.getErrorMap().values()).hasOnlyElementsOfType(InvalidStateException.class); - assertThat(results.getErrorForId(id1)) - .hasMessage( - "Task with id '%s' is in state: '%s', but must be in one of these states: '%s'", - id1, TaskState.CANCELLED, Arrays.toString(requiredStates)); - assertThat(results.getErrorForId(id2)) - .hasMessage( - "Task with id '%s' is in state: '%s', but must be in one of these states: '%s'", - id2, TaskState.TERMINATED, Arrays.toString(requiredStates)); - } - - @WithAccessId(user = "user-1-2") - @Test - void should_DoNothingForCompletedTask_When_BulkCompletingTasks() throws Exception { - String id = "TKI:000000000000000000000000000000000036"; // task is completed - List taskIdList = List.of(id); - - Task before = taskService.getTask(id); - BulkOperationResults results = taskService.completeTasks(taskIdList); - Task after = taskService.getTask(id); - - assertThat(results.containsErrors()).isFalse(); - assertThat(before).isEqualTo(after); - } - - @WithAccessId(user = "user-1-2") - @Test - void should_AddErrorForTaskIfOwnerDoesNotMach_When_BulkCompletingTasks() throws Exception { - String id1 = "TKI:000000000000000000000000000000000035"; - List taskIdList = List.of(id1); - - BulkOperationResults results = taskService.completeTasks(taskIdList); - - assertThat(results.containsErrors()).isTrue(); - assertThat(results.getFailedIds()).containsExactlyInAnyOrder(id1); - assertThat(results.getErrorForId(id1)).isInstanceOf(InvalidOwnerException.class); - } - - @WithAccessId(user = "user-1-2") - @Test - void should_CompleteAllTasks_When_BulkForceCompletingTasks() throws Exception { - String id1 = "TKI:000000000000000000000000000000000026"; - String id2 = "TKI:000000000000000000000000000000000027"; - List taskIdList = List.of(id1, id2); - - Instant beforeBulkComplete = Instant.now().truncatedTo(ChronoUnit.MILLIS); - BulkOperationResults results = - taskService.forceCompleteTasks(taskIdList); - - assertThat(results.containsErrors()).isFalse(); - - Task completedTask1 = taskService.getTask(id1); - assertThat(completedTask1.getState()).isEqualTo(TaskState.COMPLETED); - assertThat(completedTask1.getCompleted()) - .isEqualTo(completedTask1.getModified()) - .isAfterOrEqualTo(beforeBulkComplete); - assertThat(completedTask1.getOwner()).isEqualTo("user-1-2"); - - Task completedTask2 = taskService.getTask(id2); - assertThat(completedTask2.getState()).isEqualTo(TaskState.COMPLETED); - assertThat(completedTask2.getCompleted()) - .isEqualTo(completedTask2.getModified()) - .isAfterOrEqualTo(beforeBulkComplete); - assertThat(completedTask2.getOwner()).isEqualTo("user-1-2"); - } - - @WithAccessId(user = "user-1-2") - @Test - void should_CompleteValidTasksEvenIfErrorsExist_When_BulkForceCompletingTasks() throws Exception { - String invalid = "invalid-id"; - String validId = "TKI:000000000000000000000000000000000028"; - List taskIdList = List.of(invalid, validId); - - Instant beforeBulkComplete = Instant.now().truncatedTo(ChronoUnit.MILLIS); - BulkOperationResults results = - taskService.forceCompleteTasks(taskIdList); - - assertThat(results.containsErrors()).isTrue(); - Task completedTask = taskService.getTask(validId); - assertThat(completedTask.getState()).isEqualTo(TaskState.COMPLETED); - assertThat(completedTask.getCompleted()) - .isEqualTo(completedTask.getModified()) - .isAfterOrEqualTo(beforeBulkComplete); - assertThat(completedTask.getOwner()).isEqualTo("user-1-2"); - } - - @WithAccessId(user = "user-1-2") - @Test - void should_AddErrorsForInvalidTaskIds_When_BulkForceCompletingTasks() throws Exception { - String invalid1 = ""; - String invalid2 = null; - String invalid3 = "invalid-id"; - String notAuthorized = "TKI:000000000000000000000000000000000002"; - // we can't use List.of because of the null value we insert - List taskIdList = Arrays.asList(invalid1, invalid2, invalid3, notAuthorized); - - BulkOperationResults results = - taskService.forceCompleteTasks(taskIdList); - - assertThat(results.containsErrors()).isTrue(); - assertThat(results.getFailedIds()) - .containsExactlyInAnyOrder(invalid1, invalid2, invalid3, notAuthorized); - assertThat(results.getErrorMap().values()).hasOnlyElementsOfType(TaskNotFoundException.class); - } - - @WithAccessId(user = "user-1-2") - @Test - void should_AddErrorForTasksInEndState_When_BulkForceCompletingTasks() throws Exception { - String id1 = "TKI:300000000000000000000000000000000000"; // task is canceled - String id2 = "TKI:300000000000000000000000000000000010"; // task is terminated - List taskIdList = List.of(id1, id2); - TaskState[] requiredStates = - EnumUtil.allValuesExceptFor(TaskState.TERMINATED, TaskState.CANCELLED); - - BulkOperationResults results = - taskService.forceCompleteTasks(taskIdList); - - assertThat(results.containsErrors()).isTrue(); - assertThat(results.getFailedIds()).containsExactlyInAnyOrder(id1, id2); - assertThat(results.getErrorMap().values()).hasOnlyElementsOfType(InvalidStateException.class); - assertThat(results.getErrorForId(id1)) - .hasMessage( - "Task with id '%s' is in state: '%s', but must be in one of these states: '%s'", - id1, TaskState.CANCELLED, Arrays.toString(requiredStates)); - assertThat(results.getErrorForId(id2)) - .hasMessage( - "Task with id '%s' is in state: '%s', but must be in one of these states: '%s'", - id2, TaskState.TERMINATED, Arrays.toString(requiredStates)); - } - - @WithAccessId(user = "user-1-2") - @Test - void should_DoNothingForCompletedTask_When_BulkForceCompletingTasks() throws Exception { - String id = "TKI:000000000000000000000000000000000036"; // task is completed - List taskIdList = List.of(id); - - Task before = taskService.getTask(id); - BulkOperationResults results = - taskService.forceCompleteTasks(taskIdList); - Task after = taskService.getTask(id); - - assertThat(results.containsErrors()).isFalse(); - assertThat(before).isEqualTo(after); - } - - @WithAccessId(user = "user-1-2", groups = "user-1-1") // to read task - @Test - void should_CompleteTaskWhenAlreadyClaimedByDifferentUser_When_BulkForceCompletingTasks() - throws Exception { - String id = "TKI:000000000000000000000000000000000002"; - List taskIdList = List.of(id); - - Task beforeClaim = taskService.getTask(id); - assertThat(beforeClaim.getOwner()).isNotEqualTo("user-1-2"); - final Instant beforeBulkComplete = Instant.now().truncatedTo(ChronoUnit.MILLIS); - BulkOperationResults results = - taskService.forceCompleteTasks(taskIdList); - Task afterClaim = taskService.getTask(id); - - assertThat(results.containsErrors()).isFalse(); - assertThat(afterClaim.getState()).isEqualTo(TaskState.COMPLETED); - assertThat(afterClaim.getClaimed()).isEqualTo(beforeClaim.getClaimed()); - assertThat(afterClaim.getCompleted()) - .isEqualTo(afterClaim.getModified()) - .isAfterOrEqualTo(beforeBulkComplete); - assertThat(afterClaim.getOwner()).isEqualTo("user-1-2"); - } - - @WithAccessId(user = "user-1-2") - @Test - void should_ClaimTaskWhenNotClaimed_When_BulkForceCompletingTasks() throws Exception { - String id = "TKI:000000000000000000000000000000000033"; - List taskIdList = List.of(id); - - Task task = taskService.getTask(id); - assertThat(task.getState()).isSameAs(TaskState.READY); - assertThat(task.getClaimed()).isNull(); - - final Instant beforeBulkComplete = Instant.now().truncatedTo(ChronoUnit.MILLIS); - - BulkOperationResults results = - taskService.forceCompleteTasks(taskIdList); - - assertThat(results.containsErrors()).isFalse(); - task = taskService.getTask(id); - assertThat(task.getState()).isEqualTo(TaskState.COMPLETED); - assertThat(task.getCompleted()) - .isEqualTo(task.getClaimed()) - .isEqualTo(task.getModified()) - .isAfterOrEqualTo(beforeBulkComplete); - assertThat(task.getOwner()).isEqualTo("user-1-2"); - } - - @WithAccessId(user = "user-b-2") - @Test - void should_OnlyClaimTasksWhichAreNotClaimed_When_BulkForceCompletingTasks() throws Exception { - String id1 = "TKI:000000000000000000000000000000000043"; // task is already claimed - String id2 = "TKI:000000000000000000000000000000000044"; // task is ready - List taskIdList = List.of(id1, id2); - - Task task = taskService.getTask(id2); - assertThat(task.getState()).isSameAs(TaskState.READY); - assertThat(task.getClaimed()).isNull(); - - final Instant beforeBulkComplete = Instant.now().truncatedTo(ChronoUnit.MILLIS); - - BulkOperationResults results = - taskService.forceCompleteTasks(taskIdList); - - assertThat(results.containsErrors()).isFalse(); - - task = taskService.getTask(id1); - assertThat(task.getState()).isEqualTo(TaskState.COMPLETED); - // do not update claimed timestamp for already claimed task - assertThat(task.getClaimed()).isBefore(beforeBulkComplete); - assertThat(task.getCompleted()) - .isEqualTo(task.getModified()) - .isAfterOrEqualTo(beforeBulkComplete); - assertThat(task.getOwner()).isEqualTo("user-b-2"); - - task = taskService.getTask(id2); - assertThat(task.getState()).isEqualTo(TaskState.COMPLETED); - assertThat(task.getCompleted()) - .isEqualTo(task.getClaimed()) - .isEqualTo(task.getModified()) - .isAfterOrEqualTo(beforeBulkComplete); - assertThat(task.getOwner()).isEqualTo("user-b-2"); - } -}