Skip to main content

wfe_core/primitives/
sequence.rs

1use async_trait::async_trait;
2use serde_json::json;
3
4use crate::models::ExecutionResult;
5use crate::traits::step::{StepBody, StepExecutionContext};
6
7/// A container step that executes its children sequentially.
8/// Completes when all children have finished.
9#[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        if context.workflow.is_branch_complete(&scope) {
19            Ok(ExecutionResult::next())
20        } else {
21            Ok(ExecutionResult::persist(json!({"children_active": true})))
22        }
23    }
24}
25
26#[cfg(test)]
27mod tests {
28    use super::*;
29    use crate::models::{ExecutionPointer, PointerStatus};
30    use crate::primitives::test_helpers::*;
31
32    #[tokio::test]
33    async fn children_complete_proceeds() {
34        let mut step = SequenceStep;
35        let pointer = ExecutionPointer::new(0);
36        let wf_step = default_step();
37
38        let mut workflow = default_workflow();
39        let mut child = ExecutionPointer::new(1);
40        child.scope = vec![pointer.id.clone()];
41        child.status = PointerStatus::Complete;
42        workflow.execution_pointers.push(child);
43
44        let ctx = make_context(&pointer, &wf_step, &workflow);
45        let result = step.run(&ctx).await.unwrap();
46        assert!(result.proceed);
47    }
48
49    #[tokio::test]
50    async fn children_incomplete_persists() {
51        let mut step = SequenceStep;
52        let pointer = ExecutionPointer::new(0);
53        let wf_step = default_step();
54
55        let mut workflow = default_workflow();
56        let mut child = ExecutionPointer::new(1);
57        child.scope = vec![pointer.id.clone()];
58        child.status = PointerStatus::Running;
59        workflow.execution_pointers.push(child);
60
61        let ctx = make_context(&pointer, &wf_step, &workflow);
62        let result = step.run(&ctx).await.unwrap();
63        assert!(!result.proceed);
64        assert_eq!(
65            result.persistence_data,
66            Some(json!({"children_active": true}))
67        );
68    }
69
70    #[tokio::test]
71    async fn no_children_in_scope_proceeds() {
72        let mut step = SequenceStep;
73        let pointer = ExecutionPointer::new(0);
74        let wf_step = default_step();
75        let workflow = default_workflow();
76
77        let ctx = make_context(&pointer, &wf_step, &workflow);
78        let result = step.run(&ctx).await.unwrap();
79        // No children in scope means is_branch_complete returns true (vacuously).
80        assert!(result.proceed);
81    }
82}