package com.dalab.autodelete.service.impl; import com.dalab.autodelete.dto.*; import com.dalab.autodelete.mapper.DeletionTaskMapper; import com.dalab.autodelete.model.DeletionStatus; import com.dalab.autodelete.model.DeletionTaskEntity; import com.dalab.autodelete.repository.DeletionTaskRepository; import com.dalab.autodelete.service.IDeletionConfigService; import com.dalab.autodelete.service.exception.DeletionTaskNotFoundException; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.ArgumentCaptor; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; import java.time.LocalDateTime; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Optional; import java.util.UUID; import static org.junit.jupiter.api.Assertions.*; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.*; @ExtendWith(MockitoExtension.class) class DeletionTaskServiceImplTest { @Mock private DeletionTaskRepository taskRepository; @Mock private DeletionTaskMapper taskMapper; @Mock private IDeletionConfigService deletionConfigService; @InjectMocks private DeletionTaskServiceImpl taskService; private DeletionTaskRequest sampleTaskRequest; private DeletionTaskEntity sampleTaskEntity; private DeletionTaskStatusDTO sampleTaskStatusDTO; private DeletionTaskResponse sampleTaskResponse; private DeletionConfigDTO globalConfigDefault; private DeletionConfigDTO globalConfigSoftDeleteApproval; private String sampleTaskId; @BeforeEach void setUp() { sampleTaskId = UUID.randomUUID().toString(); DeletionTaskRequest.DeletionScope scope = DeletionTaskRequest.DeletionScope.builder() .assetIds(List.of("asset1")).build(); sampleTaskRequest = DeletionTaskRequest.builder() .taskName("Test Task") .scope(scope) .triggeredBy("user@test.com") .build(); sampleTaskEntity = new DeletionTaskEntity(); sampleTaskEntity.setTaskId(sampleTaskId); sampleTaskEntity.setTaskName("Test Task"); sampleTaskEntity.setStatus(DeletionStatus.SUBMITTED); sampleTaskEntity.setSubmittedAt(LocalDateTime.now()); sampleTaskEntity.setScope(new DeletionTaskEntity.DeletionScopeData(List.of("asset1"))); sampleTaskEntity.setAssetStatuses(new ArrayList<>()); sampleTaskStatusDTO = DeletionTaskStatusDTO.builder().taskId(sampleTaskId).status(DeletionStatus.SUBMITTED.name()).build(); sampleTaskResponse = DeletionTaskResponse.builder().taskId(sampleTaskId).status(DeletionStatus.SUBMITTED.name()).build(); globalConfigDefault = DeletionConfigDTO.builder() .enabled(true) .softDeleteByDefault(false) // Hard delete by default .requireApprovalForHardDelete(false) // No approval for hard delete .requireApprovalForSoftDelete(true) // Approval for soft delete .build(); globalConfigSoftDeleteApproval = DeletionConfigDTO.builder() .enabled(true) .softDeleteByDefault(true) // Soft delete by default .requireApprovalForHardDelete(true) // Approval for hard delete .requireApprovalForSoftDelete(true) // Approval for soft delete .build(); } @Test void submitDeletionTask_ValidRequest_NoApprovalNeeded_ShouldSaveAndReturnResponse() { when(deletionConfigService.getDeletionConfig()).thenReturn(globalConfigDefault); when(taskMapper.requestToEntity(sampleTaskRequest)).thenReturn(sampleTaskEntity); // sampleTaskEntity is already set up when(taskRepository.save(any(DeletionTaskEntity.class))).thenReturn(sampleTaskEntity); when(taskMapper.entityToTaskResponse(sampleTaskEntity)).thenReturn(sampleTaskResponse); DeletionTaskResponse response = taskService.submitDeletionTask(sampleTaskRequest); assertNotNull(response); assertEquals(DeletionStatus.SUBMITTED.name(), response.getStatus()); ArgumentCaptor entityCaptor = ArgumentCaptor.forClass(DeletionTaskEntity.class); verify(taskRepository).save(entityCaptor.capture()); assertEquals(DeletionStatus.SUBMITTED, entityCaptor.getValue().getStatus()); assertNotNull(entityCaptor.getValue().getAssetStatuses()); assertEquals(1, entityCaptor.getValue().getAssetsPendingProcessing()); assertEquals("HARD", entityCaptor.getValue().getAssetStatuses().get(0).getDeletionType()); } @Test void submitDeletionTask_RequiresHardDeleteApproval_ShouldSetPendingApproval() { DeletionConfigDTO config = DeletionConfigDTO.builder().softDeleteByDefault(false).requireApprovalForHardDelete(true).build(); when(deletionConfigService.getDeletionConfig()).thenReturn(config); when(taskMapper.requestToEntity(sampleTaskRequest)).thenReturn(sampleTaskEntity); when(taskRepository.save(any(DeletionTaskEntity.class))).thenReturn(sampleTaskEntity); taskService.submitDeletionTask(sampleTaskRequest); ArgumentCaptor entityCaptor = ArgumentCaptor.forClass(DeletionTaskEntity.class); verify(taskRepository).save(entityCaptor.capture()); assertEquals(DeletionStatus.PENDING_APPROVAL, entityCaptor.getValue().getStatus()); } @Test void submitDeletionTask_SoftDeleteOverride_RequiresSoftApproval_ShouldSetPendingApproval() { sampleTaskRequest.setOverrideConfig(DeletionTaskRequest.DeletionConfigOverride.builder().softDelete(true).build()); when(deletionConfigService.getDeletionConfig()).thenReturn(globalConfigSoftDeleteApproval); when(taskMapper.requestToEntity(sampleTaskRequest)).thenReturn(sampleTaskEntity); when(taskRepository.save(any(DeletionTaskEntity.class))).thenReturn(sampleTaskEntity); taskService.submitDeletionTask(sampleTaskRequest); ArgumentCaptor entityCaptor = ArgumentCaptor.forClass(DeletionTaskEntity.class); verify(taskRepository).save(entityCaptor.capture()); assertEquals(DeletionStatus.PENDING_APPROVAL, entityCaptor.getValue().getStatus()); assertEquals("SOFT", entityCaptor.getValue().getAssetStatuses().get(0).getDeletionType()); } @Test void submitDeletionTask_InvalidScope_ShouldReturnFailedStatus() { DeletionTaskRequest invalidRequest = DeletionTaskRequest.builder().taskName("Invalid").scope(null).build(); DeletionTaskResponse response = taskService.submitDeletionTask(invalidRequest); assertEquals(DeletionStatus.FAILED.name(), response.getStatus()); verify(taskRepository, never()).save(any()); } @Test void getTaskStatus_TaskExists_ShouldReturnDTO() { when(taskRepository.findById(sampleTaskId)).thenReturn(Optional.of(sampleTaskEntity)); when(taskMapper.entityToStatusDTO(sampleTaskEntity)).thenReturn(sampleTaskStatusDTO); DeletionTaskStatusDTO result = taskService.getTaskStatus(sampleTaskId); assertNotNull(result); assertEquals(sampleTaskId, result.getTaskId()); } @Test void getTaskStatus_TaskNotExists_ShouldReturnNull() { when(taskRepository.findById(sampleTaskId)).thenReturn(Optional.empty()); DeletionTaskStatusDTO result = taskService.getTaskStatus(sampleTaskId); assertNull(result); } @Test void listTasks_ShouldReturnPaginatedDTOs() { Pageable pageable = PageRequest.of(0, 10); List taskList = Collections.singletonList(sampleTaskEntity); Page page = new PageImpl<>(taskList, pageable, 1); when(taskRepository.findAll(pageable)).thenReturn(page); when(taskMapper.entityToStatusDTO(sampleTaskEntity)).thenReturn(sampleTaskStatusDTO); DeletionTaskListResponse response = taskService.listTasks(pageable); assertNotNull(response); assertEquals(1, response.getTasks().size()); assertEquals(sampleTaskId, response.getTasks().get(0).getTaskId()); } @Test void approveTask_TaskExistsAndPending_ShouldApprove() { sampleTaskEntity.setStatus(DeletionStatus.PENDING_APPROVAL); when(taskRepository.findById(sampleTaskId)).thenReturn(Optional.of(sampleTaskEntity)); when(taskRepository.save(any(DeletionTaskEntity.class))).thenReturn(sampleTaskEntity); when(taskMapper.entityToStatusDTO(any(DeletionTaskEntity.class))).thenAnswer(invocation -> { DeletionTaskEntity entity = invocation.getArgument(0); return DeletionTaskStatusDTO.builder().taskId(entity.getTaskId()).status(entity.getStatus().name()).build(); }); TaskApprovalRequest approvalReq = TaskApprovalRequest.builder().comments("Approved by test").build(); DeletionTaskStatusDTO result = taskService.approveTask(sampleTaskId, approvalReq); assertEquals(DeletionStatus.APPROVED.name(), result.getStatus()); assertEquals("Approved by test", sampleTaskEntity.getApprovalComments()); verify(taskRepository).save(sampleTaskEntity); } @Test void approveTask_TaskNotPending_ShouldReturnCurrentStatusWithErrors() { sampleTaskEntity.setStatus(DeletionStatus.COMPLETED); when(taskRepository.findById(sampleTaskId)).thenReturn(Optional.of(sampleTaskEntity)); // Mapper will be called to return the current DTO when(taskMapper.entityToStatusDTO(sampleTaskEntity)).thenReturn(DeletionTaskStatusDTO.builder().taskId(sampleTaskId).status(DeletionStatus.COMPLETED.name()).build()); DeletionTaskStatusDTO result = taskService.approveTask(sampleTaskId, new TaskApprovalRequest()); assertEquals(DeletionStatus.COMPLETED.name(), result.getStatus()); assertNotNull(result.getErrorMessages()); assertFalse(result.getErrorMessages().isEmpty()); verify(taskRepository, never()).save(any()); } @Test void approveTask_TaskNotFound_ShouldThrowException() { when(taskRepository.findById(sampleTaskId)).thenReturn(Optional.empty()); assertThrows(DeletionTaskNotFoundException.class, () -> { taskService.approveTask(sampleTaskId, new TaskApprovalRequest()); }); } @Test void rejectTask_TaskExistsAndPending_ShouldReject() { sampleTaskEntity.setStatus(DeletionStatus.PENDING_APPROVAL); when(taskRepository.findById(sampleTaskId)).thenReturn(Optional.of(sampleTaskEntity)); when(taskRepository.save(any(DeletionTaskEntity.class))).thenReturn(sampleTaskEntity); when(taskMapper.entityToStatusDTO(any(DeletionTaskEntity.class))).thenAnswer(invocation -> { DeletionTaskEntity entity = invocation.getArgument(0); return DeletionTaskStatusDTO.builder().taskId(entity.getTaskId()).status(entity.getStatus().name()).build(); }); TaskApprovalRequest rejectionReq = TaskApprovalRequest.builder().comments("Rejected by test").build(); DeletionTaskStatusDTO result = taskService.rejectTask(sampleTaskId, rejectionReq); assertEquals(DeletionStatus.REJECTED.name(), result.getStatus()); assertEquals("Rejected by test", sampleTaskEntity.getRejectionComments()); verify(taskRepository).save(sampleTaskEntity); } }