wfe_core/primitives/
sequence.rs1use async_trait::async_trait;
2use serde_json::json;
3
4use crate::models::ExecutionResult;
5use crate::traits::step::{StepBody, StepExecutionContext};
6
7#[derive(Default)]
10pub struct SequenceStep;
11
12#[async_trait]
13impl StepBody for SequenceStep {
14 async fn run(&mut self, context: &StepExecutionContext<'_>) -> crate::Result<ExecutionResult> {
15 let mut scope = context.execution_pointer.scope.clone();
16 scope.push(context.execution_pointer.id.clone());
17
18 let has_children = !context.step.children.is_empty();
21 let children_spawned = context
22 .workflow
23 .execution_pointers
24 .iter()
25 .any(|p| p.scope == scope);
26
27 if has_children && !children_spawned {
28 Ok(ExecutionResult::branch(vec![json!(null)], None))
29 } else if context.workflow.is_branch_complete(&scope) {
30 Ok(ExecutionResult::next())
31 } else {
32 Ok(ExecutionResult::persist(json!({"children_active": true})))
33 }
34 }
35}
36
37#[cfg(test)]
38mod tests {
39 use super::*;
40 use crate::models::{ExecutionPointer, PointerStatus};
41 use crate::primitives::test_helpers::*;
42
43 #[tokio::test]
44 async fn children_complete_proceeds() {
45 let mut step = SequenceStep;
46 let pointer = ExecutionPointer::new(0);
47 let wf_step = default_step();
48
49 let mut workflow = default_workflow();
50 let mut child = ExecutionPointer::new(1);
51 child.scope = vec![pointer.id.clone()];
52 child.status = PointerStatus::Complete;
53 workflow.execution_pointers.push(child);
54
55 let ctx = make_context(&pointer, &wf_step, &workflow);
56 let result = step.run(&ctx).await.unwrap();
57 assert!(result.proceed);
58 }
59
60 #[tokio::test]
61 async fn children_incomplete_persists() {
62 let mut step = SequenceStep;
63 let pointer = ExecutionPointer::new(0);
64 let wf_step = default_step();
65
66 let mut workflow = default_workflow();
67 let mut child = ExecutionPointer::new(1);
68 child.scope = vec![pointer.id.clone()];
69 child.status = PointerStatus::Running;
70 workflow.execution_pointers.push(child);
71
72 let ctx = make_context(&pointer, &wf_step, &workflow);
73 let result = step.run(&ctx).await.unwrap();
74 assert!(!result.proceed);
75 assert_eq!(
76 result.persistence_data,
77 Some(json!({"children_active": true}))
78 );
79 }
80
81 #[tokio::test]
82 async fn no_children_in_scope_proceeds() {
83 let mut step = SequenceStep;
84 let pointer = ExecutionPointer::new(0);
85 let wf_step = default_step();
86 let workflow = default_workflow();
87
88 let ctx = make_context(&pointer, &wf_step, &workflow);
89 let result = step.run(&ctx).await.unwrap();
90 assert!(result.proceed);
92 }
93
94 #[tokio::test]
95 async fn spawns_children_when_defined() {
96 let mut step = SequenceStep;
97 let pointer = ExecutionPointer::new(0);
98 let mut wf_step = default_step();
99 wf_step.children = vec![1, 2];
100
101 let workflow = default_workflow();
102
103 let ctx = make_context(&pointer, &wf_step, &workflow);
104 let result = step.run(&ctx).await.unwrap();
105 assert!(!result.proceed);
106 assert!(result.branch_values.is_some());
107 }
108}