package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate; import cn.hutool.core.map.MapUtil; import cn.hutool.extra.spring.SpringUtil; import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest; import cn.iocoder.yudao.module.bpm.enums.definition.BpmUserTaskAssignStartUserHandlerTypeEnum; import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.other.BpmTaskCandidateAssignEmptyStrategy; import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.user.BpmTaskCandidateUserStrategy; import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum; import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants; import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils; import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService; import cn.iocoder.yudao.module.system.api.user.AdminUserApi; import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; import org.flowable.bpmn.model.BpmnModel; import org.flowable.bpmn.model.ExtensionElement; import org.flowable.bpmn.model.FlowElement; import org.flowable.bpmn.model.UserTask; import org.flowable.engine.delegate.DelegateExecution; import org.flowable.engine.runtime.ProcessInstance; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mock; import org.mockito.MockedStatic; import org.mockito.Spy; import org.mockito.internal.util.collections.Sets; import java.util.*; import static cn.iocoder.yudao.framework.common.util.collection.SetUtils.asSet; import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomString; import static org.flowable.bpmn.constants.BpmnXMLConstants.FLOWABLE_EXTENSIONS_NAMESPACE; import static org.flowable.bpmn.constants.BpmnXMLConstants.FLOWABLE_EXTENSIONS_PREFIX; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.*; /** * {@link BpmTaskCandidateInvoker} 的单元测试 * * @author 芋道源码 */ public class BpmTaskCandidateInvokerTest extends BaseMockitoUnitTest { private BpmTaskCandidateInvoker taskCandidateInvoker; @Mock private AdminUserApi adminUserApi; @Mock private BpmProcessInstanceService processInstanceService; @Spy private BpmTaskCandidateStrategy userStrategy; @Mock private BpmTaskCandidateAssignEmptyStrategy emptyStrategy; @Spy private List strategyList; @BeforeEach public void setUp() { userStrategy = new BpmTaskCandidateUserStrategy(); // 创建 strategy 实例 when(emptyStrategy.getStrategy()).thenReturn(BpmTaskCandidateStrategyEnum.ASSIGN_EMPTY); strategyList = List.of(userStrategy, emptyStrategy); // 创建 strategyList taskCandidateInvoker = new BpmTaskCandidateInvoker(strategyList, adminUserApi); } /** * 场景:成功计算到候选人,但是移除了发起人的用户 */ @Test public void testCalculateUsersByTask_some() { try (MockedStatic springUtilMockedStatic = mockStatic(SpringUtil.class)) { // 准备参数 String param = "1,2"; DelegateExecution execution = mock(DelegateExecution.class); // mock 方法(DelegateExecution) UserTask userTask = mock(UserTask.class); String processInstanceId = randomString(); when(execution.getProcessInstanceId()).thenReturn(processInstanceId); when(execution.getCurrentFlowElement()).thenReturn(userTask); when(userTask.getAttributeValue(eq(BpmnModelConstants.NAMESPACE), eq(BpmnModelConstants.USER_TASK_CANDIDATE_STRATEGY))) .thenReturn(BpmTaskCandidateStrategyEnum.USER.getStrategy().toString()); when(userTask.getAttributeValue(eq(BpmnModelConstants.NAMESPACE), eq(BpmnModelConstants.USER_TASK_CANDIDATE_PARAM))) .thenReturn(param); // mock 方法(adminUserApi) AdminUserRespDTO user1 = randomPojo(AdminUserRespDTO.class, o -> o.setId(1L) .setStatus(CommonStatusEnum.ENABLE.getStatus())); AdminUserRespDTO user2 = randomPojo(AdminUserRespDTO.class, o -> o.setId(2L) .setStatus(CommonStatusEnum.ENABLE.getStatus())); Map userMap = MapUtil.builder(user1.getId(), user1) .put(user2.getId(), user2).build(); when(adminUserApi.getUserMap(eq(asSet(1L, 2L)))).thenReturn(userMap); // mock 移除发起人的用户 springUtilMockedStatic.when(() -> SpringUtil.getBean(BpmProcessInstanceService.class)) .thenReturn(processInstanceService); ProcessInstance processInstance = mock(ProcessInstance.class); when(processInstanceService.getProcessInstance(eq(processInstanceId))).thenReturn(processInstance); when(processInstance.getStartUserId()).thenReturn("1"); mockFlowElementExtensionElement(userTask, BpmnModelConstants.USER_TASK_ASSIGN_START_USER_HANDLER_TYPE, String.valueOf(BpmUserTaskAssignStartUserHandlerTypeEnum.SKIP.getType())); // 调用 Set results = taskCandidateInvoker.calculateUsersByTask(execution); // 断言 assertEquals(asSet(2L), results); } } /** * 场景:没有计算到候选人,但是被禁用移除,最终通过 empty 进行分配 */ @Test public void testCalculateUsersByTask_none() { try (MockedStatic springUtilMockedStatic = mockStatic(SpringUtil.class)) { // 准备参数 String param = "1,2"; DelegateExecution execution = mock(DelegateExecution.class); // mock 方法(DelegateExecution) UserTask userTask = mock(UserTask.class); String processInstanceId = randomString(); when(execution.getProcessInstanceId()).thenReturn(processInstanceId); when(execution.getCurrentFlowElement()).thenReturn(userTask); when(userTask.getAttributeValue(eq(BpmnModelConstants.NAMESPACE), eq(BpmnModelConstants.USER_TASK_CANDIDATE_STRATEGY))) .thenReturn(BpmTaskCandidateStrategyEnum.USER.getStrategy().toString()); when(userTask.getAttributeValue(eq(BpmnModelConstants.NAMESPACE), eq(BpmnModelConstants.USER_TASK_CANDIDATE_PARAM))) .thenReturn(param); // mock 方法(adminUserApi) AdminUserRespDTO user1 = randomPojo(AdminUserRespDTO.class, o -> o.setId(1L) .setStatus(CommonStatusEnum.DISABLE.getStatus())); AdminUserRespDTO user2 = randomPojo(AdminUserRespDTO.class, o -> o.setId(2L) .setStatus(CommonStatusEnum.DISABLE.getStatus())); Map userMap = MapUtil.builder(user1.getId(), user1) .put(user2.getId(), user2).build(); when(adminUserApi.getUserMap(eq(asSet(1L, 2L)))).thenReturn(userMap); // mock 方法(empty) when(emptyStrategy.calculateUsersByTask(same(execution), same(param))) .thenReturn(Sets.newSet(2L)); // mock 移除发起人的用户 springUtilMockedStatic.when(() -> SpringUtil.getBean(BpmProcessInstanceService.class)) .thenReturn(processInstanceService); ProcessInstance processInstance = mock(ProcessInstance.class); when(processInstanceService.getProcessInstance(eq(processInstanceId))).thenReturn(processInstance); when(processInstance.getStartUserId()).thenReturn("1"); // 调用 Set results = taskCandidateInvoker.calculateUsersByTask(execution); // 断言 assertEquals(asSet(2L), results); } } /** * 场景:没有计算到候选人,但是被禁用移除,最终通过 empty 进行分配 */ @Test public void testCalculateUsersByActivity_some() { try (MockedStatic bpmnModelUtilsMockedStatic = mockStatic(BpmnModelUtils.class)) { // 准备参数 String param = "1,2"; BpmnModel bpmnModel = mock(BpmnModel.class); String activityId = randomString(); Long startUserId = 1L; String processDefinitionId = randomString(); Map processVariables = new HashMap<>(); // mock 方法(DelegateExecution) UserTask userTask = mock(UserTask.class); bpmnModelUtilsMockedStatic.when(() -> BpmnModelUtils.parseCandidateStrategy(same(userTask))) .thenReturn(BpmTaskCandidateStrategyEnum.USER.getStrategy()); bpmnModelUtilsMockedStatic.when(() -> BpmnModelUtils.parseCandidateParam(same(userTask))) .thenReturn(param); bpmnModelUtilsMockedStatic.when(() -> BpmnModelUtils.getFlowElementById(same(bpmnModel), eq(activityId))).thenReturn(userTask); // mock 方法(adminUserApi) AdminUserRespDTO user1 = randomPojo(AdminUserRespDTO.class, o -> o.setId(1L) .setStatus(CommonStatusEnum.ENABLE.getStatus())); AdminUserRespDTO user2 = randomPojo(AdminUserRespDTO.class, o -> o.setId(2L) .setStatus(CommonStatusEnum.ENABLE.getStatus())); Map userMap = MapUtil.builder(user1.getId(), user1) .put(user2.getId(), user2).build(); when(adminUserApi.getUserMap(eq(asSet(1L, 2L)))).thenReturn(userMap); // mock 移除发起人的用户 bpmnModelUtilsMockedStatic.when(() -> BpmnModelUtils.parseAssignStartUserHandlerType(same(userTask))) .thenReturn(BpmUserTaskAssignStartUserHandlerTypeEnum.SKIP.getType()); // 调用 Set results = taskCandidateInvoker.calculateUsersByActivity(bpmnModel, activityId, startUserId, processDefinitionId, processVariables); // 断言 assertEquals(asSet(2L), results); } } /** * 场景:成功计算到候选人,但是移除了发起人的用户 */ @Test public void testCalculateUsersByActivity_none() { try (MockedStatic bpmnModelUtilsMockedStatic = mockStatic(BpmnModelUtils.class)) { // 准备参数 String param = "1,2"; BpmnModel bpmnModel = mock(BpmnModel.class); String activityId = randomString(); Long startUserId = 1L; String processDefinitionId = randomString(); Map processVariables = new HashMap<>(); // mock 方法(DelegateExecution) UserTask userTask = mock(UserTask.class); bpmnModelUtilsMockedStatic.when(() -> BpmnModelUtils.parseCandidateStrategy(same(userTask))) .thenReturn(BpmTaskCandidateStrategyEnum.USER.getStrategy()); bpmnModelUtilsMockedStatic.when(() -> BpmnModelUtils.parseCandidateParam(same(userTask))) .thenReturn(param); bpmnModelUtilsMockedStatic.when(() -> BpmnModelUtils.getFlowElementById(same(bpmnModel), eq(activityId))).thenReturn(userTask); // mock 方法(adminUserApi) AdminUserRespDTO user1 = randomPojo(AdminUserRespDTO.class, o -> o.setId(1L) .setStatus(CommonStatusEnum.DISABLE.getStatus())); AdminUserRespDTO user2 = randomPojo(AdminUserRespDTO.class, o -> o.setId(2L) .setStatus(CommonStatusEnum.DISABLE.getStatus())); Map userMap = MapUtil.builder(user1.getId(), user1) .put(user2.getId(), user2).build(); when(adminUserApi.getUserMap(eq(asSet(1L, 2L)))).thenReturn(userMap); // mock 方法(empty) when(emptyStrategy.calculateUsersByActivity(same(bpmnModel), eq(activityId), eq(param), same(startUserId), same(processDefinitionId), same(processVariables))) .thenReturn(Sets.newSet(2L)); // 调用 Set results = taskCandidateInvoker.calculateUsersByActivity(bpmnModel, activityId, startUserId, processDefinitionId, processVariables); // 断言 assertEquals(asSet(2L), results); } } private static void mockFlowElementExtensionElement(FlowElement element, String name, String value) { if (value == null) { return; } ExtensionElement extensionElement = new ExtensionElement(); extensionElement.setNamespace(FLOWABLE_EXTENSIONS_NAMESPACE); extensionElement.setNamespacePrefix(FLOWABLE_EXTENSIONS_PREFIX); extensionElement.setElementText(value); extensionElement.setName(name); // mock Map> extensionElements = element.getExtensionElements(); if (extensionElements == null) { extensionElements = new LinkedHashMap<>(); } extensionElements.put(name, Collections.singletonList(extensionElement)); when(element.getExtensionElements()).thenReturn(extensionElements); } @Test public void testRemoveDisableUsers() { // 准备参数. 1L 可以找到;2L 是禁用的;3L 找不到 Set assigneeUserIds = asSet(1L, 2L, 3L); // mock 方法 AdminUserRespDTO user1 = randomPojo(AdminUserRespDTO.class, o -> o.setId(1L) .setStatus(CommonStatusEnum.ENABLE.getStatus())); AdminUserRespDTO user2 = randomPojo(AdminUserRespDTO.class, o -> o.setId(2L) .setStatus(CommonStatusEnum.DISABLE.getStatus())); Map userMap = MapUtil.builder(user1.getId(), user1) .put(user2.getId(), user2).build(); when(adminUserApi.getUserMap(eq(assigneeUserIds))).thenReturn(userMap); // 调用 taskCandidateInvoker.removeDisableUsers(assigneeUserIds); // 断言 assertEquals(asSet(1L), assigneeUserIds); } }