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