mockforge_collab/
error.rs1pub type Result<T> = std::result::Result<T, CollabError>;
5
6#[derive(Debug, thiserror::Error)]
8pub enum CollabError {
9 #[error("Authentication failed: {0}")]
11 AuthenticationFailed(String),
12
13 #[error("Authorization failed: {0}")]
15 AuthorizationFailed(String),
16
17 #[error("Workspace not found: {0}")]
19 WorkspaceNotFound(String),
20
21 #[error("User not found: {0}")]
23 UserNotFound(String),
24
25 #[error("Conflict detected: {0}")]
27 ConflictDetected(String),
28
29 #[error("Sync error: {0}")]
31 SyncError(String),
32
33 #[error("Database error: {0}")]
35 DatabaseError(String),
36
37 #[error("WebSocket error: {0}")]
39 WebSocketError(String),
40
41 #[error("Serialization error: {0}")]
43 SerializationError(String),
44
45 #[error("Invalid input: {0}")]
47 InvalidInput(String),
48
49 #[error("Resource already exists: {0}")]
51 AlreadyExists(String),
52
53 #[error("Operation timeout: {0}")]
55 Timeout(String),
56
57 #[error("Connection error: {0}")]
59 ConnectionError(String),
60
61 #[error("Version mismatch: expected {expected}, got {actual}")]
63 VersionMismatch {
64 expected: u64,
66 actual: u64,
68 },
69
70 #[error("Internal error: {0}")]
72 Internal(String),
73}
74
75impl From<sqlx::Error> for CollabError {
76 fn from(err: sqlx::Error) -> Self {
77 Self::DatabaseError(err.to_string())
78 }
79}
80
81impl From<serde_json::Error> for CollabError {
82 fn from(err: serde_json::Error) -> Self {
83 Self::SerializationError(err.to_string())
84 }
85}
86
87impl From<tokio::time::error::Elapsed> for CollabError {
88 fn from(err: tokio::time::error::Elapsed) -> Self {
89 Self::Timeout(err.to_string())
90 }
91}
92
93impl From<sqlx::migrate::MigrateError> for CollabError {
94 fn from(err: sqlx::migrate::MigrateError) -> Self {
95 Self::DatabaseError(err.to_string())
96 }
97}
98
99#[cfg(test)]
100mod tests {
101 use super::*;
102
103 #[test]
104 fn test_authentication_failed() {
105 let err = CollabError::AuthenticationFailed("invalid token".to_string());
106 let msg = err.to_string();
107 assert!(msg.contains("Authentication failed"));
108 assert!(msg.contains("invalid token"));
109 }
110
111 #[test]
112 fn test_authorization_failed() {
113 let err = CollabError::AuthorizationFailed("missing permission".to_string());
114 let msg = err.to_string();
115 assert!(msg.contains("Authorization failed"));
116 assert!(msg.contains("missing permission"));
117 }
118
119 #[test]
120 fn test_workspace_not_found() {
121 let err = CollabError::WorkspaceNotFound("ws-123".to_string());
122 let msg = err.to_string();
123 assert!(msg.contains("Workspace not found"));
124 assert!(msg.contains("ws-123"));
125 }
126
127 #[test]
128 fn test_user_not_found() {
129 let err = CollabError::UserNotFound("user-456".to_string());
130 let msg = err.to_string();
131 assert!(msg.contains("User not found"));
132 assert!(msg.contains("user-456"));
133 }
134
135 #[test]
136 fn test_conflict_detected() {
137 let err = CollabError::ConflictDetected("concurrent edit".to_string());
138 let msg = err.to_string();
139 assert!(msg.contains("Conflict detected"));
140 assert!(msg.contains("concurrent edit"));
141 }
142
143 #[test]
144 fn test_sync_error() {
145 let err = CollabError::SyncError("sync failed".to_string());
146 let msg = err.to_string();
147 assert!(msg.contains("Sync error"));
148 assert!(msg.contains("sync failed"));
149 }
150
151 #[test]
152 fn test_database_error() {
153 let err = CollabError::DatabaseError("connection refused".to_string());
154 let msg = err.to_string();
155 assert!(msg.contains("Database error"));
156 assert!(msg.contains("connection refused"));
157 }
158
159 #[test]
160 fn test_websocket_error() {
161 let err = CollabError::WebSocketError("connection closed".to_string());
162 let msg = err.to_string();
163 assert!(msg.contains("WebSocket error"));
164 assert!(msg.contains("connection closed"));
165 }
166
167 #[test]
168 fn test_serialization_error() {
169 let err = CollabError::SerializationError("invalid json".to_string());
170 let msg = err.to_string();
171 assert!(msg.contains("Serialization error"));
172 assert!(msg.contains("invalid json"));
173 }
174
175 #[test]
176 fn test_invalid_input() {
177 let err = CollabError::InvalidInput("empty name".to_string());
178 let msg = err.to_string();
179 assert!(msg.contains("Invalid input"));
180 assert!(msg.contains("empty name"));
181 }
182
183 #[test]
184 fn test_already_exists() {
185 let err = CollabError::AlreadyExists("workspace-name".to_string());
186 let msg = err.to_string();
187 assert!(msg.contains("already exists"));
188 assert!(msg.contains("workspace-name"));
189 }
190
191 #[test]
192 fn test_timeout() {
193 let err = CollabError::Timeout("operation timed out".to_string());
194 let msg = err.to_string();
195 assert!(msg.contains("timeout"));
196 assert!(msg.contains("operation timed out"));
197 }
198
199 #[test]
200 fn test_connection_error() {
201 let err = CollabError::ConnectionError("host unreachable".to_string());
202 let msg = err.to_string();
203 assert!(msg.contains("Connection error"));
204 assert!(msg.contains("host unreachable"));
205 }
206
207 #[test]
208 fn test_version_mismatch() {
209 let err = CollabError::VersionMismatch {
210 expected: 10,
211 actual: 8,
212 };
213 let msg = err.to_string();
214 assert!(msg.contains("Version mismatch"));
215 assert!(msg.contains("10"));
216 assert!(msg.contains('8'));
217 }
218
219 #[test]
220 fn test_internal_error() {
221 let err = CollabError::Internal("unexpected failure".to_string());
222 let msg = err.to_string();
223 assert!(msg.contains("Internal error"));
224 assert!(msg.contains("unexpected failure"));
225 }
226
227 #[test]
228 fn test_from_serde_json_error() {
229 let json_err: serde_json::Error = serde_json::from_str::<String>("invalid").unwrap_err();
230 let err: CollabError = json_err.into();
231 assert!(matches!(err, CollabError::SerializationError(_)));
232 }
233
234 #[test]
235 fn test_error_debug() {
236 let err = CollabError::AuthenticationFailed("test".to_string());
237 let debug = format!("{err:?}");
238 assert!(debug.contains("AuthenticationFailed"));
239 }
240}