wfe_core/primitives/
while_step.rs1use async_trait::async_trait;
2use serde_json::json;
3
4use crate::models::ExecutionResult;
5use crate::traits::step::{StepBody, StepExecutionContext};
6
7#[derive(Default)]
9pub struct WhileStep {
10 pub condition: bool,
11}
12
13#[async_trait]
14impl StepBody for WhileStep {
15 async fn run(&mut self, context: &StepExecutionContext<'_>) -> crate::Result<ExecutionResult> {
16 let children_active = context
17 .persistence_data
18 .and_then(|d| d.get("children_active"))
19 .and_then(|v| v.as_bool())
20 .unwrap_or(false);
21
22 if children_active {
23 let mut scope = context.execution_pointer.scope.clone();
25 scope.push(context.execution_pointer.id.clone());
26
27 if context.workflow.is_branch_complete(&scope) {
28 if self.condition {
30 Ok(ExecutionResult::branch(
32 vec![json!(null)],
33 Some(json!({"children_active": true})),
34 ))
35 } else {
36 Ok(ExecutionResult::next())
37 }
38 } else {
39 Ok(ExecutionResult::persist(json!({"children_active": true})))
40 }
41 } else {
42 if self.condition {
44 Ok(ExecutionResult::branch(
45 vec![json!(null)],
46 Some(json!({"children_active": true})),
47 ))
48 } else {
49 Ok(ExecutionResult::next())
50 }
51 }
52 }
53}
54
55#[cfg(test)]
56mod tests {
57 use super::*;
58 use crate::models::{ExecutionPointer, PointerStatus};
59 use crate::primitives::test_helpers::*;
60
61 #[tokio::test]
62 async fn condition_true_first_run_branches() {
63 let mut step = WhileStep { condition: true };
64 let pointer = ExecutionPointer::new(0);
65 let wf_step = default_step();
66 let workflow = default_workflow();
67 let ctx = make_context(&pointer, &wf_step, &workflow);
68
69 let result = step.run(&ctx).await.unwrap();
70 assert!(!result.proceed);
71 assert_eq!(result.branch_values, Some(vec![json!(null)]));
72 assert_eq!(result.persistence_data, Some(json!({"children_active": true})));
73 }
74
75 #[tokio::test]
76 async fn condition_false_first_run_proceeds() {
77 let mut step = WhileStep { condition: false };
78 let pointer = ExecutionPointer::new(0);
79 let wf_step = default_step();
80 let workflow = default_workflow();
81 let ctx = make_context(&pointer, &wf_step, &workflow);
82
83 let result = step.run(&ctx).await.unwrap();
84 assert!(result.proceed);
85 }
86
87 #[tokio::test]
88 async fn children_complete_and_condition_true_re_branches() {
89 let mut step = WhileStep { condition: true };
90 let mut pointer = ExecutionPointer::new(0);
91 pointer.persistence_data = Some(json!({"children_active": true}));
92
93 let wf_step = default_step();
94 let mut workflow = default_workflow();
95 let mut child = ExecutionPointer::new(1);
96 child.scope = vec![pointer.id.clone()];
97 child.status = PointerStatus::Complete;
98 workflow.execution_pointers.push(child);
99
100 let ctx = make_context(&pointer, &wf_step, &workflow);
101
102 let result = step.run(&ctx).await.unwrap();
103 assert!(!result.proceed);
104 assert_eq!(result.branch_values, Some(vec![json!(null)]));
105 }
106
107 #[tokio::test]
108 async fn children_complete_and_condition_false_proceeds() {
109 let mut step = WhileStep { condition: false };
110 let mut pointer = ExecutionPointer::new(0);
111 pointer.persistence_data = Some(json!({"children_active": true}));
112
113 let wf_step = default_step();
114 let mut workflow = default_workflow();
115 let mut child = ExecutionPointer::new(1);
116 child.scope = vec![pointer.id.clone()];
117 child.status = PointerStatus::Complete;
118 workflow.execution_pointers.push(child);
119
120 let ctx = make_context(&pointer, &wf_step, &workflow);
121
122 let result = step.run(&ctx).await.unwrap();
123 assert!(result.proceed);
124 }
125
126 #[tokio::test]
127 async fn children_incomplete_persists() {
128 let mut step = WhileStep { condition: true };
129 let mut pointer = ExecutionPointer::new(0);
130 pointer.persistence_data = Some(json!({"children_active": true}));
131
132 let wf_step = default_step();
133 let mut workflow = default_workflow();
134 let mut child = ExecutionPointer::new(1);
135 child.scope = vec![pointer.id.clone()];
136 child.status = PointerStatus::Running;
137 workflow.execution_pointers.push(child);
138
139 let ctx = make_context(&pointer, &wf_step, &workflow);
140
141 let result = step.run(&ctx).await.unwrap();
142 assert!(!result.proceed);
143 assert!(result.branch_values.is_none());
144 assert_eq!(result.persistence_data, Some(json!({"children_active": true})));
145 }
146}