1use crate::{
25 analytics::{
26 AnalyticsPeriod, ExecutionStats, PerformanceMetrics, PeriodType, WorkflowAnalytics,
27 },
28 execution::{ExecutionContext, ExecutionResult, NodeExecutionResult, NodeMetrics, TokenUsage},
29 node::{LlmConfig, LoopConfig, Node, NodeKind},
30 workflow::{Workflow, WorkflowMetadata},
31 Edge, WorkflowBuilder,
32};
33use chrono::Utc;
34use std::collections::HashMap;
35use uuid::Uuid;
36
37pub fn create_test_workflow(name: &str, node_count: usize) -> Workflow {
53 let mut builder = WorkflowBuilder::new(name);
54
55 builder = builder.start("Start");
57
58 for i in 1..node_count.saturating_sub(1) {
60 let llm_config = LlmConfig {
61 provider: "test".to_string(),
62 model: "gpt-4".to_string(),
63 system_prompt: None,
64 prompt_template: format!("Process step {}", i),
65 temperature: Some(0.7),
66 max_tokens: Some(1000),
67 tools: vec![],
68 images: vec![],
69 extra_params: serde_json::Value::Null,
70 };
71 builder = builder.llm(format!("LLM_{}", i), llm_config);
72 }
73
74 builder = builder.end("End");
76
77 builder.build()
78}
79
80pub fn create_branching_workflow(name: &str) -> Workflow {
97 use crate::node::{SwitchCase, SwitchConfig};
98
99 let mut builder = WorkflowBuilder::new(name);
100
101 builder = builder.start("Start");
103
104 let llm_config = LlmConfig {
106 provider: "test".to_string(),
107 model: "gpt-4".to_string(),
108 system_prompt: None,
109 prompt_template: "Initial processing".to_string(),
110 temperature: Some(0.7),
111 max_tokens: Some(1000),
112 tools: vec![],
113 images: vec![],
114 extra_params: serde_json::Value::Null,
115 };
116 builder = builder.llm("Process", llm_config);
117
118 let switch_config = SwitchConfig {
120 switch_on: "{{status}}".to_string(),
121 cases: vec![
122 SwitchCase {
123 match_value: "success".to_string(),
124 action: "Process success".to_string(),
125 },
126 SwitchCase {
127 match_value: "error".to_string(),
128 action: "Handle error".to_string(),
129 },
130 ],
131 default_case: Some("Default handling".to_string()),
132 };
133 builder = builder.switch("Router", switch_config);
134
135 builder = builder.end("End");
137
138 builder.build()
139}
140
141pub fn create_test_analytics(
159 workflow_name: &str,
160 total_executions: u64,
161 success_rate: f64,
162) -> WorkflowAnalytics {
163 let successful = (total_executions as f64 * success_rate) as u64;
164 let failed = total_executions - successful;
165
166 WorkflowAnalytics {
167 workflow_id: Uuid::new_v4(),
168 workflow_name: workflow_name.to_string(),
169 period: AnalyticsPeriod {
170 start: Utc::now(),
171 end: Utc::now(),
172 period_type: PeriodType::Daily,
173 },
174 execution_stats: ExecutionStats {
175 total_executions,
176 successful_executions: successful,
177 failed_executions: failed,
178 cancelled_executions: 0,
179 success_rate,
180 failure_rate: 1.0 - success_rate,
181 executions_per_hour: total_executions as f64 / 24.0,
182 },
183 performance_metrics: PerformanceMetrics {
184 avg_duration_ms: 1500.0,
185 p50_duration_ms: 1200,
186 p95_duration_ms: 3000,
187 p99_duration_ms: 4500,
188 min_duration_ms: 500,
189 max_duration_ms: 5000,
190 total_tokens: total_executions * 1000,
191 avg_tokens: 1000.0,
192 total_cost_usd: total_executions as f64 * 0.01,
193 avg_cost_usd: 0.01,
194 },
195 node_analytics: vec![],
196 error_patterns: vec![],
197 updated_at: Utc::now(),
198 }
199}
200
201pub fn create_test_execution_context() -> ExecutionContext {
212 ExecutionContext::new(Uuid::new_v4())
213}
214
215pub fn create_test_node_result(success: bool) -> NodeExecutionResult {
226 let result = NodeExecutionResult::new();
227
228 if success {
229 result
230 .with_metrics(NodeMetrics {
231 duration_ms: Some(100),
232 token_usage: Some(TokenUsage {
233 input_tokens: 50,
234 output_tokens: 100,
235 total_tokens: 150,
236 cached_tokens: None,
237 }),
238 cost_usd: Some(0.001),
239 api_calls: 1,
240 bytes_transferred: 1024,
241 memory_bytes: None,
242 custom: HashMap::new(),
243 })
244 .complete(ExecutionResult::Success(
245 serde_json::json!({"status": "success"}),
246 ))
247 } else {
248 result.complete(ExecutionResult::Failure("Test error".to_string()))
249 }
250}
251
252pub fn create_test_metadata(name: &str, version: &str) -> WorkflowMetadata {
264 WorkflowMetadata {
265 id: Uuid::new_v4(),
266 name: name.to_string(),
267 description: Some(format!("Test workflow: {}", name)),
268 version: version.to_string(),
269 created_at: Utc::now(),
270 updated_at: Utc::now(),
271 tags: vec!["test".to_string()],
272 parent_id: None,
273 change_description: None,
274 schedule: None,
275 }
276}
277
278pub fn create_test_llm_node(name: &str, prompt: &str) -> Node {
289 Node {
290 id: Uuid::new_v4(),
291 name: name.to_string(),
292 kind: NodeKind::LLM(LlmConfig {
293 provider: "test".to_string(),
294 model: "gpt-4".to_string(),
295 system_prompt: None,
296 prompt_template: prompt.to_string(),
297 temperature: Some(0.7),
298 max_tokens: Some(1000),
299 tools: vec![],
300 images: vec![],
301 extra_params: serde_json::Value::Null,
302 }),
303 position: None,
304 retry_config: None,
305 timeout_config: None,
306 }
307}
308
309pub fn create_test_edge(from: Uuid, to: Uuid, label: Option<&str>) -> Edge {
324 Edge {
325 id: Uuid::new_v4(),
326 from,
327 to,
328 label: label.map(String::from),
329 condition: None,
330 }
331}
332
333pub fn create_test_workflow_batch(count: usize, nodes_per_workflow: usize) -> Vec<Workflow> {
352 (0..count)
353 .map(|i| create_test_workflow(&format!("test_workflow_{}", i), nodes_per_workflow))
354 .collect()
355}
356
357pub fn create_loop_workflow(name: &str) -> Workflow {
369 use crate::node::LoopType;
370
371 let mut builder = WorkflowBuilder::new(name);
372
373 let loop_config = LoopConfig {
374 loop_type: LoopType::ForEach {
375 collection_path: "{{items}}".to_string(),
376 item_variable: "item".to_string(),
377 index_variable: None,
378 body_expression: "process item".to_string(),
379 parallel: false,
380 max_concurrency: None,
381 },
382 max_iterations: 10,
383 };
384
385 builder = builder
386 .start("Start")
387 .loop_node("Loop", loop_config)
388 .end("End");
389
390 builder.build()
391}
392
393#[cfg(test)]
394mod tests {
395 use super::*;
396 use crate::execution::ExecutionState;
397
398 #[test]
399 fn test_create_test_workflow() {
400 let workflow = create_test_workflow("test", 5);
401 assert_eq!(workflow.metadata.name, "test");
402 assert_eq!(workflow.nodes.len(), 5);
403 assert!(!workflow.edges.is_empty());
404 }
405
406 #[test]
407 fn test_create_test_workflow_minimum() {
408 let workflow = create_test_workflow("minimal", 2);
409 assert_eq!(workflow.nodes.len(), 2);
410 }
411
412 #[test]
413 fn test_create_branching_workflow() {
414 let workflow = create_branching_workflow("branch");
415 assert!(workflow.nodes.len() >= 4);
416 }
417
418 #[test]
419 fn test_create_test_analytics() {
420 let analytics = create_test_analytics("test", 100, 0.85);
421 assert_eq!(analytics.execution_stats.total_executions, 100);
422 assert_eq!(analytics.execution_stats.success_rate, 0.85);
423 assert_eq!(analytics.execution_stats.successful_executions, 85);
424 assert_eq!(analytics.execution_stats.failed_executions, 15);
425 }
426
427 #[test]
428 fn test_create_test_execution_context() {
429 let context = create_test_execution_context();
430 assert_eq!(context.state, ExecutionState::Running);
431 }
432
433 #[test]
434 fn test_create_test_node_result_success() {
435 let result = create_test_node_result(true);
436 assert!(result.completed_at.is_some());
437 assert!(matches!(result.result, ExecutionResult::Success(_)));
438 assert!(result.metrics.is_some());
439 }
440
441 #[test]
442 fn test_create_test_node_result_failure() {
443 let result = create_test_node_result(false);
444 assert!(result.completed_at.is_some());
445 assert!(matches!(result.result, ExecutionResult::Failure(_)));
446 }
447
448 #[test]
449 fn test_create_test_metadata() {
450 let metadata = create_test_metadata("my_workflow", "1.0.0");
451 assert_eq!(metadata.name, "my_workflow");
452 assert_eq!(metadata.version, "1.0.0");
453 assert!(metadata.description.is_some());
454 assert!(!metadata.tags.is_empty());
455 }
456
457 #[test]
458 fn test_create_test_llm_node() {
459 let node = create_test_llm_node("test", "prompt");
460 assert_eq!(node.name, "test");
461 match node.kind {
462 NodeKind::LLM(config) => {
463 assert_eq!(config.prompt_template, "prompt");
464 }
465 _ => panic!("Expected LLM node"),
466 }
467 }
468
469 #[test]
470 fn test_create_test_edge() {
471 let from = Uuid::new_v4();
472 let to = Uuid::new_v4();
473 let edge = create_test_edge(from, to, Some("success"));
474 assert_eq!(edge.from, from);
475 assert_eq!(edge.to, to);
476 assert_eq!(edge.label, Some("success".to_string()));
477 }
478
479 #[test]
480 fn test_create_test_workflow_batch() {
481 let workflows = create_test_workflow_batch(3, 5);
482 assert_eq!(workflows.len(), 3);
483 for workflow in workflows {
484 assert_eq!(workflow.nodes.len(), 5);
485 }
486 }
487
488 #[test]
489 fn test_create_loop_workflow() {
490 let workflow = create_loop_workflow("loop");
491 assert!(!workflow.nodes.is_empty());
492 assert_eq!(workflow.metadata.name, "loop");
493 }
494
495 #[test]
496 fn test_analytics_calculations() {
497 let analytics = create_test_analytics("test", 200, 0.75);
498 assert_eq!(analytics.execution_stats.successful_executions, 150);
499 assert_eq!(analytics.execution_stats.failed_executions, 50);
500 assert_eq!(analytics.execution_stats.failure_rate, 0.25);
501 }
502}