wfe_core/models/
workflow_instance.rs1use chrono::{DateTime, Utc};
2use serde::{Deserialize, Serialize};
3
4use super::execution_pointer::ExecutionPointer;
5use super::status::{PointerStatus, WorkflowStatus};
6
7#[derive(Debug, Clone, Serialize, Deserialize)]
8pub struct WorkflowInstance {
9 pub id: String,
10 pub workflow_definition_id: String,
11 pub version: u32,
12 pub description: Option<String>,
13 pub reference: Option<String>,
14 pub execution_pointers: Vec<ExecutionPointer>,
15 pub next_execution: Option<i64>,
16 pub status: WorkflowStatus,
17 pub data: serde_json::Value,
18 pub create_time: DateTime<Utc>,
19 pub complete_time: Option<DateTime<Utc>>,
20}
21
22impl WorkflowInstance {
23 pub fn new(workflow_definition_id: impl Into<String>, version: u32, data: serde_json::Value) -> Self {
24 Self {
25 id: uuid::Uuid::new_v4().to_string(),
26 workflow_definition_id: workflow_definition_id.into(),
27 version,
28 description: None,
29 reference: None,
30 execution_pointers: Vec::new(),
31 next_execution: Some(0),
32 status: WorkflowStatus::Runnable,
33 data,
34 create_time: Utc::now(),
35 complete_time: None,
36 }
37 }
38
39 pub fn is_branch_complete(&self, scope: &[String]) -> bool {
41 self.execution_pointers
42 .iter()
43 .filter(|p| p.scope == scope)
44 .all(|p| {
45 matches!(
46 p.status,
47 PointerStatus::Complete
48 | PointerStatus::Compensated
49 | PointerStatus::Cancelled
50 | PointerStatus::Failed
51 )
52 })
53 }
54}
55
56#[cfg(test)]
57mod tests {
58 use super::*;
59 use pretty_assertions::assert_eq;
60
61 #[test]
62 fn new_instance_defaults() {
63 let instance = WorkflowInstance::new("test-workflow", 1, serde_json::json!({}));
64 assert_eq!(instance.workflow_definition_id, "test-workflow");
65 assert_eq!(instance.version, 1);
66 assert_eq!(instance.status, WorkflowStatus::Runnable);
67 assert_eq!(instance.next_execution, Some(0));
68 assert!(instance.execution_pointers.is_empty());
69 assert!(instance.complete_time.is_none());
70 }
71
72 #[test]
73 fn is_branch_complete_empty_scope_returns_true() {
74 let instance = WorkflowInstance::new("test", 1, serde_json::json!({}));
75 assert!(instance.is_branch_complete(&[]));
76 }
77
78 #[test]
79 fn is_branch_complete_all_complete() {
80 let scope = vec!["parent-1".to_string()];
81 let mut instance = WorkflowInstance::new("test", 1, serde_json::json!({}));
82
83 let mut p1 = ExecutionPointer::new(0);
84 p1.scope = scope.clone();
85 p1.status = PointerStatus::Complete;
86
87 let mut p2 = ExecutionPointer::new(1);
88 p2.scope = scope.clone();
89 p2.status = PointerStatus::Compensated;
90
91 instance.execution_pointers = vec![p1, p2];
92 assert!(instance.is_branch_complete(&scope));
93 }
94
95 #[test]
96 fn is_branch_complete_with_active_pointer() {
97 let scope = vec!["parent-1".to_string()];
98 let mut instance = WorkflowInstance::new("test", 1, serde_json::json!({}));
99
100 let mut p1 = ExecutionPointer::new(0);
101 p1.scope = scope.clone();
102 p1.status = PointerStatus::Complete;
103
104 let mut p2 = ExecutionPointer::new(1);
105 p2.scope = scope.clone();
106 p2.status = PointerStatus::Running;
107
108 instance.execution_pointers = vec![p1, p2];
109 assert!(!instance.is_branch_complete(&scope));
110 }
111
112 #[test]
113 fn is_branch_complete_ignores_different_scope() {
114 let scope_a = vec!["parent-a".to_string()];
115 let scope_b = vec!["parent-b".to_string()];
116 let mut instance = WorkflowInstance::new("test", 1, serde_json::json!({}));
117
118 let mut p1 = ExecutionPointer::new(0);
119 p1.scope = scope_a.clone();
120 p1.status = PointerStatus::Complete;
121
122 let mut p2 = ExecutionPointer::new(1);
123 p2.scope = scope_b.clone();
124 p2.status = PointerStatus::Running;
125
126 instance.execution_pointers = vec![p1, p2];
127 assert!(instance.is_branch_complete(&scope_a));
128 }
129
130 #[test]
131 fn serde_round_trip() {
132 let instance = WorkflowInstance::new("my-workflow", 2, serde_json::json!({"key": "value"}));
133 let json = serde_json::to_string(&instance).unwrap();
134 let deserialized: WorkflowInstance = serde_json::from_str(&json).unwrap();
135 assert_eq!(instance.id, deserialized.id);
136 assert_eq!(instance.workflow_definition_id, deserialized.workflow_definition_id);
137 assert_eq!(instance.version, deserialized.version);
138 assert_eq!(instance.status, deserialized.status);
139 assert_eq!(instance.data, deserialized.data);
140 }
141}