1use thiserror::Error;
2
3#[derive(Error, Debug)]
4pub enum TaskQueueError {
5 #[error("Redis error: {0}")]
6 Redis(#[from] redis::RedisError),
7
8 #[error("Connection pool error: {0}")]
9 Pool(#[from] deadpool_redis::PoolError),
10
11 #[error("Serialization error: {0}")]
12 Serialization(#[from] rmp_serde::encode::Error),
13
14 #[error("Deserialization error: {0}")]
15 Deserialization(#[from] rmp_serde::decode::Error),
16
17 #[error("Task execution error: {0}")]
18 TaskExecution(String),
19
20 #[error("Task not found: {0}")]
21 TaskNotFound(String),
22
23 #[error("Task timeout: task {id} exceeded {timeout_seconds}s")]
24 TaskTimeout { id: String, timeout_seconds: u64 },
25
26 #[error("Connection error: {0}")]
27 Connection(String),
28
29 #[error("Configuration error: {0}")]
30 Configuration(String),
31
32 #[error("Worker error: {0}")]
33 Worker(String),
34
35 #[error("Scheduler error: {0}")]
36 Scheduler(String),
37
38 #[error("Queue error: {0}")]
39 Queue(String),
40
41 #[error("Broker error: {0}")]
42 Broker(String),
43
44 #[error("Scheduling error: {0}")]
45 Scheduling(String),
46
47 #[error("Auto-scaling error: {0}")]
48 AutoScaling(String),
49
50 #[error("Registry error: {0}")]
51 Registry(String),
52}
53
54impl TaskQueueError {
56 pub fn task_not_found(task_name: &str) -> Self {
57 Self::TaskNotFound(task_name.to_string())
58 }
59
60 pub fn task_timeout(task_id: &str, timeout_seconds: u64) -> Self {
61 Self::TaskTimeout {
62 id: task_id.to_string(),
63 timeout_seconds,
64 }
65 }
66}
67
68#[cfg(test)]
69mod tests {
70 use super::*;
71
72 #[test]
73 fn test_task_not_found_helper() {
74 let error = TaskQueueError::task_not_found("my_task");
75 match &error {
76 TaskQueueError::TaskNotFound(name) => assert_eq!(name, "my_task"),
77 _ => panic!("Expected TaskNotFound error"),
78 }
79 assert_eq!(error.to_string(), "Task not found: my_task");
80 }
81
82 #[test]
83 fn test_task_timeout_helper() {
84 let error = TaskQueueError::task_timeout("task_123", 300);
85 match &error {
86 TaskQueueError::TaskTimeout {
87 id,
88 timeout_seconds,
89 } => {
90 assert_eq!(id, "task_123");
91 assert_eq!(*timeout_seconds, 300);
92 }
93 _ => panic!("Expected TaskTimeout error"),
94 }
95 assert_eq!(
96 error.to_string(),
97 "Task timeout: task task_123 exceeded 300s"
98 );
99 }
100
101 #[test]
102 fn test_error_display_messages() {
103 let test_cases = vec![
104 (
105 TaskQueueError::TaskExecution("Failed to process".to_string()),
106 "Task execution error: Failed to process",
107 ),
108 (
109 TaskQueueError::Connection("Redis down".to_string()),
110 "Connection error: Redis down",
111 ),
112 (
113 TaskQueueError::Configuration("Invalid config".to_string()),
114 "Configuration error: Invalid config",
115 ),
116 (
117 TaskQueueError::Worker("Worker crash".to_string()),
118 "Worker error: Worker crash",
119 ),
120 (
121 TaskQueueError::Scheduler("Schedule failed".to_string()),
122 "Scheduler error: Schedule failed",
123 ),
124 (
125 TaskQueueError::Queue("Queue full".to_string()),
126 "Queue error: Queue full",
127 ),
128 (
129 TaskQueueError::Broker("Broker error".to_string()),
130 "Broker error: Broker error",
131 ),
132 (
133 TaskQueueError::Scheduling("Schedule conflict".to_string()),
134 "Scheduling error: Schedule conflict",
135 ),
136 (
137 TaskQueueError::AutoScaling("Scale failed".to_string()),
138 "Auto-scaling error: Scale failed",
139 ),
140 (
141 TaskQueueError::Registry("Registry error".to_string()),
142 "Registry error: Registry error",
143 ),
144 ];
145
146 for (error, expected_message) in test_cases {
147 assert_eq!(error.to_string(), expected_message);
148 }
149 }
150
151 #[test]
152 fn test_serialization_error_conversion() {
153 let custom_error = rmp_serde::encode::Error::Syntax("Test serialization error".to_string());
159 let queue_error: TaskQueueError = custom_error.into();
160
161 match queue_error {
162 TaskQueueError::Serialization(_) => {} _ => panic!("Expected Serialization error"),
164 }
165
166 assert!(queue_error.to_string().contains("Serialization error"));
167 }
168
169 #[test]
170 fn test_deserialization_error_conversion() {
171 let invalid_data = vec![0xFF, 0xFF, 0xFF, 0xFF];
173 let result: Result<String, rmp_serde::decode::Error> = rmp_serde::from_slice(&invalid_data);
174 assert!(result.is_err());
175
176 let deserialization_error = result.unwrap_err();
177 let queue_error: TaskQueueError = deserialization_error.into();
178
179 match queue_error {
180 TaskQueueError::Deserialization(_) => {} _ => panic!("Expected Deserialization error"),
182 }
183 }
184
185 #[test]
186 fn test_error_debug_formatting() {
187 let error = TaskQueueError::TaskExecution("Test error".to_string());
188 let debug_str = format!("{:?}", error);
189 assert!(debug_str.contains("TaskExecution"));
190 assert!(debug_str.contains("Test error"));
191 }
192
193 #[test]
194 fn test_error_send_sync_traits() {
195 fn assert_send<T: Send>() {}
196 fn assert_sync<T: Sync>() {}
197
198 assert_send::<TaskQueueError>();
199 assert_sync::<TaskQueueError>();
200 }
201
202 #[test]
203 fn test_error_source_chain() {
204 use std::error::Error;
205
206 let error = TaskQueueError::TaskExecution("Root cause".to_string());
207 assert!(error.source().is_none());
208
209 let _: &dyn Error = &error;
211 }
212}