runtara_sdk/
error.rs

1// Copyright (C) 2025 SyncMyOrders Sp. z o.o.
2// SPDX-License-Identifier: AGPL-3.0-or-later
3//! SDK-specific error types.
4
5#[cfg(feature = "quic")]
6use runtara_protocol::ClientError;
7use thiserror::Error;
8
9/// Errors that can occur in the SDK.
10#[derive(Debug, Error)]
11pub enum SdkError {
12    /// Configuration error (missing or invalid environment variable)
13    #[error("configuration error: {0}")]
14    Config(String),
15
16    /// Connection to runtara-core failed
17    #[cfg(feature = "quic")]
18    #[error("connection error: {0}")]
19    Connection(#[from] ClientError),
20
21    /// Registration with runtara-core failed
22    #[error("registration failed: {0}")]
23    Registration(String),
24
25    /// Checkpoint operation failed
26    #[error("checkpoint error: {0}")]
27    Checkpoint(String),
28
29    /// Sleep request failed
30    #[error("sleep error: {0}")]
31    Sleep(String),
32
33    /// Event sending failed
34    #[error("event error: {0}")]
35    Event(String),
36
37    /// Signal error
38    #[error("signal error: {0}")]
39    Signal(String),
40
41    /// Status query failed
42    #[error("status error: {0}")]
43    Status(String),
44
45    /// Server returned an error response
46    #[error("server error: {code} - {message}")]
47    Server {
48        /// Error code from the server
49        code: String,
50        /// Error message from the server
51        message: String,
52    },
53
54    /// Instance was cancelled
55    #[error("instance cancelled")]
56    Cancelled,
57
58    /// Instance was paused
59    #[error("instance paused")]
60    Paused,
61
62    /// Serialization/deserialization error
63    #[error("serialization error: {0}")]
64    Serialization(String),
65
66    /// Unexpected response from server
67    #[error("unexpected response: {0}")]
68    UnexpectedResponse(String),
69
70    /// Internal SDK error
71    #[error("internal error: {0}")]
72    Internal(String),
73}
74
75impl From<prost::DecodeError> for SdkError {
76    fn from(err: prost::DecodeError) -> Self {
77        SdkError::Serialization(err.to_string())
78    }
79}
80
81/// Type alias for SDK results.
82pub type Result<T> = std::result::Result<T, SdkError>;
83
84#[cfg(test)]
85mod tests {
86    use super::*;
87
88    #[test]
89    fn test_config_error_display() {
90        let err = SdkError::Config("missing RUNTARA_INSTANCE_ID".to_string());
91        assert_eq!(
92            format!("{}", err),
93            "configuration error: missing RUNTARA_INSTANCE_ID"
94        );
95    }
96
97    #[test]
98    fn test_registration_error_display() {
99        let err = SdkError::Registration("instance already exists".to_string());
100        assert_eq!(
101            format!("{}", err),
102            "registration failed: instance already exists"
103        );
104    }
105
106    #[test]
107    fn test_checkpoint_error_display() {
108        let err = SdkError::Checkpoint("failed to save state".to_string());
109        assert_eq!(format!("{}", err), "checkpoint error: failed to save state");
110    }
111
112    #[test]
113    fn test_sleep_error_display() {
114        let err = SdkError::Sleep("invalid duration".to_string());
115        assert_eq!(format!("{}", err), "sleep error: invalid duration");
116    }
117
118    #[test]
119    fn test_event_error_display() {
120        let err = SdkError::Event("failed to send event".to_string());
121        assert_eq!(format!("{}", err), "event error: failed to send event");
122    }
123
124    #[test]
125    fn test_signal_error_display() {
126        let err = SdkError::Signal("signal rejected".to_string());
127        assert_eq!(format!("{}", err), "signal error: signal rejected");
128    }
129
130    #[test]
131    fn test_status_error_display() {
132        let err = SdkError::Status("not found".to_string());
133        assert_eq!(format!("{}", err), "status error: not found");
134    }
135
136    #[test]
137    fn test_server_error_display() {
138        let err = SdkError::Server {
139            code: "ERR_NOT_FOUND".to_string(),
140            message: "Instance not found".to_string(),
141        };
142        assert_eq!(
143            format!("{}", err),
144            "server error: ERR_NOT_FOUND - Instance not found"
145        );
146    }
147
148    #[test]
149    fn test_cancelled_error_display() {
150        let err = SdkError::Cancelled;
151        assert_eq!(format!("{}", err), "instance cancelled");
152    }
153
154    #[test]
155    fn test_paused_error_display() {
156        let err = SdkError::Paused;
157        assert_eq!(format!("{}", err), "instance paused");
158    }
159
160    #[test]
161    fn test_serialization_error_display() {
162        let err = SdkError::Serialization("invalid JSON".to_string());
163        assert_eq!(format!("{}", err), "serialization error: invalid JSON");
164    }
165
166    #[test]
167    fn test_unexpected_response_error_display() {
168        let err = SdkError::UnexpectedResponse("expected Ack".to_string());
169        assert_eq!(format!("{}", err), "unexpected response: expected Ack");
170    }
171
172    #[test]
173    fn test_internal_error_display() {
174        let err = SdkError::Internal("mutex poisoned".to_string());
175        assert_eq!(format!("{}", err), "internal error: mutex poisoned");
176    }
177
178    #[test]
179    fn test_error_debug() {
180        let err = SdkError::Config("test".to_string());
181        let debug_str = format!("{:?}", err);
182        assert!(debug_str.contains("Config"));
183        assert!(debug_str.contains("test"));
184    }
185
186    #[test]
187    fn test_server_error_debug() {
188        let err = SdkError::Server {
189            code: "ERR_500".to_string(),
190            message: "Internal error".to_string(),
191        };
192        let debug_str = format!("{:?}", err);
193        assert!(debug_str.contains("Server"));
194        assert!(debug_str.contains("ERR_500"));
195        assert!(debug_str.contains("Internal error"));
196    }
197
198    #[test]
199    fn test_from_prost_decode_error() {
200        // Create a decode error by trying to decode invalid protobuf
201        let invalid_bytes = vec![0xFF, 0xFF, 0xFF];
202        let decode_result: std::result::Result<runtara_protocol::instance_proto::InstanceEvent, _> =
203            prost::Message::decode(invalid_bytes.as_slice());
204
205        if let Err(decode_error) = decode_result {
206            let sdk_error: SdkError = decode_error.into();
207            let msg = format!("{}", sdk_error);
208            assert!(msg.starts_with("serialization error:"));
209        }
210    }
211
212    #[test]
213    fn test_result_type_alias() {
214        fn returns_ok() -> Result<i32> {
215            Ok(42)
216        }
217
218        fn returns_err() -> Result<i32> {
219            Err(SdkError::Internal("test".to_string()))
220        }
221
222        assert!(returns_ok().is_ok());
223        assert!(returns_err().is_err());
224    }
225}