1use aion_core::{SearchAttributeError, WorkflowId};
4use aion_store::StoreError;
5
6#[derive(thiserror::Error, Clone, Debug, PartialEq, Eq)]
8#[error(
9 "non-determinism in workflow {workflow_id} at sequence {seq}: expected {expected}, found {found}"
10)]
11pub struct NonDeterminismError {
12 pub workflow_id: WorkflowId,
14 pub seq: u64,
16 pub expected: String,
18 pub found: String,
20}
21
22#[derive(thiserror::Error, Debug)]
24pub enum DurabilityError {
25 #[error("store error: {0}")]
27 Store(#[from] StoreError),
28
29 #[error("non-determinism violation: {0}")]
31 NonDeterminism(#[from] NonDeterminismError),
32
33 #[error("history shape error: {reason}")]
35 HistoryShape {
36 reason: String,
38 },
39
40 #[error("search attribute validation error: {0}")]
42 SearchAttribute(#[from] SearchAttributeError),
43
44 #[error("engine task epoch closed: {reason}")]
54 EngineTaskEpochClosed {
55 reason: String,
57 },
58}
59
60#[cfg(test)]
61mod tests {
62 use super::{DurabilityError, NonDeterminismError};
63 use aion_core::WorkflowId;
64 use aion_store::StoreError;
65
66 fn non_determinism_error() -> NonDeterminismError {
67 NonDeterminismError {
68 workflow_id: WorkflowId::new(uuid::Uuid::nil()),
69 seq: 42,
70 expected: "activity schedule ordinal 7".to_owned(),
71 found: "timer fired timer:named:deadline".to_owned(),
72 }
73 }
74
75 #[test]
76 fn non_determinism_display_includes_context() {
77 let error = non_determinism_error();
78
79 let message = error.to_string();
80
81 assert!(message.contains("00000000-0000-0000-0000-000000000000"));
82 assert!(message.contains("42"));
83 assert!(message.contains("activity schedule ordinal 7"));
84 assert!(message.contains("timer fired timer:named:deadline"));
85 }
86
87 #[test]
88 fn durability_error_display_mentions_underlying_cause() {
89 let store = DurabilityError::Store(StoreError::SequenceConflict {
90 expected: 10,
91 found: 11,
92 });
93 let non_determinism = DurabilityError::NonDeterminism(non_determinism_error());
94 let history_shape = DurabilityError::HistoryShape {
95 reason: "activity result without preceding schedule".to_owned(),
96 };
97
98 let store_message = store.to_string();
99 let non_determinism_message = non_determinism.to_string();
100 let history_shape_message = history_shape.to_string();
101
102 assert!(!store_message.is_empty());
103 assert!(store_message.contains("sequence conflict"));
104 assert!(!non_determinism_message.is_empty());
105 assert!(non_determinism_message.contains("activity schedule ordinal 7"));
106 assert!(non_determinism_message.contains("timer fired timer:named:deadline"));
107 assert!(!history_shape_message.is_empty());
108 assert!(history_shape_message.contains("activity result without preceding schedule"));
109 }
110}