rust_task_queue/
error.rs

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
54// Helper methods for creating common errors
55impl 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        // Test that TaskQueueError can be converted from serialization errors
154        // Since it's hard to trigger a serialization error with MessagePack,
155        // we'll test the error type and conversion structure
156
157        // Test with a direct TaskQueueError::Serialization
158        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(_) => {} // Success
163            _ => 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        // Create invalid msgpack data
172        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(_) => {} // Success
181            _ => 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        // Test that our error type properly implements the Error trait
210        let _: &dyn Error = &error;
211    }
212}