/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.apache.dolphinscheduler.api.service;

import static org.mockito.ArgumentMatchers.isA;

import org.apache.dolphinscheduler.api.dto.taskRelation.TaskRelationCreateRequest;
import org.apache.dolphinscheduler.api.enums.Status;
import org.apache.dolphinscheduler.api.exceptions.ServiceException;
import org.apache.dolphinscheduler.api.service.impl.ProcessTaskRelationServiceImpl;
import org.apache.dolphinscheduler.api.service.impl.ProjectServiceImpl;
import org.apache.dolphinscheduler.common.constants.Constants;
import org.apache.dolphinscheduler.common.enums.UserType;
import org.apache.dolphinscheduler.dao.entity.ProcessDefinition;
import org.apache.dolphinscheduler.dao.entity.ProcessTaskRelation;
import org.apache.dolphinscheduler.dao.entity.ProcessTaskRelationLog;
import org.apache.dolphinscheduler.dao.entity.Project;
import org.apache.dolphinscheduler.dao.entity.TaskDefinition;
import org.apache.dolphinscheduler.dao.entity.TaskDefinitionLog;
import org.apache.dolphinscheduler.dao.entity.User;
import org.apache.dolphinscheduler.dao.mapper.ProcessDefinitionMapper;
import org.apache.dolphinscheduler.dao.mapper.ProcessTaskRelationLogMapper;
import org.apache.dolphinscheduler.dao.mapper.ProcessTaskRelationMapper;
import org.apache.dolphinscheduler.dao.mapper.ProjectMapper;
import org.apache.dolphinscheduler.dao.mapper.TaskDefinitionLogMapper;
import org.apache.dolphinscheduler.dao.mapper.TaskDefinitionMapper;
import org.apache.dolphinscheduler.service.process.ProcessService;

import org.apache.commons.collections4.CollectionUtils;

import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

import org.junit.jupiter.api.Assertions;
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.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;
import org.mockito.junit.jupiter.MockitoSettings;
import org.mockito.quality.Strictness;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.google.common.collect.Lists;

/**
 * process task instance relation service test
 */
@ExtendWith(MockitoExtension.class)
@MockitoSettings(strictness = Strictness.LENIENT)
public class ProcessTaskRelationServiceTest {

    @InjectMocks
    ProcessTaskRelationServiceImpl processTaskRelationService;

    @Mock
    private ProjectMapper projectMapper;

    @Mock
    private ProjectServiceImpl projectService;

    @Mock
    private ProcessTaskRelationMapper processTaskRelationMapper;

    @Mock
    private TaskDefinitionLogMapper taskDefinitionLogMapper;

    @Mock
    private ProcessDefinitionMapper processDefinitionMapper;

    @Mock
    private TaskDefinitionMapper taskDefinitionMapper;

    @Mock
    private ProcessTaskRelationLogMapper processTaskRelationLogMapper;

    @Mock
    private ProcessService processService;

    private static final long PROJECT_CODE = 1L;
    private static final long PROCESS_DEFINITION_CODE = 2L;
    private static final long UPSTREAM_TASK_CODE = 3L;
    private static final long DOWNSTREAM_TASK_CODE = 4L;
    protected User user;
    protected Exception exception;

    @BeforeEach
    public void before() {
        User loginUser = new User();
        loginUser.setId(1);
        loginUser.setTenantId(2);
        loginUser.setUserType(UserType.GENERAL_USER);
        loginUser.setUserName("admin");
        user = loginUser;
    }

    /**
     * get mock Project
     *
     * @param projectCode projectCode
     * @return Project
     */
    private Project getProject(long projectCode) {
        Project project = new Project();
        project.setCode(projectCode);
        project.setId(1);
        project.setName("project_test1");
        project.setUserId(1);
        return project;
    }

    private void putMsg(Map<String, Object> result, Status status, Object... statusParams) {
        result.put(Constants.STATUS, status);
        if (statusParams != null && statusParams.length > 0) {
            result.put(Constants.MSG, MessageFormat.format(status.getMsg(), statusParams));
        } else {
            result.put(Constants.MSG, status.getMsg());
        }
    }

    private TaskDefinitionLog buildTaskDefinitionLog(long projectCode, long code, int version) {

        TaskDefinitionLog taskDefinitionLog = new TaskDefinitionLog() {

            @Override
            public boolean equals(Object o) {
                if (this == o) {
                    return true;
                }
                if (!(o instanceof TaskDefinitionLog)) {
                    return false;
                }
                TaskDefinitionLog that = (TaskDefinitionLog) o;
                return getCode() == that.getCode()
                        && getVersion() == that.getVersion()
                        && getProjectCode() == that.getProjectCode();
            }

            @Override
            public int hashCode() {
                return Objects.hash(getCode(), getVersion(), getProjectCode());
            }
        };
        taskDefinitionLog.setProjectCode(projectCode);
        taskDefinitionLog.setCode(code);
        taskDefinitionLog.setVersion(version);
        return taskDefinitionLog;
    }

    private TaskDefinition buildTaskDefinition(long projectCode, long code, int version) {

        TaskDefinition taskDefinition = new TaskDefinition() {

            @Override
            public boolean equals(Object o) {
                if (this == o) {
                    return true;
                }
                if (!(o instanceof TaskDefinition)) {
                    return false;
                }
                TaskDefinition that = (TaskDefinition) o;
                return getCode() == that.getCode()
                        && getVersion() == that.getVersion()
                        && getProjectCode() == that.getProjectCode();
            }

            @Override
            public int hashCode() {
                return Objects.hash(getCode(), getVersion(), getProjectCode());
            }
        };
        taskDefinition.setProjectCode(projectCode);
        taskDefinition.setCode(code);
        taskDefinition.setVersion(version);
        return taskDefinition;
    }

    private List<ProcessTaskRelation> getProcessTaskUpstreamRelationList(long projectCode, long taskCode) {
        ProcessTaskRelation processTaskRelationUpstream0 = new ProcessTaskRelation();
        processTaskRelationUpstream0.setPostTaskCode(taskCode);
        processTaskRelationUpstream0.setPreTaskVersion(1);
        processTaskRelationUpstream0.setProjectCode(projectCode);
        processTaskRelationUpstream0.setPreTaskCode(123);
        processTaskRelationUpstream0.setProcessDefinitionCode(123);
        ProcessTaskRelation processTaskRelationUpstream1 = new ProcessTaskRelation();
        processTaskRelationUpstream1.setPostTaskCode(taskCode);
        processTaskRelationUpstream1.setPreTaskVersion(1);
        processTaskRelationUpstream1.setPreTaskCode(123);
        processTaskRelationUpstream1.setProcessDefinitionCode(124);
        processTaskRelationUpstream1.setProjectCode(projectCode);
        ProcessTaskRelation processTaskRelationUpstream2 = new ProcessTaskRelation();
        processTaskRelationUpstream2.setPostTaskCode(taskCode);
        processTaskRelationUpstream2.setPreTaskVersion(2);
        processTaskRelationUpstream2.setPreTaskCode(123);
        processTaskRelationUpstream2.setProcessDefinitionCode(125);
        processTaskRelationUpstream2.setProjectCode(projectCode);
        List<ProcessTaskRelation> processTaskRelationList = new ArrayList<>();
        processTaskRelationList.add(processTaskRelationUpstream0);
        processTaskRelationList.add(processTaskRelationUpstream1);
        processTaskRelationList.add(processTaskRelationUpstream2);
        return processTaskRelationList;
    }

    private List<ProcessTaskRelation> getProcessTaskDownstreamRelationList(long projectCode, long taskCode) {
        ProcessTaskRelation processTaskRelationDownstream0 = new ProcessTaskRelation();
        processTaskRelationDownstream0.setPreTaskCode(taskCode);
        processTaskRelationDownstream0.setPostTaskCode(456);
        processTaskRelationDownstream0.setPostTaskVersion(1);
        processTaskRelationDownstream0.setProjectCode(projectCode);
        ProcessTaskRelation processTaskRelationDownstream1 = new ProcessTaskRelation();
        processTaskRelationDownstream1.setPreTaskCode(taskCode);
        processTaskRelationDownstream1.setPostTaskCode(456);
        processTaskRelationDownstream1.setPostTaskVersion(1);
        processTaskRelationDownstream1.setProjectCode(projectCode);
        ProcessTaskRelation processTaskRelationDownstream2 = new ProcessTaskRelation();
        processTaskRelationDownstream2.setPreTaskCode(taskCode);
        processTaskRelationDownstream2.setPostTaskCode(4567);
        processTaskRelationDownstream2.setPostTaskVersion(1);
        processTaskRelationDownstream2.setProjectCode(projectCode);
        List<ProcessTaskRelation> processTaskRelationList = new ArrayList<>();
        processTaskRelationList.add(processTaskRelationDownstream0);
        processTaskRelationList.add(processTaskRelationDownstream1);
        processTaskRelationList.add(processTaskRelationDownstream2);
        return processTaskRelationList;
    }

    private ProcessDefinition getProcessDefinition() {
        ProcessDefinition processDefinition = new ProcessDefinition();
        processDefinition.setId(1);
        processDefinition.setProjectCode(1L);
        processDefinition.setName("test_pdf");
        processDefinition.setDescription("");
        processDefinition.setCode(1L);
        processDefinition.setVersion(1);
        return processDefinition;
    }

    private TaskDefinition getTaskDefinition() {
        TaskDefinition taskDefinition = new TaskDefinition();
        taskDefinition.setProjectCode(1L);
        taskDefinition.setCode(1L);
        taskDefinition.setVersion(1);
        taskDefinition.setTaskType("SHELL");
        return taskDefinition;
    }

    @Test
    public void testCreateProcessTaskRelation() {
        long projectCode = 1L;
        long processDefinitionCode = 1L;
        long preTaskCode = 0L;
        long postTaskCode = 1L;

        Project project = getProject(projectCode);
        Mockito.when(projectMapper.queryByCode(projectCode)).thenReturn(project);

        Map<String, Object> result = new HashMap<>();
        putMsg(result, Status.SUCCESS, projectCode);
        Mockito.when(projectService.checkProjectAndAuth(user, project, projectCode, null)).thenReturn(result);
        Mockito.when(processDefinitionMapper.queryByCode(processDefinitionCode)).thenReturn(getProcessDefinition());
        Mockito.when(
                processTaskRelationMapper.queryByCode(projectCode, processDefinitionCode, preTaskCode, postTaskCode))
                .thenReturn(Lists.newArrayList());
        Mockito.when(taskDefinitionMapper.queryByCode(postTaskCode)).thenReturn(getTaskDefinition());

        List<ProcessTaskRelation> processTaskRelationList = Lists.newArrayList();
        List<ProcessTaskRelationLog> processTaskRelationLogList = Lists.newArrayList();
        ProcessTaskRelation processTaskRelation = new ProcessTaskRelation();
        processTaskRelation.setProjectCode(projectCode);
        processTaskRelation.setProcessDefinitionCode(processDefinitionCode);
        processTaskRelation.setPreTaskCode(0L);
        processTaskRelation.setPreTaskVersion(0);
        processTaskRelation.setPostTaskCode(postTaskCode);
        processTaskRelation.setPostTaskVersion(1);
        processTaskRelationList.add(processTaskRelation);
        processTaskRelationLogList.add(new ProcessTaskRelationLog(processTaskRelation));

        Mockito.when(processTaskRelationMapper.batchInsert(processTaskRelationList)).thenReturn(1);
        Mockito.when(processTaskRelationLogMapper.batchInsert(processTaskRelationLogList)).thenReturn(1);
        Assertions.assertEquals(Status.SUCCESS, result.get(Constants.STATUS));
    }

    @Test
    public void testQueryDownstreamRelation() {
        long projectCode = 1L;
        long taskCode = 2L;

        Project project = getProject(projectCode);
        Mockito.when(projectMapper.queryByCode(projectCode)).thenReturn(project);

        Map<String, Object> result = new HashMap<>();
        putMsg(result, Status.SUCCESS, projectCode);
        Mockito.when(projectService.checkProjectAndAuth(user, project, projectCode, null)).thenReturn(result);

        List<ProcessTaskRelation> processTaskRelationList = getProcessTaskDownstreamRelationList(projectCode, taskCode);

        Mockito.when(processTaskRelationMapper.queryDownstreamByCode(projectCode, taskCode))
                .thenReturn(processTaskRelationList);

        if (CollectionUtils.isNotEmpty(processTaskRelationList)) {
            Set<TaskDefinition> taskDefinitions = processTaskRelationList
                    .stream()
                    .map(processTaskRelation -> {
                        TaskDefinition taskDefinition = buildTaskDefinition(
                                processTaskRelation.getProjectCode(),
                                processTaskRelation.getPostTaskCode(),
                                processTaskRelation.getPostTaskVersion());
                        return taskDefinition;
                    })
                    .collect(Collectors.toSet());

            Set<TaskDefinitionLog> taskDefinitionLogSet = processTaskRelationList
                    .stream()
                    .map(processTaskRelation -> {
                        TaskDefinitionLog taskDefinitionLog = buildTaskDefinitionLog(
                                processTaskRelation.getProjectCode(),
                                processTaskRelation.getPostTaskCode(),
                                processTaskRelation.getPostTaskVersion());
                        return taskDefinitionLog;
                    })
                    .collect(Collectors.toSet());
            List<TaskDefinitionLog> taskDefinitionLogList = taskDefinitionLogSet.stream().collect(Collectors.toList());
            Mockito.when(taskDefinitionLogMapper.queryByTaskDefinitions(taskDefinitions))
                    .thenReturn(taskDefinitionLogList);
        }
        Map<String, Object> relation = processTaskRelationService
                .queryDownstreamRelation(user, projectCode, taskCode);
        Assertions.assertEquals(Status.SUCCESS, relation.get(Constants.STATUS));
        Assertions.assertEquals(2, ((List) relation.get("data")).size());
    }

    @Test
    public void testQueryUpstreamRelation() {
        long projectCode = 1L;
        long taskCode = 2L;

        Project project = getProject(projectCode);
        Mockito.when(projectMapper.queryByCode(projectCode)).thenReturn(project);

        Map<String, Object> result = new HashMap<>();
        putMsg(result, Status.SUCCESS, projectCode);
        Mockito.when(projectService.checkProjectAndAuth(user, project, projectCode, null)).thenReturn(result);
        List<ProcessTaskRelation> processTaskRelationList = getProcessTaskUpstreamRelationList(projectCode, taskCode);

        Mockito.when(processTaskRelationMapper.queryUpstreamByCode(projectCode, taskCode))
                .thenReturn(processTaskRelationList);

        if (CollectionUtils.isNotEmpty(processTaskRelationList)) {
            Set<TaskDefinition> taskDefinitions = processTaskRelationList
                    .stream()
                    .map(processTaskRelation -> {
                        TaskDefinition taskDefinition = buildTaskDefinition(
                                processTaskRelation.getProjectCode(),
                                processTaskRelation.getPreTaskCode(),
                                processTaskRelation.getPreTaskVersion());
                        return taskDefinition;
                    })
                    .collect(Collectors.toSet());

            Set<TaskDefinitionLog> taskDefinitionLogSet = processTaskRelationList
                    .stream()
                    .map(processTaskRelation -> {
                        TaskDefinitionLog taskDefinitionLog = buildTaskDefinitionLog(
                                processTaskRelation.getProjectCode(),
                                processTaskRelation.getPreTaskCode(),
                                processTaskRelation.getPreTaskVersion());
                        return taskDefinitionLog;
                    })
                    .collect(Collectors.toSet());
            List<TaskDefinitionLog> taskDefinitionLogList = taskDefinitionLogSet.stream().collect(Collectors.toList());
            Mockito.when(taskDefinitionLogMapper.queryByTaskDefinitions(taskDefinitions))
                    .thenReturn(taskDefinitionLogList);
        }
        Map<String, Object> relation = processTaskRelationService
                .queryUpstreamRelation(user, projectCode, taskCode);
        Assertions.assertEquals(Status.SUCCESS, relation.get(Constants.STATUS));
        Assertions.assertEquals(2, ((List) relation.get("data")).size());
    }

    @Test
    public void testDeleteDownstreamRelation() {
        long projectCode = 1L;
        long taskCode = 2L;
        Project project = getProject(projectCode);
        Mockito.when(projectMapper.queryByCode(projectCode)).thenReturn(project);

        Map<String, Object> result = new HashMap<>();
        putMsg(result, Status.SUCCESS, projectCode);
        Mockito.when(projectService.checkProjectAndAuth(user, project, projectCode, null)).thenReturn(result);
        List<ProcessTaskRelation> processTaskRelationList = new ArrayList<>();
        ProcessTaskRelation processTaskRelation = new ProcessTaskRelation();
        processTaskRelation.setProjectCode(projectCode);
        processTaskRelation.setProcessDefinitionCode(1L);
        processTaskRelation.setPreTaskCode(taskCode);
        processTaskRelation.setPostTaskCode(123L);
        processTaskRelationList.add(processTaskRelation);
        Mockito.when(processTaskRelationMapper.queryDownstreamByCode(projectCode, taskCode))
                .thenReturn(processTaskRelationList);
        ProcessTaskRelationLog processTaskRelationLog = new ProcessTaskRelationLog(processTaskRelation);
        Mockito.when(processTaskRelationMapper.deleteRelation(processTaskRelationLog)).thenReturn(1);
        Mockito.when(processTaskRelationLogMapper.deleteRelation(processTaskRelationLog)).thenReturn(1);
        ProcessDefinition processDefinition = getProcessDefinition();
        Mockito.when(processDefinitionMapper.queryByCode(1L)).thenReturn(processDefinition);
        Mockito.when(processService.saveProcessDefine(user, processDefinition, Boolean.TRUE, Boolean.TRUE))
                .thenReturn(1);
        Map<String, Object> result1 =
                processTaskRelationService.deleteDownstreamRelation(user, projectCode, "123", taskCode);
        Assertions.assertEquals(Status.SUCCESS, result1.get(Constants.STATUS));
    }

    @Test
    public void testDeleteUpstreamRelation() {
        long projectCode = 1L;
        long taskCode = 2L;
        Project project = getProject(projectCode);
        Mockito.when(projectMapper.queryByCode(projectCode)).thenReturn(project);

        Map<String, Object> result = new HashMap<>();
        putMsg(result, Status.SUCCESS, projectCode);
        List<ProcessTaskRelation> processTaskRelationList = Lists.newArrayList();
        ProcessTaskRelation processTaskRelation = new ProcessTaskRelation();
        processTaskRelation.setProjectCode(projectCode);
        processTaskRelation.setProcessDefinitionCode(1L);
        processTaskRelation.setPreTaskCode(0L);
        processTaskRelation.setPreTaskVersion(0);
        processTaskRelation.setPostTaskCode(taskCode);
        processTaskRelation.setPostTaskVersion(1);
        processTaskRelationList.add(processTaskRelation);
        Mockito.when(projectService.checkProjectAndAuth(user, project, projectCode, null)).thenReturn(result);
        Mockito.when(processTaskRelationMapper.queryUpstreamByCode(projectCode, taskCode))
                .thenReturn(processTaskRelationList);
        Mockito.when(processDefinitionMapper.queryByCode(1L)).thenReturn(getProcessDefinition());
        Mockito.when(processTaskRelationMapper.queryByProcessCode(1L)).thenReturn(processTaskRelationList);
        List<ProcessTaskRelationLog> relationLogs =
                processTaskRelationList.stream().map(ProcessTaskRelationLog::new).collect(Collectors.toList());
        Mockito.when(processService.saveTaskRelation(user, 1L, 1L,
                1, relationLogs, Lists.newArrayList(), Boolean.TRUE)).thenReturn(0);
        Assertions.assertEquals(Status.SUCCESS, result.get(Constants.STATUS));
    }

    @Test
    public void testDeleteTaskProcessRelation() {
        long projectCode = 1L;
        long taskCode = 1L;
        long processDefinitionCode = 1L;
        long preTaskCode = 4L;
        long postTaskCode = 5L;
        Project project = getProject(projectCode);
        Mockito.when(projectMapper.queryByCode(projectCode)).thenReturn(project);

        Map<String, Object> result = new HashMap<>();
        putMsg(result, Status.SUCCESS, projectCode);
        Mockito.when(projectService.checkProjectAndAuth(user, project, projectCode, null)).thenReturn(result);
        Mockito.when(
                processTaskRelationMapper.queryByCode(projectCode, processDefinitionCode, preTaskCode, postTaskCode))
                .thenReturn(Lists.newArrayList());
        Mockito.when(processDefinitionMapper.queryByCode(processDefinitionCode)).thenReturn(getProcessDefinition());
        Mockito.when(taskDefinitionMapper.queryByCode(taskCode)).thenReturn(getTaskDefinition());
        TaskDefinition taskDefinition = new TaskDefinition();
        taskDefinition.setTaskType("CONDITIONS");
        Mockito.when(taskDefinitionMapper.queryByCode(taskCode)).thenReturn(taskDefinition);
        List<ProcessTaskRelation> processTaskRelationList = Lists.newArrayList();
        ProcessTaskRelation processTaskRelation = new ProcessTaskRelation();
        processTaskRelation.setProjectCode(projectCode);
        processTaskRelation.setProcessDefinitionCode(1L);
        processTaskRelation.setPreTaskCode(0L);
        processTaskRelation.setPreTaskVersion(0);
        processTaskRelation.setPostTaskCode(taskCode);
        processTaskRelation.setPostTaskVersion(1);
        processTaskRelationList.add(processTaskRelation);
        Mockito.when(processTaskRelationMapper.queryByProcessCode(processDefinitionCode))
                .thenReturn(processTaskRelationList);
        List<ProcessTaskRelationLog> relationLogs =
                processTaskRelationList.stream().map(ProcessTaskRelationLog::new).collect(Collectors.toList());

        Mockito.when(processService.saveTaskRelation(user, 1L, 1L,
                1, relationLogs, Lists.newArrayList(), Boolean.TRUE)).thenReturn(0);
        Assertions.assertEquals(Status.SUCCESS, result.get(Constants.STATUS));
    }

    @Test
    public void testDeleteEdge() {
        long projectCode = 1L;
        long processDefinitionCode = 3L;
        long preTaskCode = 0L;
        long postTaskCode = 5L;
        Project project = getProject(projectCode);
        Mockito.when(projectMapper.queryByCode(projectCode)).thenReturn(project);

        Map<String, Object> result = new HashMap<>();
        putMsg(result, Status.SUCCESS, projectCode);
        Mockito.when(projectService.checkProjectAndAuth(user, project, projectCode, null)).thenReturn(result);
        ProcessTaskRelation processTaskRelation = new ProcessTaskRelation();
        processTaskRelation.setProjectCode(projectCode);
        processTaskRelation.setProcessDefinitionCode(processDefinitionCode);
        processTaskRelation.setProcessDefinitionVersion(1);
        processTaskRelation.setPreTaskCode(preTaskCode);
        processTaskRelation.setPostTaskCode(postTaskCode);
        ProcessTaskRelationLog processTaskRelationLog = new ProcessTaskRelationLog(processTaskRelation);
        processTaskRelationLog.setOperator(user.getId());
        List<ProcessTaskRelation> processTaskRelationList = new ArrayList<>();
        processTaskRelationList.add(processTaskRelation);
        Mockito.when(projectService.checkProjectAndAuth(user, project, projectCode, null)).thenReturn(result);
        Mockito.when(processTaskRelationMapper.queryByProcessCode(1L)).thenReturn(processTaskRelationList);
        List<ProcessTaskRelationLog> relationLogs =
                processTaskRelationList.stream().map(ProcessTaskRelationLog::new).collect(Collectors.toList());
        Mockito.when(processService.saveTaskRelation(user, 1L, 1L,
                1, relationLogs, Lists.newArrayList(), Boolean.TRUE)).thenReturn(0);
        Assertions.assertEquals(Status.SUCCESS, result.get(Constants.STATUS));
    }

    @Test
    public void testCreateProcessTaskRelationV2() {
        TaskRelationCreateRequest taskRelationCreateRequest = new TaskRelationCreateRequest();
        taskRelationCreateRequest.setWorkflowCode(PROCESS_DEFINITION_CODE);

        // error process definition not exists
        exception = Assertions.assertThrows(ServiceException.class,
                () -> processTaskRelationService.createProcessTaskRelationV2(user, taskRelationCreateRequest));
        Assertions.assertEquals(Status.PROCESS_DEFINE_NOT_EXIST.getCode(), ((ServiceException) exception).getCode());

        // error project without permissions
        Mockito.when(processDefinitionMapper.queryByCode(PROCESS_DEFINITION_CODE)).thenReturn(getProcessDefinition());
        Mockito.when(projectMapper.queryByCode(PROJECT_CODE)).thenReturn(getProject(PROJECT_CODE));
        Mockito.doThrow(new ServiceException(Status.USER_NO_OPERATION_PROJECT_PERM)).when(projectService)
                .checkProjectAndAuthThrowException(user, getProject(PROJECT_CODE), null);
        exception = Assertions.assertThrows(ServiceException.class,
                () -> processTaskRelationService.createProcessTaskRelationV2(user, taskRelationCreateRequest));
        Assertions.assertEquals(Status.USER_NO_OPERATION_PROJECT_PERM.getCode(),
                ((ServiceException) exception).getCode());

        // error insert process task relation
        Mockito.doNothing().when(projectService).checkProjectAndAuthThrowException(user, getProject(PROJECT_CODE),
                null);
        Mockito.when(processTaskRelationMapper.insert(isA(ProcessTaskRelation.class))).thenReturn(0);
        Mockito.when(taskDefinitionMapper.queryByCode(isA(Long.class))).thenReturn(getTaskDefinition());
        exception = Assertions.assertThrows(ServiceException.class,
                () -> processTaskRelationService.createProcessTaskRelationV2(user, taskRelationCreateRequest));
        Assertions.assertEquals(Status.CREATE_PROCESS_TASK_RELATION_ERROR.getCode(),
                ((ServiceException) exception).getCode());

        // error insert process task relation log
        Mockito.when(processTaskRelationMapper.insert(isA(ProcessTaskRelation.class))).thenReturn(1);
        Mockito.when(processTaskRelationLogMapper.insert(isA(ProcessTaskRelationLog.class))).thenReturn(0);
        exception = Assertions.assertThrows(ServiceException.class,
                () -> processTaskRelationService.createProcessTaskRelationV2(user, taskRelationCreateRequest));
        Assertions.assertEquals(Status.CREATE_PROCESS_TASK_RELATION_LOG_ERROR.getCode(),
                ((ServiceException) exception).getCode());

        // success
        Mockito.when(processTaskRelationLogMapper.insert(isA(ProcessTaskRelationLog.class))).thenReturn(1);
        Assertions.assertDoesNotThrow(
                () -> processTaskRelationService.createProcessTaskRelationV2(user, taskRelationCreateRequest));
    }

    @Test
    public void testDeleteTaskProcessRelationV2() {
        TaskRelationCreateRequest taskRelationCreateRequest = new TaskRelationCreateRequest();
        taskRelationCreateRequest.setWorkflowCode(PROCESS_DEFINITION_CODE);

        // error task relation size
        Mockito.when(
                processTaskRelationMapper.filterProcessTaskRelation(isA(Page.class), isA(ProcessTaskRelation.class)))
                .thenReturn(getMultiProcessTaskRelations());
        exception = Assertions.assertThrows(ServiceException.class, () -> processTaskRelationService
                .deleteTaskProcessRelationV2(user, UPSTREAM_TASK_CODE, DOWNSTREAM_TASK_CODE));
        Assertions.assertEquals(Status.PROCESS_TASK_RELATION_NOT_EXPECT.getCode(),
                ((ServiceException) exception).getCode());

        // success
        Mockito.when(
                processTaskRelationMapper.filterProcessTaskRelation(isA(Page.class), isA(ProcessTaskRelation.class)))
                .thenReturn(getOneProcessTaskRelation());
        Assertions.assertDoesNotThrow(() -> processTaskRelationService.deleteTaskProcessRelationV2(user,
                UPSTREAM_TASK_CODE, DOWNSTREAM_TASK_CODE));
    }

    private IPage<ProcessTaskRelation> getOneProcessTaskRelation() {
        IPage<ProcessTaskRelation> processTaskRelationIPage = new Page<>();
        ProcessTaskRelation processTaskRelation = new ProcessTaskRelation();
        processTaskRelation.setProcessDefinitionCode(PROCESS_DEFINITION_CODE);
        processTaskRelation.setPreTaskCode(UPSTREAM_TASK_CODE);
        processTaskRelation.setPostTaskCode(DOWNSTREAM_TASK_CODE);
        processTaskRelationIPage.setRecords(Collections.singletonList(processTaskRelation));
        return processTaskRelationIPage;
    }

    private IPage<ProcessTaskRelation> getMultiProcessTaskRelations() {
        IPage<ProcessTaskRelation> processTaskRelationIPage = new Page<>();
        List<ProcessTaskRelation> processTaskRelations = new ArrayList<>();

        ProcessTaskRelation processTaskRelation0 = new ProcessTaskRelation();
        processTaskRelation0.setProcessDefinitionCode(PROCESS_DEFINITION_CODE);
        processTaskRelation0.setPreTaskCode(UPSTREAM_TASK_CODE);
        processTaskRelation0.setPostTaskCode(DOWNSTREAM_TASK_CODE);
        processTaskRelations.add(processTaskRelation0);

        ProcessTaskRelation processTaskRelation1 = new ProcessTaskRelation();
        processTaskRelation1.setProcessDefinitionCode(PROCESS_DEFINITION_CODE);
        processTaskRelation1.setPreTaskCode(UPSTREAM_TASK_CODE);
        processTaskRelation1.setPostTaskCode(DOWNSTREAM_TASK_CODE);
        processTaskRelations.add(processTaskRelation1);

        processTaskRelationIPage.setRecords(processTaskRelations);
        return processTaskRelationIPage;
    }
}
