package pro.taskana.workbasket.internal; 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 org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.lenient; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; import java.time.Instant; import java.time.temporal.ChronoUnit; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.stream.Collectors; import java.util.stream.IntStream; 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 org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.Spy; import org.mockito.junit.jupiter.MockitoExtension; import pro.taskana.TaskanaEngineConfiguration; import pro.taskana.common.api.TaskanaEngine; import pro.taskana.common.api.exceptions.ConcurrencyException; import pro.taskana.common.api.exceptions.DomainNotFoundException; import pro.taskana.common.api.exceptions.NotAuthorizedException; import pro.taskana.common.internal.InternalTaskanaEngine; import pro.taskana.task.api.TaskQuery; import pro.taskana.task.api.TaskService; import pro.taskana.workbasket.api.WorkbasketType; import pro.taskana.workbasket.api.exceptions.InvalidWorkbasketException; import pro.taskana.workbasket.api.exceptions.WorkbasketAccessItemAlreadyExistException; import pro.taskana.workbasket.api.exceptions.WorkbasketAlreadyExistException; import pro.taskana.workbasket.api.exceptions.WorkbasketNotFoundException; import pro.taskana.workbasket.api.models.Workbasket; import pro.taskana.workbasket.api.models.WorkbasketAccessItem; import pro.taskana.workbasket.internal.models.WorkbasketAccessItemImpl; import pro.taskana.workbasket.internal.models.WorkbasketImpl; /** * Unit Test for workbasketServiceImpl. * * @author EH */ @ExtendWith(MockitoExtension.class) class WorkbasketServiceImplTest { @Spy @InjectMocks private WorkbasketServiceImpl workbasketServiceSpy; @Mock private WorkbasketMapper workbasketMapperMock; @Mock private DistributionTargetMapper distributionTargetMapperMock; @Mock private WorkbasketAccessMapper workbasketAccessMapperMock; @Mock private TaskService taskServiceMock; @Mock private TaskQuery taskQueryMock; @Mock private TaskanaEngine taskanaEngine; @Mock private InternalTaskanaEngine internalTaskanaEngineMock; @Mock private TaskanaEngineConfiguration taskanaEngineConfigurationMock; @BeforeEach void setup() { lenient().when(internalTaskanaEngineMock.getEngine()).thenReturn(taskanaEngine); } @Test void testCreateWorkbasket_WithDistibutionTargets() throws WorkbasketNotFoundException, NotAuthorizedException, InvalidWorkbasketException, WorkbasketAlreadyExistException, DomainNotFoundException { final int distTargetAmount = 2; WorkbasketImpl expectedWb = createTestWorkbasket(null, "Key-1"); doReturn(expectedWb).when(workbasketServiceSpy).getWorkbasket(any()); when(internalTaskanaEngineMock.domainExists(any())).thenReturn(true); final Workbasket actualWb = workbasketServiceSpy.createWorkbasket(expectedWb); workbasketServiceSpy.setDistributionTargets( expectedWb.getId(), createTestDistributionTargets(distTargetAmount)); verify(internalTaskanaEngineMock, times(4)).openConnection(); verify(workbasketMapperMock, times(3)).insert(any()); verify(workbasketServiceSpy, times(distTargetAmount + 1)).getWorkbasket(any()); verify(distributionTargetMapperMock, times(1)).deleteAllDistributionTargetsBySourceId(any()); verify(distributionTargetMapperMock, times(distTargetAmount)).insert(any(), any()); verify(workbasketMapperMock, times(3)).findByKeyAndDomain(any(), any()); verify(workbasketMapperMock, times(1)).update(any()); verify(internalTaskanaEngineMock, times(4)).returnConnection(); verify(taskanaEngine, times(4)).checkRoleMembership(any()); verify(internalTaskanaEngineMock, times(4)).getEngine(); verify(internalTaskanaEngineMock, times(3)).domainExists(any()); verifyNoMoreInteractions( taskQueryMock, taskServiceMock, workbasketMapperMock, workbasketAccessMapperMock, distributionTargetMapperMock, internalTaskanaEngineMock, taskanaEngine, taskanaEngineConfigurationMock); assertThat(actualWb.getId()).isNotNull(); assertThat(actualWb.getId()).startsWith("WBI"); assertThat(actualWb.getCreated()).isNotNull(); assertThat(actualWb.getModified()).isNotNull(); } @Test void testCreateWorkbasket_DistibutionTargetNotExisting() throws Exception { WorkbasketImpl expectedWb = createTestWorkbasket("ID-1", "Key-1"); when(internalTaskanaEngineMock.domainExists(any())).thenReturn(true); String otherWorkbasketId = "4711"; List<String> destinations = Collections.singletonList(otherWorkbasketId); workbasketServiceSpy.createWorkbasket(expectedWb); doReturn(expectedWb).when(workbasketServiceSpy).getWorkbasket(eq(expectedWb.getId())); ThrowingCallable call = () -> { workbasketServiceSpy.setDistributionTargets(expectedWb.getId(), destinations); }; assertThatThrownBy(call) .isInstanceOf(WorkbasketNotFoundException.class) .hasFieldOrPropertyWithValue("id", otherWorkbasketId) .hasFieldOrPropertyWithValue("key", null) .hasFieldOrPropertyWithValue("domain", null); verify(internalTaskanaEngineMock, times(3)).openConnection(); verify(workbasketMapperMock, times(1)).insert(expectedWb); verify(workbasketMapperMock, times(1)).findById(any()); verify(workbasketMapperMock, times(1)).findByKeyAndDomain(any(), any()); verify(workbasketServiceSpy, times(2)).getWorkbasket(any()); verify(internalTaskanaEngineMock, times(3)).returnConnection(); verify(taskanaEngine, times(2)).checkRoleMembership(any()); verify(internalTaskanaEngineMock, times(2)).getEngine(); verify(internalTaskanaEngineMock, times(1)).domainExists(any()); verify(distributionTargetMapperMock) .deleteAllDistributionTargetsBySourceId(eq(expectedWb.getId())); verify(workbasketMapperMock).update(eq(expectedWb)); verifyNoMoreInteractions( taskQueryMock, taskServiceMock, workbasketMapperMock, workbasketAccessMapperMock, distributionTargetMapperMock, internalTaskanaEngineMock, taskanaEngine, taskanaEngineConfigurationMock); } @Test void testDeleteWorkbasketIsUsed() throws NotAuthorizedException, WorkbasketNotFoundException { Workbasket wb = createTestWorkbasket("WBI:0", "wb-key"); ThrowingCallable call = () -> { workbasketServiceSpy.deleteWorkbasket(wb.getId()); }; assertThatThrownBy(call).isInstanceOf(WorkbasketNotFoundException.class); verify(internalTaskanaEngineMock, times(2)).openConnection(); verify(workbasketServiceSpy, times(1)).getWorkbasket(wb.getId()); verify(taskanaEngine, times(0)).getTaskService(); verify(taskServiceMock, times(0)).createTaskQuery(); verify(taskQueryMock, times(0)).workbasketIdIn(wb.getId()); verify(taskQueryMock, times(0)).count(); verify(internalTaskanaEngineMock, times(2)).returnConnection(); verifyNoMoreInteractions( taskQueryMock, taskServiceMock, workbasketAccessMapperMock, distributionTargetMapperMock, taskanaEngineConfigurationMock); } @Test void testSetWorkbasketAccessItemsWithMultipleAccessIds() { String wid = "workbasketId"; List<WorkbasketAccessItem> accessItems = IntStream.rangeClosed(0, 10) .mapToObj(i -> createWorkbasketAccessItem("id" + i, "access" + i, wid)) .collect(Collectors.toList()); accessItems.add(createWorkbasketAccessItem("id5", "access5", wid)); assertThatThrownBy(() -> workbasketServiceSpy.setWorkbasketAccessItems(wid, accessItems)) .isInstanceOf(WorkbasketAccessItemAlreadyExistException.class); } @Test void testCheckModifiedHasNotChanged() { Instant expectedModifiedTimestamp = Instant.now(); WorkbasketImpl oldWb = createTestWorkbasket(null, "Key-1"); WorkbasketImpl workbasketImplToUpdate = createTestWorkbasket(null, "Key-2"); oldWb.setModified(expectedModifiedTimestamp); workbasketImplToUpdate.setModified(expectedModifiedTimestamp); ThrowingCallable call = () -> workbasketServiceSpy.checkModifiedHasNotChanged(oldWb, workbasketImplToUpdate); assertThatCode(call).doesNotThrowAnyException(); workbasketImplToUpdate.setModified(expectedModifiedTimestamp.minus(1, ChronoUnit.HOURS)); call = () -> workbasketServiceSpy.checkModifiedHasNotChanged(oldWb, workbasketImplToUpdate); assertThatThrownBy(call).isInstanceOf(ConcurrencyException.class); } private WorkbasketImpl createTestWorkbasket(String id, String key) { WorkbasketImpl workbasket = new WorkbasketImpl(); workbasket.setId(id); workbasket.setKey(key); workbasket.setName("Workbasket " + id); workbasket.setDescription("Description WB with Key " + key); workbasket.setType(WorkbasketType.PERSONAL); workbasket.setDomain("DOMAIN_A"); return workbasket; } private List<String> createTestDistributionTargets(int amount) throws InvalidWorkbasketException, NotAuthorizedException, WorkbasketAlreadyExistException, DomainNotFoundException { List<String> distributionsTargets = new ArrayList<>(); amount = Math.max(amount, 0); for (int i = 0; i < amount; i++) { WorkbasketImpl wb = createTestWorkbasket("WB-ID-" + i, "WB-KEY-" + i); workbasketServiceSpy.createWorkbasket(wb); distributionsTargets.add(wb.getId()); } return distributionsTargets; } private WorkbasketAccessItem createWorkbasketAccessItem( String id, String accessId, String workbasketId) { WorkbasketAccessItemImpl workbasketAccessItem = new WorkbasketAccessItemImpl(); workbasketAccessItem.setId(id); workbasketAccessItem.setAccessId(accessId); workbasketAccessItem.setWorkbasketId(workbasketId); return workbasketAccessItem; } }