Files
base/crates/erp-server/tests/integration/workflow_tests.rs
iven 59856ac2fc feat: initialize ERP base platform (extracted from HMS)
- Stripped 11 business crates (health, ai, dialysis, plugins)
- Cleaned AppState, AppConfig, main.rs from business coupling
- Reduced migrations from 169 to 53 (base-only)
- Removed health_provider trait from erp-core
- Removed business integration tests
- Removed gateway rate limiting middleware
- Base capabilities: auth, RBAC, JWT, config, workflow, message, plugin, audit, crypto, RLS, multi-tenant

Cargo check: OK
Cargo test: OK
2026-05-31 20:35:57 +08:00

248 lines
7.0 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
use erp_core::events::EventBus;
use erp_core::types::Pagination;
use erp_workflow::dto::{
CompleteTaskReq, CreateProcessDefinitionReq, EdgeDef, NodeDef, NodeType, StartInstanceReq,
};
use erp_workflow::service::definition_service::DefinitionService;
use erp_workflow::service::instance_service::InstanceService;
use erp_workflow::service::task_service::TaskService;
use super::test_db::TestDb;
/// 构建一个最简单的线性流程:开始 → 审批 → 结束
/// assignee 指向 operator_id使 list_pending 能查到任务
fn make_simple_definition(
name: &str,
key: &str,
assignee_id: Option<uuid::Uuid>,
) -> CreateProcessDefinitionReq {
CreateProcessDefinitionReq {
name: name.to_string(),
key: key.to_string(),
category: Some("test".to_string()),
description: Some("集成测试流程".to_string()),
nodes: vec![
NodeDef {
id: "start".to_string(),
node_type: NodeType::StartEvent,
name: "开始".to_string(),
assignee_id: None,
candidate_groups: None,
service_type: None,
service_config: None,
position: None,
},
NodeDef {
id: "approve".to_string(),
node_type: NodeType::UserTask,
name: "审批".to_string(),
assignee_id,
candidate_groups: None,
service_type: None,
service_config: None,
position: None,
},
NodeDef {
id: "end".to_string(),
node_type: NodeType::EndEvent,
name: "结束".to_string(),
assignee_id: None,
candidate_groups: None,
service_type: None,
service_config: None,
position: None,
},
],
edges: vec![
EdgeDef {
id: "e1".to_string(),
source: "start".to_string(),
target: "approve".to_string(),
condition: None,
label: None,
},
EdgeDef {
id: "e2".to_string(),
source: "approve".to_string(),
target: "end".to_string(),
condition: None,
label: None,
},
],
}
}
#[tokio::test]
async fn test_workflow_definition_crud() {
let test_db = TestDb::new().await;
let db = test_db.db();
let tenant_id = uuid::Uuid::new_v4();
let operator_id = uuid::Uuid::new_v4();
let event_bus = EventBus::new(100);
let def = DefinitionService::create(
tenant_id,
operator_id,
&make_simple_definition("测试流程", "test-flow-1", None),
db,
&event_bus,
)
.await
.expect("创建流程定义失败");
assert_eq!(def.name, "测试流程");
assert_eq!(def.status, "draft");
let (defs, total) = DefinitionService::list(
tenant_id,
&Pagination {
page: Some(1),
page_size: Some(10),
},
db,
)
.await
.expect("查询流程定义列表失败");
assert_eq!(total, 1);
assert_eq!(defs[0].name, "测试流程");
let found = DefinitionService::get_by_id(def.id, tenant_id, db)
.await
.expect("查询流程定义失败");
assert_eq!(found.id, def.id);
let published = DefinitionService::publish(def.id, tenant_id, operator_id, db, &event_bus)
.await
.expect("发布流程定义失败");
assert_eq!(published.status, "published");
}
#[tokio::test]
async fn test_workflow_instance_lifecycle() {
let test_db = TestDb::new().await;
let db = test_db.db();
let tenant_id = uuid::Uuid::new_v4();
let operator_id = uuid::Uuid::new_v4();
let event_bus = EventBus::new(100);
let def = DefinitionService::create(
tenant_id,
operator_id,
&make_simple_definition("生命周期测试", "lifecycle-flow", Some(operator_id)),
db,
&event_bus,
)
.await
.expect("创建流程定义失败");
let def = DefinitionService::publish(def.id, tenant_id, operator_id, db, &event_bus)
.await
.expect("发布流程定义失败");
let instance = InstanceService::start(
tenant_id,
operator_id,
&StartInstanceReq {
definition_id: def.id,
business_key: Some("测试实例".to_string()),
variables: None,
},
db,
&event_bus,
)
.await
.expect("启动流程实例失败");
assert_eq!(instance.status, "running");
let (tasks, task_total) = TaskService::list_pending(
tenant_id,
operator_id,
&Pagination {
page: Some(1),
page_size: Some(10),
},
db,
)
.await
.expect("查询待办任务失败");
assert_eq!(task_total, 1);
assert_eq!(tasks[0].status, "pending");
let completed = TaskService::complete(
tasks[0].id,
tenant_id,
operator_id,
&CompleteTaskReq {
outcome: "approved".to_string(),
form_data: Some(serde_json::json!({"comment": "同意"})),
},
db,
&event_bus,
)
.await
.expect("完成任务失败");
assert_eq!(completed.status, "completed");
}
#[tokio::test]
async fn test_workflow_tenant_isolation() {
let test_db = TestDb::new().await;
let db = test_db.db();
let tenant_a = uuid::Uuid::new_v4();
let tenant_b = uuid::Uuid::new_v4();
let operator_id = uuid::Uuid::new_v4();
let event_bus = EventBus::new(100);
let def_a = DefinitionService::create(
tenant_a,
operator_id,
&make_simple_definition("租户A流程", "tenant-a-flow", None),
db,
&event_bus,
)
.await
.expect("创建流程定义失败");
let (defs_b, total_b) = DefinitionService::list(
tenant_b,
&Pagination {
page: Some(1),
page_size: Some(10),
},
db,
)
.await
.expect("查询流程定义列表失败");
assert_eq!(total_b, 0);
assert!(defs_b.is_empty());
let result = DefinitionService::get_by_id(def_a.id, tenant_b, db).await;
assert!(result.is_err());
}
#[tokio::test]
async fn test_event_bus_pub_sub() {
let event_bus = EventBus::new(100);
let tenant_id = uuid::Uuid::new_v4();
let (mut receiver, _handle) = event_bus.subscribe_filtered("user.".to_string());
let event = erp_core::events::DomainEvent::new(
"user.created",
tenant_id,
serde_json::json!({"username": "test"}),
);
event_bus.broadcast(event);
let other_event =
erp_core::events::DomainEvent::new("workflow.started", tenant_id, serde_json::json!({}));
event_bus.broadcast(other_event);
let received = receiver.recv().await;
assert!(received.is_some());
let received = received.unwrap();
assert_eq!(received.event_type, "user.created");
assert_eq!(received.payload["username"], "test");
}