修复: Wave5 审批子系统收尾(R7补 types.ts + R9⑪ failed_node + R8 Request 测试)
- R7补: types.ts event.type string→WorkflowEventType 字面量联合(11 变体 snake_case),静态拦截写错 type - R9⑪: workflow.rs WorkflowFailed failed_node String::new()→状态机取首个 Failed/Cancelled 节点 - R8: human_node 加 request_is_emitted_to_bus 测试,验证 R6(Request 真发到 EventBus) cargo/vue-tsc 0 err / df-nodes 15 test pass
This commit is contained in:
@@ -195,6 +195,40 @@ mod tests {
|
|||||||
assert_eq!(out.data["comment"], json!("好的"));
|
assert_eq!(out.data["comment"], json!("好的"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn request_is_emitted_to_bus() {
|
||||||
|
// B-03b-R8: 验证 HumanNode 真发出 HumanApprovalRequest 到事件总线
|
||||||
|
// (R6 修复前 :41 send 缺 await → Request 未进 channel,此测会超时失败)
|
||||||
|
let bus = EventBus::new();
|
||||||
|
let mut rx = bus.subscribe(); // subscribe 先于 execute send(broadcast 不回放)
|
||||||
|
let ctx = make_ctx(&bus, "exec-1", "node-h", json!({ "timeout_secs": 1 }));
|
||||||
|
let bus_clone = bus.clone();
|
||||||
|
let handle = tokio::spawn(async move { HumanNode.execute(ctx).await });
|
||||||
|
|
||||||
|
// execute send Request 后 rx 应收到
|
||||||
|
let received = tokio::time::timeout(Duration::from_millis(500), rx.recv()).await;
|
||||||
|
assert!(
|
||||||
|
received.is_ok(),
|
||||||
|
"应收到 HumanApprovalRequest(R6 Request 真发出),实际超时"
|
||||||
|
);
|
||||||
|
match received.unwrap().unwrap() {
|
||||||
|
WorkflowEvent::HumanApprovalRequest {
|
||||||
|
execution_id,
|
||||||
|
node_id,
|
||||||
|
title,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
assert_eq!(execution_id, "exec-1");
|
||||||
|
assert_eq!(node_id, "node-h");
|
||||||
|
assert_eq!(title, "请确认");
|
||||||
|
}
|
||||||
|
other => panic!("期望 HumanApprovalRequest,收到 {:?}", other),
|
||||||
|
}
|
||||||
|
// 发 Response 让 execute 正常返回(防 task 泄漏)
|
||||||
|
send_response(&bus_clone, "exec-1", "node-h", "同意", None).await;
|
||||||
|
let _ = handle.await;
|
||||||
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn mismatched_execution_id_filtered_then_timeout() {
|
async fn mismatched_execution_id_filtered_then_timeout() {
|
||||||
// execution_id 不匹配:Response 被过滤,短超时验证 → Err 超时
|
// execution_id 不匹配:Response 被过滤,短超时验证 → Err 超时
|
||||||
|
|||||||
@@ -132,11 +132,20 @@ pub async fn run_workflow(
|
|||||||
state_registry.lock().await.remove(&exec_id);
|
state_registry.lock().await.remove(&exec_id);
|
||||||
|
|
||||||
// 执行失败时补发 WorkflowFailed(执行器内部只发 NodeFailed)
|
// 执行失败时补发 WorkflowFailed(执行器内部只发 NodeFailed)
|
||||||
|
// failed_node 从状态机取首个失败/取消节点(R9⑪: 原 String::new() 空值)
|
||||||
if let Some(error) = error {
|
if let Some(error) = error {
|
||||||
|
use df_core::types::NodeStatus;
|
||||||
|
let failed_node = executor
|
||||||
|
.state_machine()
|
||||||
|
.snapshot()
|
||||||
|
.into_iter()
|
||||||
|
.find(|(_, s)| matches!(s, NodeStatus::Failed | NodeStatus::Cancelled))
|
||||||
|
.map(|(id, _)| id)
|
||||||
|
.unwrap_or_default();
|
||||||
event_bus
|
event_bus
|
||||||
.send(WorkflowEvent::WorkflowFailed {
|
.send(WorkflowEvent::WorkflowFailed {
|
||||||
error,
|
error,
|
||||||
failed_node: String::new(),
|
failed_node,
|
||||||
})
|
})
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -121,10 +121,16 @@ export interface WorkflowRecord {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** 工作流事件载荷(后端 emit 的结构) */
|
/** 工作流事件载荷(后端 emit 的结构) */
|
||||||
|
/** 工作流事件类型字面量联合(对应后端 WorkflowEvent serde tag=type rename_all=snake_case,R7收窄防写错 type 字符串) */
|
||||||
|
export type WorkflowEventType =
|
||||||
|
| 'node_started' | 'node_progress' | 'node_output' | 'node_completed' | 'node_failed'
|
||||||
|
| 'workflow_paused' | 'workflow_completed' | 'workflow_failed'
|
||||||
|
| 'human_approval_request' | 'human_approval_response'
|
||||||
|
|
||||||
export interface WorkflowEventPayload {
|
export interface WorkflowEventPayload {
|
||||||
execution_id: string
|
execution_id: string
|
||||||
event: {
|
event: {
|
||||||
type: string
|
type: WorkflowEventType
|
||||||
[key: string]: unknown
|
[key: string]: unknown
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user