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