package com.jsowell.system.service; import com.jsowell.common.exception.ServiceException; import com.jsowell.system.constant.TodoTaskConstants; import com.jsowell.system.domain.SysTodoTask; import com.jsowell.system.domain.dto.TodoTaskCreateCommand; import com.jsowell.system.domain.dto.TodoTaskQuery; import com.jsowell.system.mapper.SysTodoTaskMapper; import com.jsowell.system.service.impl.TodoTaskServiceImpl; import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; import org.springframework.dao.DuplicateKeyException; import java.lang.reflect.Field; import java.util.Collections; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; class TodoTaskServiceImplTest { @Test void createTask_shouldSetDefaultsAndInsert() { SysTodoTaskMapper mapper = mock(SysTodoTaskMapper.class); TodoTaskServiceImpl service = newService(mapper); TodoTaskCreateCommand command = createCommand(); when(mapper.selectActiveByIdempotentKey("INVOICE:100:8")).thenReturn(null); doAnswer(invocation -> { SysTodoTask task = invocation.getArgument(0); task.setTodoId(11L); return 1; }).when(mapper).insertTodoTask(any(SysTodoTask.class)); SysTodoTask result = service.createTask(command); ArgumentCaptor captor = ArgumentCaptor.forClass(SysTodoTask.class); verify(mapper).insertTodoTask(captor.capture()); SysTodoTask inserted = captor.getValue(); assertEquals(11L, result.getTodoId()); assertEquals(TodoTaskConstants.PRIORITY_NORMAL, inserted.getPriority()); assertEquals(TodoTaskConstants.STATUS_PENDING, inserted.getTaskStatus()); assertEquals(TodoTaskConstants.READ_UNREAD, inserted.getReadStatus()); assertEquals(inserted.getIdempotentKey(), inserted.getActiveIdempotentKey()); } @Test void createTask_shouldReturnExistingTask() { SysTodoTaskMapper mapper = mock(SysTodoTaskMapper.class); TodoTaskServiceImpl service = newService(mapper); SysTodoTask existing = new SysTodoTask(); existing.setTodoId(12L); when(mapper.selectActiveByIdempotentKey("INVOICE:100:8")).thenReturn(existing); SysTodoTask result = service.createTask(createCommand()); assertSame(existing, result); verify(mapper, never()).insertTodoTask(any(SysTodoTask.class)); } @Test void createTask_shouldRecoverFromConcurrentDuplicate() { SysTodoTaskMapper mapper = mock(SysTodoTaskMapper.class); TodoTaskServiceImpl service = newService(mapper); SysTodoTask existing = new SysTodoTask(); existing.setTodoId(13L); when(mapper.selectActiveByIdempotentKey("INVOICE:100:8")).thenReturn(null, existing); doThrow(new DuplicateKeyException("duplicate")) .when(mapper).insertTodoTask(any(SysTodoTask.class)); SysTodoTask result = service.createTask(createCommand()); assertSame(existing, result); } @Test void listForAssignee_shouldOverrideRequestedUser() { SysTodoTaskMapper mapper = mock(SysTodoTaskMapper.class); TodoTaskServiceImpl service = newService(mapper); TodoTaskQuery query = new TodoTaskQuery(); query.setAssigneeUserId(999L); when(mapper.selectTodoList(query)).thenReturn(Collections.emptyList()); service.listForAssignee(query, 8L); assertEquals(8L, query.getAssigneeUserId()); verify(mapper).selectTodoList(query); } @Test void complete_shouldBeIdempotentWhenAlreadyCompleted() { SysTodoTaskMapper mapper = mock(SysTodoTaskMapper.class); TodoTaskServiceImpl service = newService(mapper); SysTodoTask completed = new SysTodoTask(); completed.setTaskStatus(TodoTaskConstants.STATUS_COMPLETED); when(mapper.completeTodoForAssignee(20L, 8L, 8L, "tester")).thenReturn(0); when(mapper.selectTodoByIdForAssignee(20L, 8L)).thenReturn(completed); assertTrue(service.complete(20L, 8L, "tester")); } @Test void complete_shouldRejectCancelledTask() { SysTodoTaskMapper mapper = mock(SysTodoTaskMapper.class); TodoTaskServiceImpl service = newService(mapper); SysTodoTask cancelled = new SysTodoTask(); cancelled.setTaskStatus(TodoTaskConstants.STATUS_CANCELLED); when(mapper.completeTodoForAssignee(20L, 8L, 8L, "tester")).thenReturn(0); when(mapper.selectTodoByIdForAssignee(20L, 8L)).thenReturn(cancelled); assertThrows(ServiceException.class, () -> service.complete(20L, 8L, "tester")); } private static TodoTaskCreateCommand createCommand() { return TodoTaskCreateCommand.builder() .taskType("INVOICE_REVIEW") .title("司机开票申请") .businessType("INVOICE_APPLY") .businessId("100") .routeName("InvoiceApplyDetail") .assigneeUserId(8L) .idempotentKey("INVOICE:100:8") .createBy("tester") .build(); } private static TodoTaskServiceImpl newService(SysTodoTaskMapper mapper) { TodoTaskServiceImpl service = new TodoTaskServiceImpl(); setField(service, "todoTaskMapper", mapper); return service; } private static void setField(Object target, String fieldName, Object value) { try { Field field = target.getClass().getDeclaredField(fieldName); field.setAccessible(true); field.set(target, value); } catch (Exception exception) { throw new RuntimeException(exception); } } }