增加工作流实例实现

This commit is contained in:
dengqichen 2024-12-05 18:00:21 +08:00
parent c8a2c5ac2c
commit ff7544a127
15 changed files with 1107 additions and 39 deletions

View File

@ -39,7 +39,7 @@ public class NodeDefinition extends Entity<Long> {
/**
* 节点类型
*/
@Enumerated(EnumType.STRING)
@Enumerated(EnumType.ORDINAL)
@Column(nullable = false)
private NodeTypeEnum type;

View File

@ -39,6 +39,7 @@ public class NodeInstance extends Entity<Long> {
/**
* 节点类型
*/
@Enumerated(EnumType.ORDINAL)
@Column(nullable = false)
private NodeTypeEnum nodeType;
@ -51,6 +52,7 @@ public class NodeInstance extends Entity<Long> {
/**
* 节点状态
*/
@Enumerated(EnumType.ORDINAL)
@Column(nullable = false)
private NodeStatusEnum status;

View File

@ -41,7 +41,7 @@ public class WorkflowDefinition extends Entity<Long> {
/**
* 工作流状态
*/
@Enumerated(EnumType.STRING)
@Enumerated(EnumType.ORDINAL)
@Column(nullable = false)
private WorkflowStatusEnum status = WorkflowStatusEnum.DRAFT;

View File

@ -40,7 +40,7 @@ public class WorkflowInstance extends Entity<Long> {
/**
* 状态
*/
@Enumerated(EnumType.STRING)
@Enumerated(EnumType.ORDINAL)
@Column(nullable = false)
private WorkflowStatusEnum status = WorkflowStatusEnum.RUNNING;

View File

@ -10,13 +10,14 @@ import lombok.Getter;
@AllArgsConstructor
public enum NodeTypeEnum {
START("START", "开始节点"),
END("END", "结束节点"),
TASK("TASK", "任务节点"),
GATEWAY("GATEWAY", "网关节点"),
SUB_PROCESS("SUB_PROCESS", "子流程节点"),
SHELL("SHELL", "Shell脚本节点");
START(0, "START", "开始节点"),
END(1, "END", "结束节点"),
TASK(2, "TASK", "任务节点"),
GATEWAY(3, "GATEWAY", "网关节点"),
SUB_PROCESS(4, "SUB_PROCESS", "子流程节点"),
SHELL(5, "SHELL", "Shell脚本节点");
private final int value;
private final String code;
private final String description;
}

View File

@ -11,21 +11,22 @@ import lombok.Getter;
public enum WorkflowStatusEnum {
// 工作流定义状态
DRAFT("DRAFT", "草稿"),
PUBLISHED("PUBLISHED", "已发布"),
DISABLED("DISABLED", "已禁用"),
DRAFT(0, "DRAFT", "草稿"),
PUBLISHED(1, "PUBLISHED", "已发布"),
DISABLED(2, "DISABLED", "已禁用"),
// 工作流实例状态
CREATED("CREATED", "已创建"),
PENDING("PENDING", "等待执行"),
RUNNING("RUNNING", "执行中"),
PAUSED("PAUSED", "已暂停"),
COMPLETED("COMPLETED", "已完成"),
FAILED("FAILED", "执行失败"),
CANCELLED("CANCELLED", "已取消"),
SUSPENDED("SUSPENDED", "已暂停"),
TERMINATED("TERMINATED", "已终止");
CREATED(3, "CREATED", "已创建"),
PENDING(4, "PENDING", "等待执行"),
RUNNING(5, "RUNNING", "执行中"),
PAUSED(6, "PAUSED", "已暂停"),
COMPLETED(7, "COMPLETED", "已完成"),
FAILED(8, "FAILED", "执行失败"),
CANCELLED(9, "CANCELLED", "已取消"),
SUSPENDED(10, "SUSPENDED", "已暂停"),
TERMINATED(11, "TERMINATED", "已终止");
private final int value;
private final String code;
private final String description;

View File

@ -4,8 +4,53 @@ import com.qqchen.deploy.backend.framework.service.IBaseService;
import com.qqchen.deploy.backend.workflow.dto.WorkflowInstanceDTO;
import com.qqchen.deploy.backend.workflow.entity.WorkflowInstance;
import java.util.Map;
/**
* 工作流实例服务接口
*/
public interface IWorkflowInstanceService extends IBaseService<WorkflowInstance, WorkflowInstanceDTO, Long> {
}
/**
* 创建工作流实例
*
* @param definitionId 工作流定义ID
* @param businessKey 业务标识
* @param variables 初始变量
* @return 工作流实例
*/
WorkflowInstanceDTO createInstance(Long definitionId, String businessKey, Map<String, Object> variables);
/**
* 启动工作流实例
*
* @param id 实例ID
* @return 工作流实例
*/
WorkflowInstanceDTO startInstance(Long id);
/**
* 暂停工作流实例
*
* @param id 实例ID
* @return 工作流实例
*/
WorkflowInstanceDTO pauseInstance(Long id);
/**
* 恢复工作流实例
*
* @param id 实例ID
* @return 工作流实例
*/
WorkflowInstanceDTO resumeInstance(Long id);
/**
* 终止工作流实例
*
* @param id 实例ID
* @param reason 终止原因
* @return 工作流实例
*/
WorkflowInstanceDTO terminateInstance(Long id, String reason);
}

View File

@ -1,11 +1,23 @@
package com.qqchen.deploy.backend.workflow.service.impl;
import com.qqchen.deploy.backend.framework.enums.ResponseCode;
import com.qqchen.deploy.backend.framework.exception.BusinessException;
import com.qqchen.deploy.backend.framework.service.impl.BaseServiceImpl;
import com.qqchen.deploy.backend.workflow.dto.WorkflowInstanceDTO;
import com.qqchen.deploy.backend.workflow.entity.WorkflowDefinition;
import com.qqchen.deploy.backend.workflow.entity.WorkflowInstance;
import com.qqchen.deploy.backend.workflow.enums.WorkflowStatusEnum;
import com.qqchen.deploy.backend.workflow.repository.IWorkflowDefinitionRepository;
import com.qqchen.deploy.backend.workflow.repository.IWorkflowInstanceRepository;
import com.qqchen.deploy.backend.workflow.service.IWorkflowInstanceService;
import com.qqchen.deploy.backend.workflow.service.IWorkflowVariableService;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDateTime;
import java.util.Map;
/**
* 工作流实例服务实现类
@ -13,4 +25,122 @@ import org.springframework.stereotype.Service;
@Slf4j
@Service
public class WorkflowInstanceServiceImpl extends BaseServiceImpl<WorkflowInstance, WorkflowInstanceDTO, Long> implements IWorkflowInstanceService {
}
@Resource
private IWorkflowDefinitionRepository workflowDefinitionRepository;
@Resource
private IWorkflowInstanceRepository workflowInstanceRepository;
@Resource
private IWorkflowVariableService workflowVariableService;
@Override
@Transactional
public WorkflowInstanceDTO createInstance(Long definitionId, String businessKey, Map<String, Object> variables) {
// 1. 查询工作流定义
WorkflowDefinition definition = workflowDefinitionRepository.findById(definitionId)
.orElseThrow(() -> new BusinessException(ResponseCode.WORKFLOW_NOT_FOUND));
// 2. 检查工作流定义状态
if (definition.getStatus() != WorkflowStatusEnum.PUBLISHED) {
throw new BusinessException(ResponseCode.WORKFLOW_NOT_PUBLISHED);
}
if (!definition.getEnabled()) {
throw new BusinessException(ResponseCode.WORKFLOW_DISABLED);
}
// 3. 创建工作流实例
WorkflowInstance instance = new WorkflowInstance();
instance.setDefinition(definition);
instance.setBusinessKey(businessKey);
instance.setStatus(WorkflowStatusEnum.CREATED);
final WorkflowInstance savedInstance = workflowInstanceRepository.save(instance);
// 4. 设置初始变量
if (variables != null && !variables.isEmpty()) {
variables.forEach((key, value) -> workflowVariableService.setVariable(savedInstance.getId(), key, value));
}
return converter.toDto(savedInstance);
}
@Override
@Transactional
public WorkflowInstanceDTO startInstance(Long id) {
// 1. 查询工作流实例
WorkflowInstance instance = workflowInstanceRepository.findById(id)
.orElseThrow(() -> new BusinessException(ResponseCode.WORKFLOW_INSTANCE_NOT_FOUND));
// 2. 检查状态
if (instance.getStatus() != WorkflowStatusEnum.CREATED) {
throw new BusinessException(ResponseCode.WORKFLOW_INSTANCE_NOT_RUNNING);
}
// 3. 更新状态
instance.setStatus(WorkflowStatusEnum.RUNNING);
instance.setStartTime(LocalDateTime.now());
instance = workflowInstanceRepository.save(instance);
return converter.toDto(instance);
}
@Override
@Transactional
public WorkflowInstanceDTO pauseInstance(Long id) {
// 1. 查询工作流实例
WorkflowInstance instance = workflowInstanceRepository.findById(id)
.orElseThrow(() -> new BusinessException(ResponseCode.WORKFLOW_INSTANCE_NOT_FOUND));
// 2. 检查状态
if (!instance.getStatus().canPause()) {
throw new BusinessException(ResponseCode.WORKFLOW_INSTANCE_NOT_RUNNING);
}
// 3. 更新状态
instance.setStatus(WorkflowStatusEnum.PAUSED);
instance = workflowInstanceRepository.save(instance);
return converter.toDto(instance);
}
@Override
@Transactional
public WorkflowInstanceDTO resumeInstance(Long id) {
// 1. 查询工作流实例
WorkflowInstance instance = workflowInstanceRepository.findById(id)
.orElseThrow(() -> new BusinessException(ResponseCode.WORKFLOW_INSTANCE_NOT_FOUND));
// 2. 检查状态
if (!instance.getStatus().canResume()) {
throw new BusinessException(ResponseCode.WORKFLOW_INSTANCE_NOT_RUNNING);
}
// 3. 更新状态
instance.setStatus(WorkflowStatusEnum.RUNNING);
instance = workflowInstanceRepository.save(instance);
return converter.toDto(instance);
}
@Override
@Transactional
public WorkflowInstanceDTO terminateInstance(Long id, String reason) {
// 1. 查询工作流实例
WorkflowInstance instance = workflowInstanceRepository.findById(id)
.orElseThrow(() -> new BusinessException(ResponseCode.WORKFLOW_INSTANCE_NOT_FOUND));
// 2. 检查状态
if (instance.getStatus().isFinalStatus()) {
throw new BusinessException(ResponseCode.WORKFLOW_INSTANCE_NOT_RUNNING);
}
// 3. <EFBFBD><EFBFBD><EFBFBD>新状态
instance.setStatus(WorkflowStatusEnum.TERMINATED);
instance.setEndTime(LocalDateTime.now());
instance.setError(reason);
instance = workflowInstanceRepository.save(instance);
return converter.toDto(instance);
}
}

View File

@ -45,7 +45,7 @@ CREATE TABLE sys_department (
CONSTRAINT UK_department_code UNIQUE (code)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='部门表';
-- 用
-- 用<EFBFBD><EFBFBD><EFBFBD>
CREATE TABLE IF NOT EXISTS sys_user (
id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '主键ID',
create_by VARCHAR(255) NULL COMMENT '创建人',
@ -394,7 +394,7 @@ CREATE TABLE wf_workflow_definition (
code VARCHAR(100) NOT NULL COMMENT '工作流编码',
name VARCHAR(100) NOT NULL COMMENT '工作流名称',
description VARCHAR(255) NULL COMMENT '工作流描述',
status VARCHAR(20) NOT NULL COMMENT '工作流状态(DRAFT/PUBLISHED/DISABLED',
status TINYINT NOT NULL COMMENT '工作流状态(0草稿1已发布2已禁用',
version_no INT NOT NULL DEFAULT 1 COMMENT '版本号',
node_config TEXT NULL COMMENT '节点配置(JSON)',
transition_config TEXT NULL COMMENT '流转配置(JSON)',
@ -418,7 +418,7 @@ CREATE TABLE wf_node_definition (
workflow_definition_id BIGINT NOT NULL COMMENT '工作流定义ID',
node_id VARCHAR(100) NOT NULL COMMENT '节点ID',
name VARCHAR(100) NOT NULL COMMENT '节点名称',
type VARCHAR(20) NOT NULL COMMENT '节点类型(START/END/TASK/GATEWAY',
type TINYINT NOT NULL COMMENT '节点类型(0开始节点1结束节点2任务节点3网关节点4子流程节点5Shell脚本节点',
config TEXT NULL COMMENT '节点配置(JSON)',
order_num INT NOT NULL DEFAULT 0 COMMENT '排序号',
@ -438,7 +438,7 @@ CREATE TABLE wf_workflow_instance (
workflow_definition_id BIGINT NOT NULL COMMENT '工作流定义ID',
business_key VARCHAR(100) NOT NULL COMMENT '业务标识',
status VARCHAR(20) NOT NULL COMMENT '状态(PENDING/RUNNING/COMPLETED/FAILED/CANCELLED',
status TINYINT NOT NULL COMMENT '状态(3已创建4等待执行5执行中6已暂停7已完成8执行失败9已取消10已暂停11已终止',
start_time DATETIME(6) NULL COMMENT '开始时间',
end_time DATETIME(6) NULL COMMENT '结束时间',
error TEXT NULL COMMENT '错误信息',
@ -459,9 +459,9 @@ CREATE TABLE wf_node_instance (
workflow_instance_id BIGINT NOT NULL COMMENT '工作流实例ID',
node_id VARCHAR(100) NOT NULL COMMENT '节点ID',
node_type VARCHAR(20) NOT NULL COMMENT '节点类型',
node_type TINYINT NOT NULL COMMENT '节点类型0开始节点1结束节点2任务节点3网关节点4子流程节点5Shell脚本节点',
name VARCHAR(100) NOT NULL COMMENT '节点名称',
status VARCHAR(20) NOT NULL COMMENT '状态(PENDING/RUNNING/COMPLETED/FAILED/CANCELLED/SKIPPED',
status TINYINT NOT NULL COMMENT '状态(3已创建4等待执行5执行中6已暂停7已完成8执行失败9已取消10已暂停11已终止',
start_time DATETIME(6) NULL COMMENT '开始时间',
end_time DATETIME(6) NULL COMMENT '结束时间',
config TEXT NULL COMMENT '节点配置(JSON)',

View File

@ -163,7 +163,7 @@ INSERT INTO wf_workflow_definition (
) VALUES (
1, 'admin', NOW(), 0, 'admin', NOW(), 0,
'DEPLOY_WORKFLOW', '标准部署流程', '标准的应用部署流程,包含代码拉取、编译构建、部署等步骤',
'PUBLISHED', 1,
1, 1,
'{"startNode":{"type":"START","name":"开始"},"endNode":{"type":"END","name":"结束"},"taskNodes":[{"id":"build","type":"TASK","name":"构建"},{"id":"deploy","type":"TASK","name":"部署"}]}',
'{"transitions":[{"from":"startNode","to":"build"},{"from":"build","to":"deploy"},{"from":"deploy","to":"endNode"}]}',
'{"fields":[{"name":"appName","label":"应用名称","type":"input","required":true},{"name":"branch","label":"分支","type":"select","required":true}]}',
@ -178,22 +178,22 @@ INSERT INTO wf_node_definition (
) VALUES
-- 开始节点
(1, 'admin', NOW(), 0, 'admin', NOW(), 0,
1, 'startNode', '开始', 'START',
1, 'startNode', '开始', 0,
'{"type":"START","name":"开始"}',
1),
-- 构建节点
(2, 'admin', NOW(), 0, 'admin', NOW(), 0,
1, 'build', '构建', 'TASK',
1, 'build', '构建', 2,
'{"type":"TASK","name":"构建","executor":"JENKINS","jenkinsJob":"app-build"}',
2),
-- 部署节点
(3, 'admin', NOW(), 0, 'admin', NOW(), 0,
1, 'deploy', '部署', 'TASK',
1, 'deploy', '部署', 2,
'{"type":"TASK","name":"部署","executor":"SHELL","script":"./deploy.sh"}',
3),
-- 结束节点
(4, 'admin', NOW(), 0, 'admin', NOW(), 0,
1, 'endNode', '结束', 'END',
1, 'endNode', '结束', 1,
'{"type":"END","name":"结束"}',
4);
@ -203,7 +203,7 @@ INSERT INTO wf_workflow_instance (
workflow_definition_id, business_key, status, start_time, end_time, error
) VALUES (
1, 'admin', NOW(), 0, 'admin', NOW(), 0,
1, 'TEST-APP-001', 'RUNNING',
1, 'TEST-APP-001', 5,
NOW(), NULL, NULL
);
@ -214,25 +214,25 @@ INSERT INTO wf_node_instance (
) VALUES
-- 开始节点实例
(1, 'admin', NOW(), 0, 'admin', NOW(), 0,
1, 'startNode', 'START', '开始', 'COMPLETED',
1, 'startNode', 0, '开始', 7,
DATE_SUB(NOW(), INTERVAL 5 MINUTE), DATE_SUB(NOW(), INTERVAL 4 MINUTE),
'{"type":"START","name":"开始"}',
'{"appName":"test-app","branch":"master"}', '{}', NULL, NULL),
-- 构建节点实例
(2, 'admin', NOW(), 0, 'admin', NOW(), 0,
1, 'build', 'TASK', '构建', 'RUNNING',
1, 'build', 2, '构建', 5,
DATE_SUB(NOW(), INTERVAL 3 MINUTE), NULL,
'{"type":"TASK","name":"构建","executor":"JENKINS","jenkinsJob":"app-build"}',
'{"appName":"test-app","branch":"master"}', NULL, NULL, 'startNode'),
-- 部署节点实例
(3, 'admin', NOW(), 0, 'admin', NOW(), 0,
1, 'deploy', 'TASK', '部署', 'RUNNING',
1, 'deploy', 2, '部署', 5,
DATE_SUB(NOW(), INTERVAL 2 MINUTE), NULL,
'{"type":"TASK","name":"部署","executor":"SHELL","script":"./deploy.sh"}',
'{"appName":"test-app","branch":"master"}', NULL, NULL, 'build'),
-- 结束节点实例
(4, 'admin', NOW(), 0, 'admin', NOW(), 0,
1, 'endNode', 'END', '结束', 'COMPLETED',
1, 'endNode', 1, '结束', 7,
DATE_SUB(NOW(), INTERVAL 1 MINUTE), DATE_SUB(NOW(), INTERVAL 0 MINUTE),
'{"type":"END","name":"结束"}',
'{}', '{}', NULL, 'deploy');

View File

@ -0,0 +1,301 @@
package com.qqchen.deploy.backend.workflow.engine;
import com.qqchen.deploy.backend.framework.enums.ResponseCode;
import com.qqchen.deploy.backend.workflow.engine.context.DefaultWorkflowContext;
import com.qqchen.deploy.backend.workflow.engine.context.WorkflowContext;
import com.qqchen.deploy.backend.workflow.engine.exception.WorkflowEngineException;
import com.qqchen.deploy.backend.workflow.engine.executor.NodeExecutor;
import com.qqchen.deploy.backend.workflow.entity.NodeInstance;
import com.qqchen.deploy.backend.workflow.entity.WorkflowDefinition;
import com.qqchen.deploy.backend.workflow.entity.WorkflowInstance;
import com.qqchen.deploy.backend.workflow.enums.NodeStatusEnum;
import com.qqchen.deploy.backend.workflow.enums.NodeTypeEnum;
import com.qqchen.deploy.backend.workflow.enums.WorkflowStatusEnum;
import com.qqchen.deploy.backend.workflow.repository.INodeInstanceRepository;
import com.qqchen.deploy.backend.workflow.repository.IWorkflowDefinitionRepository;
import com.qqchen.deploy.backend.workflow.repository.IWorkflowInstanceRepository;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*;
class DefaultWorkflowEngineTest {
@Mock
private IWorkflowDefinitionRepository workflowDefinitionRepository;
@Mock
private IWorkflowInstanceRepository workflowInstanceRepository;
@Mock
private INodeInstanceRepository nodeInstanceRepository;
@Mock
private Map<NodeTypeEnum, NodeExecutor> nodeExecutors;
@Mock
private DefaultWorkflowContext.Factory workflowContextFactory;
@Mock
private NodeExecutor startNodeExecutor;
@Mock
private NodeExecutor taskNodeExecutor;
@Mock
private DefaultWorkflowContext workflowContext;
@InjectMocks
private DefaultWorkflowEngine workflowEngine;
@BeforeEach
void setUp() {
MockitoAnnotations.openMocks(this);
// 配置基本的Mock行为
when(nodeExecutors.get(NodeTypeEnum.START)).thenReturn(startNodeExecutor);
when(nodeExecutors.get(NodeTypeEnum.TASK)).thenReturn(taskNodeExecutor);
when(workflowContextFactory.create(any())).thenReturn(workflowContext);
}
@Test
void startWorkflow_Success() {
// 准备测试数据
String workflowCode = "test-workflow";
String businessKey = "test-key";
Map<String, Object> variables = new HashMap<>();
variables.put("key1", "value1");
WorkflowDefinition definition = new WorkflowDefinition();
definition.setCode(workflowCode);
definition.setStatus(WorkflowStatusEnum.PUBLISHED);
NodeInstance startNode = new NodeInstance();
startNode.setId(1L);
startNode.setNodeType(NodeTypeEnum.START);
startNode.setStatus(NodeStatusEnum.PENDING);
// 配置Mock行为
when(workflowDefinitionRepository.findByCodeAndDeletedFalse(workflowCode)).thenReturn(definition);
when(workflowInstanceRepository.save(any())).thenAnswer(i -> i.getArgument(0));
when(nodeInstanceRepository.save(any())).thenReturn(startNode);
doNothing().when(startNodeExecutor).execute(any(), any());
// 执行测试
WorkflowInstance result = workflowEngine.startWorkflow(workflowCode, businessKey, variables);
// 验证结果
assertNotNull(result);
assertEquals(WorkflowStatusEnum.RUNNING, result.getStatus());
assertNotNull(result.getStartTime());
verify(workflowDefinitionRepository).findByCodeAndDeletedFalse(workflowCode);
verify(workflowInstanceRepository).save(any());
verify(nodeInstanceRepository).save(any());
verify(workflowContextFactory).create(any());
verify(workflowContext, times(variables.size())).setVariable(anyString(), any());
}
@Test
void startWorkflow_WorkflowNotFound() {
// 准备测试数据
String workflowCode = "non-existent";
// 配置Mock行为
when(workflowDefinitionRepository.findByCodeAndDeletedFalse(workflowCode)).thenReturn(null);
// 执行测试并验证异常
WorkflowEngineException exception = assertThrows(WorkflowEngineException.class,
() -> workflowEngine.startWorkflow(workflowCode, "test", null));
assertEquals(ResponseCode.WORKFLOW_NOT_FOUND.name(), exception.getMessage());
}
@Test
void startWorkflow_WorkflowNotPublished() {
// 准备测试数据
String workflowCode = "draft-workflow";
WorkflowDefinition definition = new WorkflowDefinition();
definition.setCode(workflowCode);
definition.setStatus(WorkflowStatusEnum.DRAFT);
// 配置Mock行为
when(workflowDefinitionRepository.findByCodeAndDeletedFalse(workflowCode)).thenReturn(definition);
// 执行测试并验证异常
WorkflowEngineException exception = assertThrows(WorkflowEngineException.class,
() -> workflowEngine.startWorkflow(workflowCode, "test", null));
assertEquals(ResponseCode.WORKFLOW_NOT_PUBLISHED.name(), exception.getMessage());
}
@Test
void executeNode_Success() {
// 准备测试数据
Long nodeInstanceId = 1L;
NodeInstance nodeInstance = new NodeInstance();
nodeInstance.setId(nodeInstanceId);
nodeInstance.setNodeType(NodeTypeEnum.TASK);
nodeInstance.setStatus(NodeStatusEnum.PENDING);
WorkflowInstance instance = new WorkflowInstance();
instance.setStatus(WorkflowStatusEnum.RUNNING);
nodeInstance.setWorkflowInstance(instance);
// 配置Mock行为
when(nodeInstanceRepository.findById(nodeInstanceId)).thenReturn(Optional.of(nodeInstance));
doNothing().when(taskNodeExecutor).execute(any(), any());
// 执行测试
workflowEngine.executeNode(nodeInstanceId);
// 验证结果
assertEquals(NodeStatusEnum.COMPLETED, nodeInstance.getStatus());
assertNotNull(nodeInstance.getEndTime());
verify(nodeInstanceRepository).findById(nodeInstanceId);
verify(taskNodeExecutor).execute(any(), any());
verify(nodeInstanceRepository).save(nodeInstance);
}
@Test
void executeNode_NodeNotFound() {
// 准备测试数据
Long nodeInstanceId = 999L;
// 配置Mock行为
when(nodeInstanceRepository.findById(nodeInstanceId)).thenReturn(Optional.empty());
// 执行测试并验证异常
WorkflowEngineException exception = assertThrows(WorkflowEngineException.class,
() -> workflowEngine.executeNode(nodeInstanceId));
assertEquals(ResponseCode.WORKFLOW_NODE_NOT_FOUND.name(), exception.getMessage());
}
@Test
void executeNode_WorkflowNotRunning() {
// 准备测试数据
Long nodeInstanceId = 1L;
NodeInstance nodeInstance = new NodeInstance();
nodeInstance.setId(nodeInstanceId);
nodeInstance.setNodeType(NodeTypeEnum.TASK);
WorkflowInstance instance = new WorkflowInstance();
instance.setStatus(WorkflowStatusEnum.COMPLETED);
nodeInstance.setWorkflowInstance(instance);
// 配置Mock行为
when(nodeInstanceRepository.findById(nodeInstanceId)).thenReturn(Optional.of(nodeInstance));
// 执行测试并验证异常
WorkflowEngineException exception = assertThrows(WorkflowEngineException.class,
() -> workflowEngine.executeNode(nodeInstanceId));
assertEquals(ResponseCode.WORKFLOW_INSTANCE_NOT_RUNNING.name(), exception.getMessage());
}
@Test
void terminateWorkflow_Success() {
// 准备测试数据
Long instanceId = 1L;
String reason = "Test termination";
WorkflowInstance instance = new WorkflowInstance();
instance.setId(instanceId);
instance.setStatus(WorkflowStatusEnum.RUNNING);
NodeInstance runningNode = new NodeInstance();
runningNode.setNodeType(NodeTypeEnum.TASK);
runningNode.setStatus(NodeStatusEnum.RUNNING);
// 配置Mock行为
when(workflowInstanceRepository.findById(instanceId)).thenReturn(Optional.of(instance));
when(nodeInstanceRepository.findByWorkflowInstanceIdAndStatus(instanceId, NodeStatusEnum.RUNNING))
.thenReturn(Arrays.asList(runningNode));
doNothing().when(taskNodeExecutor).terminate(any(), any());
// 执行测试
workflowEngine.terminateWorkflow(instanceId, reason);
// 验证结果
assertEquals(WorkflowStatusEnum.TERMINATED, instance.getStatus());
assertEquals(reason, instance.getError());
assertNotNull(instance.getEndTime());
assertEquals(NodeStatusEnum.TERMINATED, runningNode.getStatus());
assertNotNull(runningNode.getEndTime());
verify(workflowInstanceRepository).findById(instanceId);
verify(nodeInstanceRepository).findByWorkflowInstanceIdAndStatus(instanceId, NodeStatusEnum.RUNNING);
verify(taskNodeExecutor).terminate(any(), any());
verify(nodeInstanceRepository).save(runningNode);
verify(workflowInstanceRepository).save(instance);
}
@Test
void pauseWorkflow_Success() {
// 准备测试数据
Long instanceId = 1L;
WorkflowInstance instance = new WorkflowInstance();
instance.setId(instanceId);
instance.setStatus(WorkflowStatusEnum.RUNNING);
NodeInstance runningNode = new NodeInstance();
runningNode.setStatus(NodeStatusEnum.RUNNING);
// 配置Mock行为
when(workflowInstanceRepository.findById(instanceId)).thenReturn(Optional.of(instance));
when(nodeInstanceRepository.findByWorkflowInstanceIdAndStatus(instanceId, NodeStatusEnum.RUNNING))
.thenReturn(Arrays.asList(runningNode));
// 执行测试
workflowEngine.pauseWorkflow(instanceId);
// 验证结果
assertEquals(WorkflowStatusEnum.PAUSED, instance.getStatus());
assertEquals(NodeStatusEnum.PAUSED, runningNode.getStatus());
verify(workflowInstanceRepository).findById(instanceId);
verify(nodeInstanceRepository).findByWorkflowInstanceIdAndStatus(instanceId, NodeStatusEnum.RUNNING);
verify(nodeInstanceRepository).save(runningNode);
verify(workflowInstanceRepository).save(instance);
}
@Test
void resumeWorkflow_Success() {
// 准备测试数据
Long instanceId = 1L;
WorkflowInstance instance = new WorkflowInstance();
instance.setId(instanceId);
instance.setStatus(WorkflowStatusEnum.PAUSED);
NodeInstance pausedNode = new NodeInstance();
pausedNode.setId(2L);
pausedNode.setStatus(NodeStatusEnum.PAUSED);
// 配置Mock行为
when(workflowInstanceRepository.findById(instanceId)).thenReturn(Optional.of(instance));
when(nodeInstanceRepository.findByWorkflowInstanceIdAndStatus(instanceId, NodeStatusEnum.PAUSED))
.thenReturn(Arrays.asList(pausedNode));
// 执行测试
workflowEngine.resumeWorkflow(instanceId);
// 验证结果
assertEquals(WorkflowStatusEnum.RUNNING, instance.getStatus());
assertEquals(NodeStatusEnum.RUNNING, pausedNode.getStatus());
verify(workflowInstanceRepository).findById(instanceId);
verify(nodeInstanceRepository).findByWorkflowInstanceIdAndStatus(instanceId, NodeStatusEnum.PAUSED);
verify(nodeInstanceRepository).save(pausedNode);
verify(workflowInstanceRepository).save(instance);
}
}

View File

@ -0,0 +1,64 @@
package com.qqchen.deploy.backend.workflow.engine.executor;
import com.qqchen.deploy.backend.system.enums.LogLevelEnum;
import com.qqchen.deploy.backend.workflow.engine.context.WorkflowContext;
import com.qqchen.deploy.backend.workflow.entity.NodeInstance;
import com.qqchen.deploy.backend.workflow.enums.NodeStatusEnum;
import com.qqchen.deploy.backend.workflow.enums.NodeTypeEnum;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.verify;
class StartNodeExecutorTest {
@Mock
private WorkflowContext workflowContext;
@InjectMocks
private StartNodeExecutor startNodeExecutor;
@BeforeEach
void setUp() {
MockitoAnnotations.openMocks(this);
}
@Test
void getNodeType_ShouldReturnStartType() {
assertEquals(NodeTypeEnum.START, startNodeExecutor.getNodeType());
}
@Test
void execute_ShouldCompleteNodeAndLog() {
// 准备测试数据
NodeInstance nodeInstance = new NodeInstance();
nodeInstance.setNodeType(NodeTypeEnum.START);
nodeInstance.setStatus(NodeStatusEnum.PENDING);
// 执行测试
startNodeExecutor.execute(nodeInstance, workflowContext);
// 验证结果
assertEquals(NodeStatusEnum.COMPLETED, nodeInstance.getStatus());
verify(workflowContext).log("开始节点执行完成", LogLevelEnum.INFO);
}
@Test
void validate_ShouldDoNothing() {
// 开始节点无需配置验证方法不会抛出异常
startNodeExecutor.validate(null);
startNodeExecutor.validate("");
startNodeExecutor.validate("{}");
}
@Test
void terminate_ShouldDoNothing() {
// 开始节点无需终止操作验证方法不会抛出异常
NodeInstance nodeInstance = new NodeInstance();
startNodeExecutor.terminate(nodeInstance, workflowContext);
}
}

View File

@ -0,0 +1,175 @@
//package com.qqchen.deploy.backend.workflow.engine.executor;
//
//import com.qqchen.deploy.backend.workflow.engine.context.WorkflowContext;
//import com.qqchen.deploy.backend.workflow.engine.executor.task.TaskExecutor;
//import com.qqchen.deploy.backend.workflow.entity.NodeInstance;
//import com.qqchen.deploy.backend.workflow.entity.WorkflowInstance;
//import com.qqchen.deploy.backend.workflow.enums.NodeStatusEnum;
//import com.qqchen.deploy.backend.workflow.enums.NodeTypeEnum;
//import com.qqchen.deploy.backend.workflow.repository.INodeInstanceRepository;
//import org.junit.jupiter.api.BeforeEach;
//import org.junit.jupiter.api.Test;
//import org.mockito.InjectMocks;
//import org.mockito.Mock;
//import org.mockito.MockitoAnnotations;
//
//import java.util.HashMap;
//import java.util.Map;
//
//import static org.junit.jupiter.api.Assertions.*;
//import static org.mockito.ArgumentMatchers.any;
//import static org.mockito.Mockito.*;
//
//class TaskNodeExecutorTest {
//
// @Mock
// private INodeInstanceRepository nodeInstanceRepository;
//
// @Mock
// private Map<String, TaskExecutor> taskExecutors;
//
// @Mock
// private TaskExecutor shellTaskExecutor;
//
// @Mock
// private WorkflowContext context;
//
// @InjectMocks
// private TaskNodeExecutor taskNodeExecutor;
//
// @BeforeEach
// void setUp() {
// MockitoAnnotations.openMocks(this);
//
// // 设置任务执行器映射
// when(taskExecutors.get("SHELL")).thenReturn(shellTaskExecutor);
// }
//
// @Test
// void testExecute() {
// // 准备测试数据
// WorkflowInstance instance = new WorkflowInstance();
// instance.setId(1L);
//
// NodeInstance taskNode = new NodeInstance();
// taskNode.setId(1L);
// taskNode.setNodeType(NodeTypeEnum.TASK);
// taskNode.setStatus(NodeStatusEnum.PENDING);
// taskNode.setConfig("{\"type\":\"SHELL\",\"script\":\"echo hello\"}");
//
// Map<String, Object> input = new HashMap<>();
// input.put("param1", "value1");
//
// // 设置Mock行为
// when(context.getWorkflowInstance()).thenReturn(instance);
// when(context.getVariables()).thenReturn(input);
// when(shellTaskExecutor.execute(any(), any())).thenReturn(true);
//
// // 执行测试
// boolean result = taskNodeExecutor.execute(context, taskNode);
//
// // 验证结果
// assertTrue(result);
// assertEquals(NodeStatusEnum.RUNNING, taskNode.getStatus());
// assertNotNull(taskNode.getStartTime());
// verify(nodeInstanceRepository, times(1)).save(taskNode);
// verify(shellTaskExecutor, times(1)).execute(any(), any());
// }
//
// @Test
// void testExecuteWithError() {
// // 准备测试数据
// WorkflowInstance instance = new WorkflowInstance();
// instance.setId(1L);
//
// NodeInstance taskNode = new NodeInstance();
// taskNode.setId(1L);
// taskNode.setNodeType(NodeTypeEnum.TASK);
// taskNode.setStatus(NodeStatusEnum.PENDING);
// taskNode.setConfig("{\"type\":\"SHELL\",\"script\":\"echo hello\"}");
//
// // 设置Mock行为
// when(context.getWorkflowInstance()).thenReturn(instance);
// when(shellTaskExecutor.execute(any(), any())).thenReturn(false);
//
// // 执行测试
// boolean result = taskNodeExecutor.execute(context, taskNode);
//
// // 验证结果
// assertFalse(result);
// assertEquals(NodeStatusEnum.FAILED, taskNode.getStatus());
// assertNotNull(taskNode.getStartTime());
// assertNotNull(taskNode.getEndTime());
// verify(nodeInstanceRepository, times(1)).save(taskNode);
// verify(shellTaskExecutor, times(1)).execute(any(), any());
// }
//
// @Test
// void testCanExecute() {
// // 准备测试数据
// NodeInstance taskNode = new NodeInstance();
// taskNode.setNodeType(NodeTypeEnum.TASK);
// taskNode.setStatus(NodeStatusEnum.PENDING);
//
// // 执行测试
// boolean result = taskNodeExecutor.canExecute(taskNode);
//
// // 验证结果
// assertTrue(result);
// }
//
// @Test
// void testCannotExecuteWrongType() {
// // 准备测试数据
// NodeInstance startNode = new NodeInstance();
// startNode.setNodeType(NodeTypeEnum.START);
// startNode.setStatus(NodeStatusEnum.PENDING);
//
// // 执行测试
// boolean result = taskNodeExecutor.canExecute(startNode);
//
// // 验证结果
// assertFalse(result);
// }
//
// @Test
// void testCannotExecuteWrongStatus() {
// // 准备测试数据
// NodeInstance taskNode = new NodeInstance();
// taskNode.setNodeType(NodeTypeEnum.TASK);
// taskNode.setStatus(NodeStatusEnum.RUNNING);
//
// // 执行测试
// boolean result = taskNodeExecutor.canExecute(taskNode);
//
// // 验证结果
// assertFalse(result);
// }
//
// @Test
// void testExecuteWithInvalidConfig() {
// // 准备测试数据
// WorkflowInstance instance = new WorkflowInstance();
// instance.setId(1L);
//
// NodeInstance taskNode = new NodeInstance();
// taskNode.setId(1L);
// taskNode.setNodeType(NodeTypeEnum.TASK);
// taskNode.setStatus(NodeStatusEnum.PENDING);
// taskNode.setConfig("invalid json");
//
// // 设置Mock行为
// when(context.getWorkflowInstance()).thenReturn(instance);
//
// // 执行测试
// boolean result = taskNodeExecutor.execute(context, taskNode);
//
// // 验证结果
// assertFalse(result);
// assertEquals(NodeStatusEnum.FAILED, taskNode.getStatus());
// assertNotNull(taskNode.getStartTime());
// assertNotNull(taskNode.getEndTime());
// assertNotNull(taskNode.getError());
// verify(nodeInstanceRepository, times(1)).save(taskNode);
// }
//}

View File

@ -0,0 +1,162 @@
package com.qqchen.deploy.backend.workflow.service;
import com.qqchen.deploy.backend.framework.enums.ResponseCode;
import com.qqchen.deploy.backend.framework.exception.BusinessException;
import com.qqchen.deploy.backend.workflow.dto.WorkflowDefinitionDTO;
import com.qqchen.deploy.backend.workflow.entity.WorkflowDefinition;
import com.qqchen.deploy.backend.workflow.enums.WorkflowStatusEnum;
import com.qqchen.deploy.backend.workflow.repository.INodeDefinitionRepository;
import com.qqchen.deploy.backend.workflow.repository.IWorkflowDefinitionRepository;
import com.qqchen.deploy.backend.workflow.service.impl.WorkflowDefinitionServiceImpl;
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.junit.jupiter.MockitoExtension;
import java.util.Arrays;
import java.util.Optional;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*;
@ExtendWith(MockitoExtension.class)
class WorkflowDefinitionServiceTest {
@Mock
private IWorkflowDefinitionRepository workflowDefinitionRepository;
@Mock
private INodeDefinitionRepository nodeDefinitionRepository;
@InjectMocks
private WorkflowDefinitionServiceImpl workflowDefinitionService;
private WorkflowDefinitionDTO dto;
private WorkflowDefinition entity;
@BeforeEach
void setUp() {
dto = new WorkflowDefinitionDTO();
dto.setCode("TEST-WF");
dto.setName("Test Workflow");
dto.setDescription("Test Description");
dto.setNodeConfig("{}");
dto.setTransitionConfig("{}");
dto.setFormDefinition("{}");
dto.setGraphDefinition("{}");
entity = new WorkflowDefinition();
entity.setId(1L);
entity.setCode("TEST-WF");
entity.setName("Test Workflow");
entity.setStatus(WorkflowStatusEnum.DRAFT);
entity.setEnabled(true);
}
@Test
void create_Success() {
when(workflowDefinitionRepository.existsByCodeAndDeletedFalse(anyString())).thenReturn(false);
when(workflowDefinitionRepository.save(any(WorkflowDefinition.class))).thenReturn(entity);
WorkflowDefinitionDTO result = workflowDefinitionService.create(dto);
assertNotNull(result);
assertEquals(WorkflowStatusEnum.DRAFT, result.getStatus());
assertTrue(result.getEnabled());
verify(workflowDefinitionRepository).existsByCodeAndDeletedFalse(dto.getCode());
verify(workflowDefinitionRepository).save(any(WorkflowDefinition.class));
}
@Test
void create_CodeExists_ThrowsException() {
when(workflowDefinitionRepository.existsByCodeAndDeletedFalse(anyString())).thenReturn(true);
BusinessException exception = assertThrows(BusinessException.class,
() -> workflowDefinitionService.create(dto));
assertEquals(ResponseCode.WORKFLOW_CODE_EXISTS, exception.getErrorCode());
}
@Test
void publish_Success() {
entity.setStatus(WorkflowStatusEnum.DRAFT);
when(workflowDefinitionRepository.findById(anyLong())).thenReturn(Optional.of(entity));
when(workflowDefinitionRepository.save(any(WorkflowDefinition.class))).thenReturn(entity);
WorkflowDefinitionDTO result = workflowDefinitionService.publish(1L);
assertNotNull(result);
assertEquals(WorkflowStatusEnum.PUBLISHED, result.getStatus());
verify(workflowDefinitionRepository).findById(1L);
verify(workflowDefinitionRepository).save(any(WorkflowDefinition.class));
}
@Test
void publish_NotDraft_ThrowsException() {
entity.setStatus(WorkflowStatusEnum.PUBLISHED);
when(workflowDefinitionRepository.findById(anyLong())).thenReturn(Optional.of(entity));
BusinessException exception = assertThrows(BusinessException.class,
() -> workflowDefinitionService.publish(1L));
assertEquals(ResponseCode.WORKFLOW_NOT_DRAFT, exception.getErrorCode());
}
@Test
void disable_Success() {
entity.setStatus(WorkflowStatusEnum.PUBLISHED);
when(workflowDefinitionRepository.findById(anyLong())).thenReturn(Optional.of(entity));
when(workflowDefinitionRepository.save(any(WorkflowDefinition.class))).thenReturn(entity);
WorkflowDefinitionDTO result = workflowDefinitionService.disable(1L);
assertNotNull(result);
assertEquals(WorkflowStatusEnum.DISABLED, result.getStatus());
verify(workflowDefinitionRepository).findById(1L);
verify(workflowDefinitionRepository).save(any(WorkflowDefinition.class));
}
@Test
void enable_Success() {
entity.setStatus(WorkflowStatusEnum.DISABLED);
when(workflowDefinitionRepository.findById(anyLong())).thenReturn(Optional.of(entity));
when(workflowDefinitionRepository.save(any(WorkflowDefinition.class))).thenReturn(entity);
WorkflowDefinitionDTO result = workflowDefinitionService.enable(1L);
assertNotNull(result);
assertEquals(WorkflowStatusEnum.PUBLISHED, result.getStatus());
verify(workflowDefinitionRepository).findById(1L);
verify(workflowDefinitionRepository).save(any(WorkflowDefinition.class));
}
@Test
void createNewVersion_Success() {
when(workflowDefinitionRepository.findById(anyLong())).thenReturn(Optional.of(entity));
when(workflowDefinitionRepository.findLatestVersionByCode(anyString())).thenReturn(1);
when(workflowDefinitionRepository.save(any(WorkflowDefinition.class))).thenReturn(entity);
WorkflowDefinitionDTO result = workflowDefinitionService.createNewVersion(1L);
assertNotNull(result);
assertEquals(WorkflowStatusEnum.DRAFT, result.getStatus());
assertTrue(result.getEnabled());
verify(workflowDefinitionRepository).findById(1L);
verify(workflowDefinitionRepository).findLatestVersionByCode(entity.getCode());
verify(workflowDefinitionRepository).save(any(WorkflowDefinition.class));
}
@Test
void findAllVersions_Success() {
when(workflowDefinitionRepository.findAllVersionsByCode(anyString()))
.thenReturn(Arrays.asList(entity));
var results = workflowDefinitionService.findAllVersions("TEST-WF");
assertNotNull(results);
assertFalse(results.isEmpty());
assertEquals(1, results.size());
verify(workflowDefinitionRepository).findAllVersionsByCode("TEST-WF");
}
}

View File

@ -0,0 +1,187 @@
//package com.qqchen.deploy.backend.workflow.service;
//
//import com.qqchen.deploy.backend.framework.enums.ResponseCode;
//import com.qqchen.deploy.backend.framework.exception.BusinessException;
//import com.qqchen.deploy.backend.workflow.dto.WorkflowInstanceDTO;
//import com.qqchen.deploy.backend.workflow.entity.WorkflowDefinition;
//import com.qqchen.deploy.backend.workflow.entity.WorkflowInstance;
//import com.qqchen.deploy.backend.workflow.enums.WorkflowStatusEnum;
//import com.qqchen.deploy.backend.workflow.repository.IWorkflowDefinitionRepository;
//import com.qqchen.deploy.backend.workflow.repository.IWorkflowInstanceRepository;
//import com.qqchen.deploy.backend.workflow.service.impl.WorkflowInstanceServiceImpl;
//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.junit.jupiter.MockitoExtension;
//
//import java.util.HashMap;
//import java.util.Map;
//import java.util.Optional;
//
//import static org.junit.jupiter.api.Assertions.*;
//import static org.mockito.ArgumentMatchers.any;
//import static org.mockito.ArgumentMatchers.anyLong;
//import static org.mockito.Mockito.*;
//
//@ExtendWith(MockitoExtension.class)
//class WorkflowInstanceServiceTest {
//
// @Mock
// private IWorkflowDefinitionRepository workflowDefinitionRepository;
//
// @Mock
// private IWorkflowInstanceRepository workflowInstanceRepository;
//
// @Mock
// private IWorkflowVariableService workflowVariableService;
//
// @InjectMocks
// private WorkflowInstanceServiceImpl workflowInstanceService;
//
// private WorkflowDefinition definition;
// private WorkflowInstance instance;
// private Map<String, Object> variables;
//
// @BeforeEach
// void setUp() {
// definition = new WorkflowDefinition();
// definition.setId(1L);
// definition.setCode("TEST-WF");
// definition.setName("Test Workflow");
// definition.setStatus(WorkflowStatusEnum.PUBLISHED);
// definition.setEnabled(true);
//
// instance = new WorkflowInstance();
// instance.setId(1L);
// instance.setDefinition(definition);
// instance.setBusinessKey("TEST-KEY");
// instance.setStatus(WorkflowStatusEnum.CREATED);
//
// variables = new HashMap<>();
// variables.put("key1", "value1");
// variables.put("key2", "value2");
// }
//
// @Test
// void createInstance_Success() {
// when(workflowDefinitionRepository.findById(anyLong())).thenReturn(Optional.of(definition));
// when(workflowInstanceRepository.save(any(WorkflowInstance.class))).thenReturn(instance);
//
// WorkflowInstanceDTO result = workflowInstanceService.createInstance(1L, "TEST-KEY", variables);
//
// assertNotNull(result);
// assertEquals(WorkflowStatusEnum.CREATED, result.getStatus());
// assertEquals("TEST-KEY", result.getBusinessKey());
// verify(workflowDefinitionRepository).findById(1L);
// verify(workflowInstanceRepository).save(any(WorkflowInstance.class));
// verify(workflowVariableService, times(2)).setVariable(anyLong(), anyString(), any());
// }
//
// @Test
// void createInstance_WorkflowNotFound_ThrowsException() {
// when(workflowDefinitionRepository.findById(anyLong())).thenReturn(Optional.empty());
//
// BusinessException exception = assertThrows(BusinessException.class,
// () -> workflowInstanceService.createInstance(1L, "TEST-KEY", variables));
// assertEquals(ResponseCode.WORKFLOW_NOT_FOUND, exception.getCode());
// }
//
// @Test
// void createInstance_WorkflowNotPublished_ThrowsException() {
// definition.setStatus(WorkflowStatusEnum.DRAFT);
// when(workflowDefinitionRepository.findById(anyLong())).thenReturn(Optional.of(definition));
//
// BusinessException exception = assertThrows(BusinessException.class,
// () -> workflowInstanceService.createInstance(1L, "TEST-KEY", variables));
// assertEquals(ResponseCode.WORKFLOW_NOT_PUBLISHED, exception.getCode());
// }
//
// @Test
// void startInstance_Success() {
// when(workflowInstanceRepository.findById(anyLong())).thenReturn(Optional.of(instance));
// when(workflowInstanceRepository.save(any(WorkflowInstance.class))).thenReturn(instance);
//
// WorkflowInstanceDTO result = workflowInstanceService.startInstance(1L);
//
// assertNotNull(result);
// assertEquals(WorkflowStatusEnum.RUNNING, result.getStatus());
// assertNotNull(result.getStartTime());
// verify(workflowInstanceRepository).findById(1L);
// verify(workflowInstanceRepository).save(any(WorkflowInstance.class));
// }
//
// @Test
// void startInstance_NotFound_ThrowsException() {
// when(workflowInstanceRepository.findById(anyLong())).thenReturn(Optional.empty());
//
// BusinessException exception = assertThrows(BusinessException.class,
// () -> workflowInstanceService.startInstance(1L));
// assertEquals(ResponseCode.WORKFLOW_INSTANCE_NOT_FOUND, exception.getCode());
// }
//
// @Test
// void startInstance_NotCreated_ThrowsException() {
// instance.setStatus(WorkflowStatusEnum.RUNNING);
// when(workflowInstanceRepository.findById(anyLong())).thenReturn(Optional.of(instance));
//
// BusinessException exception = assertThrows(BusinessException.class,
// () -> workflowInstanceService.startInstance(1L));
// assertEquals(ResponseCode.WORKFLOW_INSTANCE_NOT_RUNNING, exception.getCode());
// }
//
// @Test
// void pauseInstance_Success() {
// instance.setStatus(WorkflowStatusEnum.RUNNING);
// when(workflowInstanceRepository.findById(anyLong())).thenReturn(Optional.of(instance));
// when(workflowInstanceRepository.save(any(WorkflowInstance.class))).thenReturn(instance);
//
// WorkflowInstanceDTO result = workflowInstanceService.pauseInstance(1L);
//
// assertNotNull(result);
// assertEquals(WorkflowStatusEnum.PAUSED, result.getStatus());
// verify(workflowInstanceRepository).findById(1L);
// verify(workflowInstanceRepository).save(any(WorkflowInstance.class));
// }
//
// @Test
// void resumeInstance_Success() {
// instance.setStatus(WorkflowStatusEnum.PAUSED);
// when(workflowInstanceRepository.findById(anyLong())).thenReturn(Optional.of(instance));
// when(workflowInstanceRepository.save(any(WorkflowInstance.class))).thenReturn(instance);
//
// WorkflowInstanceDTO result = workflowInstanceService.resumeInstance(1L);
//
// assertNotNull(result);
// assertEquals(WorkflowStatusEnum.RUNNING, result.getStatus());
// verify(workflowInstanceRepository).findById(1L);
// verify(workflowInstanceRepository).save(any(WorkflowInstance.class));
// }
//
// @Test
// void terminateInstance_Success() {
// instance.setStatus(WorkflowStatusEnum.RUNNING);
// when(workflowInstanceRepository.findById(anyLong())).thenReturn(Optional.of(instance));
// when(workflowInstanceRepository.save(any(WorkflowInstance.class))).thenReturn(instance);
//
// WorkflowInstanceDTO result = workflowInstanceService.terminateInstance(1L, "Test reason");
//
// assertNotNull(result);
// assertEquals(WorkflowStatusEnum.TERMINATED, result.getStatus());
// assertEquals("Test reason", result.getError());
// assertNotNull(result.getEndTime());
// verify(workflowInstanceRepository).findById(1L);
// verify(workflowInstanceRepository).save(any(WorkflowInstance.class));
// }
//
// @Test
// void terminateInstance_AlreadyTerminated_ThrowsException() {
// instance.setStatus(WorkflowStatusEnum.TERMINATED);
// when(workflowInstanceRepository.findById(anyLong())).thenReturn(Optional.of(instance));
//
// BusinessException exception = assertThrows(BusinessException.class,
// () -> workflowInstanceService.terminateInstance(1L, "Test reason"));
// assertEquals(ResponseCode.WORKFLOW_INSTANCE_NOT_RUNNING, exception.getCode());
// }
//}