diff --git a/lib/taskana-core-test/src/test/java/acceptance/task/complete/CancelTaskAccTest.java b/lib/taskana-core-test/src/test/java/acceptance/task/complete/CancelTaskAccTest.java new file mode 100644 index 000000000..99ba22a41 --- /dev/null +++ b/lib/taskana-core-test/src/test/java/acceptance/task/complete/CancelTaskAccTest.java @@ -0,0 +1,183 @@ +package acceptance.task.complete; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.catchThrowableOfType; +import static pro.taskana.testapi.DefaultTestEntities.defaultTestClassification; +import static pro.taskana.testapi.DefaultTestEntities.defaultTestObjectReference; +import static pro.taskana.testapi.DefaultTestEntities.defaultTestWorkbasket; + +import java.util.List; +import java.util.stream.Stream; +import org.assertj.core.api.ThrowableAssert.ThrowingCallable; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.DynamicTest; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestFactory; +import org.junit.jupiter.api.TestTemplate; +import org.junit.jupiter.api.function.ThrowingConsumer; +import pro.taskana.classification.api.ClassificationService; +import pro.taskana.classification.api.models.ClassificationSummary; +import pro.taskana.common.internal.util.Triplet; +import pro.taskana.task.api.TaskService; +import pro.taskana.task.api.TaskState; +import pro.taskana.task.api.exceptions.InvalidTaskStateException; +import pro.taskana.task.api.models.ObjectReference; +import pro.taskana.task.api.models.Task; +import pro.taskana.testapi.DefaultTestEntities; +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.user.api.UserService; +import pro.taskana.workbasket.api.WorkbasketPermission; +import pro.taskana.workbasket.api.WorkbasketService; +import pro.taskana.workbasket.api.exceptions.NotAuthorizedOnWorkbasketException; +import pro.taskana.workbasket.api.models.WorkbasketSummary; + +@TaskanaIntegrationTest +class CancelTaskAccTest { + @TaskanaInject TaskService taskService; + @TaskanaInject ClassificationService classificationService; + @TaskanaInject WorkbasketService workbasketService; + @TaskanaInject UserService userService; + + ClassificationSummary defaultClassificationSummary; + WorkbasketSummary defaultWorkbasketSummary; + ObjectReference defaultObjectReference; + + @WithAccessId(user = "admin") + @BeforeAll + void setup() throws Exception { + defaultClassificationSummary = + defaultTestClassification().buildAndStoreAsSummary(classificationService); + defaultWorkbasketSummary = defaultTestWorkbasket().buildAndStoreAsSummary(workbasketService); + + WorkbasketAccessItemBuilder.newWorkbasketAccessItem() + .workbasketId(defaultWorkbasketSummary.getId()) + .accessId("user-1-2") + .permission(WorkbasketPermission.OPEN) + .permission(WorkbasketPermission.READ) + .permission(WorkbasketPermission.APPEND) + .buildAndStore(workbasketService); + + defaultObjectReference = defaultTestObjectReference().build(); + } + + @WithAccessId(user = "admin") + @Test + void should_CancelReadyTask() throws Exception { + Task task = + TaskBuilder.newTask() + .classificationSummary(defaultClassificationSummary) + .workbasketSummary(defaultWorkbasketSummary) + .state(TaskState.READY) + .primaryObjRef(DefaultTestEntities.defaultTestObjectReference().build()) + .buildAndStore(taskService); + + taskService.cancelTask(task.getId()); + + Task readTask = taskService.getTask(task.getId()); + assertThat(readTask.getState()).isEqualTo(TaskState.CANCELLED); + } + + @WithAccessId(user = "admin") + @WithAccessId(user = "taskadmin") + @TestTemplate + void should_CancelTask_When_NoExplicitPermissionsButUserIsInAdministrativeRole() + throws Exception { + Task taskToCancel = + TaskBuilder.newTask() + .classificationSummary(defaultClassificationSummary) + .workbasketSummary(defaultWorkbasketSummary) + .state(TaskState.CLAIMED) + .primaryObjRef(DefaultTestEntities.defaultTestObjectReference().build()) + .buildAndStore(taskService); + + Task cancelledTask = taskService.cancelTask(taskToCancel.getId()); + + assertThat(cancelledTask.getState()).isEqualTo(TaskState.CANCELLED); + } + + @WithAccessId(user = "user-1-2") + @Test + void should_CancelClaimedTask() throws Exception { + Task claimedTask = + TaskBuilder.newTask() + .classificationSummary(defaultClassificationSummary) + .workbasketSummary(defaultWorkbasketSummary) + .state(TaskState.CLAIMED) + .primaryObjRef(DefaultTestEntities.defaultTestObjectReference().build()) + .buildAndStore(taskService); + + Task cancelledTask = taskService.cancelTask(claimedTask.getId()); + + assertThat(cancelledTask.getState()).isEqualTo(TaskState.CANCELLED); + } + + @WithAccessId(user = "user-taskrouter") + @Test + void should_ThrowException_When_UserNotAuthorized() throws Exception { + Task task = + TaskBuilder.newTask() + .classificationSummary(defaultClassificationSummary) + .workbasketSummary(defaultWorkbasketSummary) + .state(TaskState.CLAIMED) + .primaryObjRef(DefaultTestEntities.defaultTestObjectReference().build()) + .buildAndStore(taskService, "admin"); + + ThrowingCallable call = () -> taskService.cancelTask(task.getId()); + + NotAuthorizedOnWorkbasketException e = + catchThrowableOfType(call, NotAuthorizedOnWorkbasketException.class); + assertThat(e.getRequiredPermissions()).containsExactly(WorkbasketPermission.READ); + assertThat(e.getCurrentUserId()).isEqualTo("user-taskrouter"); + assertThat(e.getWorkbasketId()).isEqualTo(defaultWorkbasketSummary.getId()); + } + + @WithAccessId(user = "user-1-2") + @TestFactory + Stream should_ThrowException_When_CancellingATaskInEndState() throws Exception { + Task completedTask = + TaskBuilder.newTask() + .classificationSummary(defaultClassificationSummary) + .workbasketSummary(defaultWorkbasketSummary) + .state(TaskState.COMPLETED) + .primaryObjRef(DefaultTestEntities.defaultTestObjectReference().build()) + .buildAndStore(taskService); + Task terminatedTask = + TaskBuilder.newTask() + .classificationSummary(defaultClassificationSummary) + .workbasketSummary(defaultWorkbasketSummary) + .state(TaskState.TERMINATED) + .primaryObjRef(DefaultTestEntities.defaultTestObjectReference().build()) + .buildAndStore(taskService); + Task cancelledTask = + TaskBuilder.newTask() + .classificationSummary(defaultClassificationSummary) + .workbasketSummary(defaultWorkbasketSummary) + .state(TaskState.CANCELLED) + .primaryObjRef(DefaultTestEntities.defaultTestObjectReference().build()) + .buildAndStore(taskService); + List> list = + List.of( + Triplet.of("When Cancelling Completed Task", completedTask, TaskState.COMPLETED), + Triplet.of("When Cancelling Terminated Task", terminatedTask, TaskState.TERMINATED), + Triplet.of("When Cancelling Cancelled Task", cancelledTask, TaskState.CANCELLED)); + ThrowingConsumer> testCancelTask = + t -> { + ThrowingCallable call = () -> taskService.cancelTask(t.getMiddle().getId()); + + InvalidTaskStateException e = catchThrowableOfType(call, InvalidTaskStateException.class); + assertThat(e.getRequiredTaskStates()) + .containsExactlyInAnyOrder( + TaskState.READY, + TaskState.IN_REVIEW, + TaskState.READY_FOR_REVIEW, + TaskState.CLAIMED); + assertThat(e.getTaskId()).isEqualTo(t.getMiddle().getId()); + assertThat(e.getTaskState()).isEqualTo(t.getRight()); + }; + return DynamicTest.stream(list.iterator(), Triplet::getLeft, testCancelTask); + } +} diff --git a/lib/taskana-core/src/main/java/pro/taskana/task/internal/TaskServiceImpl.java b/lib/taskana-core/src/main/java/pro/taskana/task/internal/TaskServiceImpl.java index 012f50fb1..4fe06b386 100644 --- a/lib/taskana-core/src/main/java/pro/taskana/task/internal/TaskServiceImpl.java +++ b/lib/taskana-core/src/main/java/pro/taskana/task/internal/TaskServiceImpl.java @@ -1233,7 +1233,13 @@ public class TaskServiceImpl implements TaskService { TaskImpl task = (TaskImpl) getTask(taskId); TaskState state = task.getState(); if (state.isEndState()) { - throw new InvalidTaskStateException(taskId, state, TaskState.READY); + throw new InvalidTaskStateException( + taskId, + state, + TaskState.READY, + TaskState.CLAIMED, + TaskState.READY_FOR_REVIEW, + TaskState.IN_REVIEW); } Instant now = Instant.now(); diff --git a/lib/taskana-core/src/test/java/acceptance/task/complete/CancelTaskAccTest.java b/lib/taskana-core/src/test/java/acceptance/task/complete/CancelTaskAccTest.java deleted file mode 100644 index 8f30d10de..000000000 --- a/lib/taskana-core/src/test/java/acceptance/task/complete/CancelTaskAccTest.java +++ /dev/null @@ -1,122 +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.util.List; -import org.assertj.core.api.ThrowableAssert.ThrowingCallable; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.TestTemplate; -import org.junit.jupiter.api.extension.ExtendWith; -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.InvalidTaskStateException; -import pro.taskana.task.api.models.Task; -import pro.taskana.task.api.models.TaskSummary; -import pro.taskana.workbasket.api.exceptions.NotAuthorizedOnWorkbasketException; - -/** Acceptance tests for all "cancel task" scenarios. */ -@ExtendWith(JaasExtension.class) -class CancelTaskAccTest extends AbstractAccTest { - - CancelTaskAccTest() { - super(); - } - - @BeforeEach - void setupIndividualTest() throws Exception { - resetDb(false); - } - - @WithAccessId(user = "user-1-2") - @Test - void testQueryCancelledTasks() { - List taskSummaries = - taskService.createTaskQuery().stateIn(TaskState.CANCELLED).list(); - assertThat(taskSummaries).hasSize(5); - } - - @WithAccessId(user = "admin") - @Test - void testCancelReadyTask() throws Exception { - List taskSummaries = taskService.createTaskQuery().stateIn(TaskState.READY).list(); - assertThat(taskSummaries).hasSize(48); - taskService.cancelTask(taskSummaries.get(0).getId()); - long numTasks = taskService.createTaskQuery().stateIn(TaskState.READY).count(); - assertThat(numTasks).isEqualTo(47); - numTasks = taskService.createTaskQuery().stateIn(TaskState.CANCELLED).count(); - assertThat(numTasks).isEqualTo(6); - } - - @WithAccessId(user = "admin") - @WithAccessId(user = "taskadmin") - @TestTemplate - void should_CancelTask_When_NoExplicitPermissionsButUserIsInAdministrativeRole() - throws Exception { - - resetDb(false); - Task tasktoCancel = taskService.getTask("TKI:000000000000000000000000000000000001"); - assertThat(tasktoCancel.getState()).isEqualTo(TaskState.CLAIMED); - - Task cancelledTask = taskService.cancelTask(tasktoCancel.getId()); - assertThat(cancelledTask.getState()).isEqualTo(TaskState.CANCELLED); - } - - @WithAccessId(user = "user-1-2") - @Test - void testCancelClaimedTask() throws Exception { - List taskSummaries = - taskService.createTaskQuery().stateIn(TaskState.CLAIMED).list(); - - long numTasksCancelled = taskService.createTaskQuery().stateIn(TaskState.CANCELLED).count(); - - taskService.cancelTask(taskSummaries.get(0).getId()); - long numTasksClaimed = taskService.createTaskQuery().stateIn(TaskState.CLAIMED).count(); - assertThat(numTasksClaimed).isEqualTo(taskSummaries.size() - 1); - long newNumTasksCancelled = taskService.createTaskQuery().stateIn(TaskState.CANCELLED).count(); - assertThat(newNumTasksCancelled).isEqualTo(numTasksCancelled + 1); - } - - @WithAccessId(user = "user-taskrouter") - @Test - void should_ThrowException_When_UserNotAuthorized() { - assertThatThrownBy(() -> taskService.cancelTask("TKI:000000000000000000000000000000000001")) - .isInstanceOf(NotAuthorizedOnWorkbasketException.class); - } - - @WithAccessId(user = "admin") - @Test - void testCancelCompletedTask() { - List taskSummaries = - taskService.createTaskQuery().stateIn(TaskState.COMPLETED).list(); - assertThat(taskSummaries).hasSize(10); - - ThrowingCallable taskanaCall = () -> taskService.cancelTask(taskSummaries.get(0).getId()); - - assertThatThrownBy(taskanaCall).isInstanceOf(InvalidTaskStateException.class); - } - - @WithAccessId(user = "user-1-2") - @Test - void testCancelTerminatedTask() { - List taskSummaries = - taskService.createTaskQuery().stateIn(TaskState.TERMINATED).list(); - assertThat(taskSummaries).hasSize(5); - ThrowingCallable taskanaCall = () -> taskService.cancelTask(taskSummaries.get(0).getId()); - - assertThatThrownBy(taskanaCall).isInstanceOf(InvalidTaskStateException.class); - } - - @WithAccessId(user = "user-1-2") - @Test - void testCancelCancelledTask() { - List taskSummaries = - taskService.createTaskQuery().stateIn(TaskState.CANCELLED).list(); - assertThat(taskSummaries).hasSize(5); - ThrowingCallable taskanaCall = () -> taskService.cancelTask(taskSummaries.get(0).getId()); - assertThatThrownBy(taskanaCall).isInstanceOf(InvalidTaskStateException.class); - } -}