matrixcode_core/workflow/
mod.rs1pub mod context;
52pub mod def;
53pub mod engine;
54pub mod executors;
55pub mod parser;
56pub mod persistence;
57pub mod registry;
58pub mod rule_engine;
59pub mod template;
60
61pub use def::{
63 BranchDef, EdgeDef, FailureStrategy, InputDef, NodeDef, NodeType, OutputDef, ParallelBranchDef,
64 WorkflowDef,
65};
66
67pub use parser::{parse_workflow, parse_workflow_from_file, to_yaml};
68
69pub use context::{NodeExecution, NodeStatus, WorkflowContext, WorkflowStatus};
70
71pub use template::{TemplateRenderer, render as render_template};
72
73pub use rule_engine::{Rule, RuleEngine, ValidationResult, evaluate_expression};
74
75pub use engine::{DefaultTaskExecutor, EventListener, TaskExecutor, WorkflowEngine, WorkflowEvent};
76
77pub use persistence::WorkflowPersistence;
78
79pub use registry::{WorkflowInfo, WorkflowRegistry, WorkflowSource};
80
81pub use executors::{
82 AiExecutor, AiExecutorConfig, CompositeExecutor, CompositeMode, ConditionExecutor,
83 ExecutorFactory, NodeExecutor, ProxyExecutor, ToolExecutor, ToolExecutorConfig,
84 ValidateExecutor, ValidateExecutorConfig,
85};
86
87#[cfg(test)]
88mod integration_tests {
89 use super::*;
90 use std::collections::HashMap;
91
92 #[test]
93 fn test_parse_and_validate_workflow() {
94 let yaml = r#"
95id: integration-test
96name: Integration Test Workflow
97version: "1.0.0"
98description: A test workflow for integration testing
99inputs:
100 - name: user_name
101 type: string
102 required: true
103 - name: count
104 type: number
105 required: false
106 default: 1
107outputs:
108 - name: result
109 value: "{{output}}"
110variables:
111 greeting: "Hello"
112nodes:
113 - id: start
114 type: start
115 name: Start
116 - id: process
117 type: task
118 name: Process
119 task: process_task
120 params:
121 name: "{{user_name}}"
122 count: "{{count}}"
123 on_failure:
124 type: retry
125 max_attempts: 3
126 interval_ms: 1000
127 timeout_ms: 30000
128 - id: end
129 type: end
130 name: End
131edges:
132 - from: start
133 to: process
134 - from: process
135 to: end
136"#;
137 let workflow = parse_workflow(yaml).unwrap();
138 assert_eq!(workflow.id, "integration-test");
139 assert_eq!(workflow.nodes.len(), 3);
140 assert_eq!(workflow.edges.len(), 2);
141 assert!(workflow.validate().is_ok());
142 }
143
144 #[tokio::test]
145 async fn test_workflow_execution() {
146 let workflow_def = WorkflowDef {
147 id: "test-exec".to_string(),
148 name: "Test Execution".to_string(),
149 version: "1.0.0".to_string(),
150 description: None,
151 inputs: vec![],
152 outputs: vec![],
153 nodes: vec![
154 NodeDef {
155 id: "start".to_string(),
156 node_type: NodeType::Start,
157 name: "Start".to_string(),
158 description: None,
159 task: None,
160 params: HashMap::new(),
161 on_failure: FailureStrategy::Abort,
162 timeout_ms: None,
163 branches: None,
164 parallel_branches: None,
165 execution_mode: None,
166 workflow: None,
167 wait_ms: None,
168 approvers: None,
169 },
170 NodeDef {
171 id: "end".to_string(),
172 node_type: NodeType::End,
173 name: "End".to_string(),
174 description: None,
175 task: None,
176 params: HashMap::new(),
177 on_failure: FailureStrategy::Abort,
178 timeout_ms: None,
179 branches: None,
180 parallel_branches: None,
181 execution_mode: None,
182 workflow: None,
183 wait_ms: None,
184 approvers: None,
185 },
186 ],
187 edges: vec![EdgeDef {
188 id: "e1".to_string(),
189 from: "start".to_string(),
190 to: "end".to_string(),
191 condition: None,
192 label: None,
193 }],
194 variables: HashMap::new(),
195 default_failure_strategy: FailureStrategy::Abort,
196 timeout_ms: None,
197 };
198
199 let engine = WorkflowEngine::new(workflow_def).unwrap();
200 let context = engine.run(HashMap::new()).await.unwrap();
201
202 assert_eq!(context.status, WorkflowStatus::Completed);
203 }
204
205 #[test]
206 fn test_template_rendering() {
207 let mut vars = HashMap::new();
208 vars.insert("name".to_string(), serde_json::json!("World"));
209 vars.insert("count".to_string(), serde_json::json!(42));
210
211 let result = render_template("Hello, {{name}}! Count: {{count}}", &vars).unwrap();
212 assert_eq!(result, "Hello, World! Count: 42");
213 }
214
215 #[test]
216 fn test_rule_validation() {
217 let mut engine = RuleEngine::new();
218 let mut context = HashMap::new();
219 context.insert("status".to_string(), serde_json::json!("success"));
220 context.insert("count".to_string(), serde_json::json!(10));
221
222 let rule = Rule::All {
223 rules: vec![
224 Rule::Equals {
225 field: "status".to_string(),
226 value: serde_json::json!("success"),
227 },
228 Rule::GreaterThan {
229 field: "count".to_string(),
230 value: 5.0,
231 },
232 ],
233 };
234
235 let result = engine.validate(&rule, &context).unwrap();
236 assert!(result.passed);
237 }
238}