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 workflow: None,
166 wait_ms: None,
167 approvers: None,
168 },
169 NodeDef {
170 id: "end".to_string(),
171 node_type: NodeType::End,
172 name: "End".to_string(),
173 description: None,
174 task: None,
175 params: HashMap::new(),
176 on_failure: FailureStrategy::Abort,
177 timeout_ms: None,
178 branches: None,
179 parallel_branches: None,
180 workflow: None,
181 wait_ms: None,
182 approvers: None,
183 },
184 ],
185 edges: vec![EdgeDef {
186 id: "e1".to_string(),
187 from: "start".to_string(),
188 to: "end".to_string(),
189 condition: None,
190 label: None,
191 }],
192 variables: HashMap::new(),
193 default_failure_strategy: FailureStrategy::Abort,
194 timeout_ms: None,
195 };
196
197 let engine = WorkflowEngine::new(workflow_def).unwrap();
198 let context = engine.run(HashMap::new()).await.unwrap();
199
200 assert_eq!(context.status, WorkflowStatus::Completed);
201 }
202
203 #[test]
204 fn test_template_rendering() {
205 let mut vars = HashMap::new();
206 vars.insert("name".to_string(), serde_json::json!("World"));
207 vars.insert("count".to_string(), serde_json::json!(42));
208
209 let result = render_template("Hello, {{name}}! Count: {{count}}", &vars).unwrap();
210 assert_eq!(result, "Hello, World! Count: 42");
211 }
212
213 #[test]
214 fn test_rule_validation() {
215 let mut engine = RuleEngine::new();
216 let mut context = HashMap::new();
217 context.insert("status".to_string(), serde_json::json!("success"));
218 context.insert("count".to_string(), serde_json::json!(10));
219
220 let rule = Rule::All {
221 rules: vec![
222 Rule::Equals {
223 field: "status".to_string(),
224 value: serde_json::json!("success"),
225 },
226 Rule::GreaterThan {
227 field: "count".to_string(),
228 value: 5.0,
229 },
230 ],
231 };
232
233 let result = engine.validate(&rule, &context).unwrap();
234 assert!(result.passed);
235 }
236}