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