test(workflow): erp-workflow 单元测试从 16 增至 63 — 覆盖 model/error/parser/expression/executor
- model.rs: 13 个 FlowGraph 测试(build/outgoing/incoming/start-end 边界) - error.rs: 7 个 WorkflowError → AppError 转换测试 - parser.rs: 11 个新增验证边界测试(空图/多起点/幽灵边/网关约束) - expression.rs: 13 个新增求值测试(float/bool/string/复合表达式/空白容错) - executor.rs: 3 个 is_join_gateway 纯函数测试
This commit is contained in:
@@ -266,4 +266,233 @@ mod tests {
|
||||
let result = parse_and_validate(&nodes, &edges);
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
// ---- 边界测试扩展 ----
|
||||
|
||||
#[test]
|
||||
fn test_empty_nodes_rejected() {
|
||||
let result = parse_and_validate(&[], &[]);
|
||||
assert!(result.is_err());
|
||||
let msg = result.unwrap_err().to_string();
|
||||
assert!(msg.contains("不能为空"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_multiple_start_events_rejected() {
|
||||
let nodes = vec![
|
||||
make_start(),
|
||||
NodeDef {
|
||||
id: "start2".to_string(),
|
||||
node_type: NodeType::StartEvent,
|
||||
name: "开始2".to_string(),
|
||||
assignee_id: None,
|
||||
candidate_groups: None,
|
||||
service_type: None,
|
||||
service_config: None,
|
||||
position: None,
|
||||
},
|
||||
make_end(),
|
||||
];
|
||||
let edges = vec![
|
||||
make_edge("e1", "start", "end"),
|
||||
make_edge("e2", "start2", "end"),
|
||||
];
|
||||
let result = parse_and_validate(&nodes, &edges);
|
||||
assert!(result.is_err());
|
||||
let msg = result.unwrap_err().to_string();
|
||||
assert!(msg.contains("只能包含一个开始事件"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_edge_references_nonexistent_source() {
|
||||
let nodes = vec![make_start(), make_end()];
|
||||
let edges = vec![
|
||||
make_edge("e1", "ghost", "end"),
|
||||
];
|
||||
let result = parse_and_validate(&nodes, &edges);
|
||||
assert!(result.is_err());
|
||||
let msg = result.unwrap_err().to_string();
|
||||
assert!(msg.contains("源节点") && msg.contains("不存在"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_edge_references_nonexistent_target() {
|
||||
let nodes = vec![make_start(), make_end()];
|
||||
let edges = vec![
|
||||
make_edge("e1", "start", "ghost"),
|
||||
];
|
||||
let result = parse_and_validate(&nodes, &edges);
|
||||
assert!(result.is_err());
|
||||
let msg = result.unwrap_err().to_string();
|
||||
assert!(msg.contains("目标节点") && msg.contains("不存在"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_start_event_with_incoming_edge_rejected() {
|
||||
let nodes = vec![make_start(), make_end()];
|
||||
let edges = vec![
|
||||
make_edge("e1", "start", "end"),
|
||||
make_edge("e2", "end", "start"), // start 有入边
|
||||
];
|
||||
let result = parse_and_validate(&nodes, &edges);
|
||||
assert!(result.is_err());
|
||||
let msg = result.unwrap_err().to_string();
|
||||
assert!(msg.contains("入边"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_start_event_without_outgoing_edge_rejected() {
|
||||
let nodes = vec![make_start(), make_end()];
|
||||
let edges = vec![]; // start 没有出边
|
||||
let result = parse_and_validate(&nodes, &edges);
|
||||
assert!(result.is_err());
|
||||
let msg = result.unwrap_err().to_string();
|
||||
assert!(msg.contains("出边"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_exclusive_gateway_single_outgoing_ok() {
|
||||
// 单条出边的排他网关不需要条件
|
||||
let nodes = vec![
|
||||
make_start(),
|
||||
NodeDef {
|
||||
id: "gw".to_string(),
|
||||
node_type: NodeType::ExclusiveGateway,
|
||||
name: "判断".to_string(),
|
||||
assignee_id: None,
|
||||
candidate_groups: None,
|
||||
service_type: None,
|
||||
service_config: None,
|
||||
position: None,
|
||||
},
|
||||
make_end(),
|
||||
];
|
||||
let edges = vec![
|
||||
make_edge("e1", "start", "gw"),
|
||||
make_edge("e2", "gw", "end"),
|
||||
];
|
||||
assert!(parse_and_validate(&nodes, &edges).is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_exclusive_gateway_with_conditions_ok() {
|
||||
let nodes = vec![
|
||||
make_start(),
|
||||
NodeDef {
|
||||
id: "gw".to_string(),
|
||||
node_type: NodeType::ExclusiveGateway,
|
||||
name: "金额判断".to_string(),
|
||||
assignee_id: None,
|
||||
candidate_groups: None,
|
||||
service_type: None,
|
||||
service_config: None,
|
||||
position: None,
|
||||
},
|
||||
make_user_task("task_a", "小额审批"),
|
||||
make_user_task("task_b", "大额审批"),
|
||||
make_end(),
|
||||
];
|
||||
let edges = vec![
|
||||
make_edge("e1", "start", "gw"),
|
||||
EdgeDef {
|
||||
id: "e2".to_string(),
|
||||
source: "gw".to_string(),
|
||||
target: "task_a".to_string(),
|
||||
condition: Some("amount <= 1000".to_string()),
|
||||
label: None,
|
||||
},
|
||||
EdgeDef {
|
||||
id: "e3".to_string(),
|
||||
source: "gw".to_string(),
|
||||
target: "task_b".to_string(),
|
||||
condition: Some("amount > 1000".to_string()),
|
||||
label: None,
|
||||
},
|
||||
make_edge("e4", "task_a", "end"),
|
||||
make_edge("e5", "task_b", "end"),
|
||||
];
|
||||
assert!(parse_and_validate(&nodes, &edges).is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_gateway_without_incoming_rejected() {
|
||||
let nodes = vec![
|
||||
make_start(),
|
||||
NodeDef {
|
||||
id: "gw".to_string(),
|
||||
node_type: NodeType::ParallelGateway,
|
||||
name: "并行".to_string(),
|
||||
assignee_id: None,
|
||||
candidate_groups: None,
|
||||
service_type: None,
|
||||
service_config: None,
|
||||
position: None,
|
||||
},
|
||||
make_end(),
|
||||
];
|
||||
// gw 没有入边
|
||||
let edges = vec![
|
||||
make_edge("e1", "start", "end"),
|
||||
make_edge("e2", "gw", "end"),
|
||||
];
|
||||
let result = parse_and_validate(&nodes, &edges);
|
||||
assert!(result.is_err());
|
||||
let msg = result.unwrap_err().to_string();
|
||||
assert!(msg.contains("入边"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_gateway_without_outgoing_rejected() {
|
||||
let nodes = vec![
|
||||
make_start(),
|
||||
NodeDef {
|
||||
id: "gw".to_string(),
|
||||
node_type: NodeType::ParallelGateway,
|
||||
name: "并行".to_string(),
|
||||
assignee_id: None,
|
||||
candidate_groups: None,
|
||||
service_type: None,
|
||||
service_config: None,
|
||||
position: None,
|
||||
},
|
||||
make_end(),
|
||||
];
|
||||
// gw 没有出边
|
||||
let edges = vec![
|
||||
make_edge("e1", "start", "gw"),
|
||||
make_edge("e2", "start", "end"),
|
||||
];
|
||||
let result = parse_and_validate(&nodes, &edges);
|
||||
assert!(result.is_err());
|
||||
let msg = result.unwrap_err().to_string();
|
||||
assert!(msg.contains("出边"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parallel_gateway_valid() {
|
||||
let nodes = vec![
|
||||
make_start(),
|
||||
NodeDef {
|
||||
id: "fork".to_string(),
|
||||
node_type: NodeType::ParallelGateway,
|
||||
name: "拆分".to_string(),
|
||||
assignee_id: None,
|
||||
candidate_groups: None,
|
||||
service_type: None,
|
||||
service_config: None,
|
||||
position: None,
|
||||
},
|
||||
make_user_task("a", "任务A"),
|
||||
make_user_task("b", "任务B"),
|
||||
make_end(),
|
||||
];
|
||||
let edges = vec![
|
||||
make_edge("e1", "start", "fork"),
|
||||
make_edge("e2", "fork", "a"),
|
||||
make_edge("e3", "fork", "b"),
|
||||
make_edge("e4", "a", "end"),
|
||||
make_edge("e5", "b", "end"),
|
||||
];
|
||||
assert!(parse_and_validate(&nodes, &edges).is_ok());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user