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
45#[cfg(test)]
46mod tests {
47 use super::{DurabilityError, NonDeterminismError};
48 use aion_core::WorkflowId;
49 use aion_store::StoreError;
50
51 fn non_determinism_error() -> NonDeterminismError {
52 NonDeterminismError {
53 workflow_id: WorkflowId::new(uuid::Uuid::nil()),
54 seq: 42,
55 expected: "activity schedule ordinal 7".to_owned(),
56 found: "timer fired timer:named:deadline".to_owned(),
57 }
58 }
59
60 #[test]
61 fn non_determinism_display_includes_context() {
62 let error = non_determinism_error();
63
64 let message = error.to_string();
65
66 assert!(message.contains("00000000-0000-0000-0000-000000000000"));
67 assert!(message.contains("42"));
68 assert!(message.contains("activity schedule ordinal 7"));
69 assert!(message.contains("timer fired timer:named:deadline"));
70 }
71
72 #[test]
73 fn durability_error_display_mentions_underlying_cause() {
74 let store = DurabilityError::Store(StoreError::SequenceConflict {
75 expected: 10,
76 found: 11,
77 });
78 let non_determinism = DurabilityError::NonDeterminism(non_determinism_error());
79 let history_shape = DurabilityError::HistoryShape {
80 reason: "activity result without preceding schedule".to_owned(),
81 };
82
83 let store_message = store.to_string();
84 let non_determinism_message = non_determinism.to_string();
85 let history_shape_message = history_shape.to_string();
86
87 assert!(!store_message.is_empty());
88 assert!(store_message.contains("sequence conflict"));
89 assert!(!non_determinism_message.is_empty());
90 assert!(non_determinism_message.contains("activity schedule ordinal 7"));
91 assert!(non_determinism_message.contains("timer fired timer:named:deadline"));
92 assert!(!history_shape_message.is_empty());
93 assert!(history_shape_message.contains("activity result without preceding schedule"));
94 }
95}