mdk_sqlite_storage/
error.rs1#[derive(Debug, thiserror::Error)]
5pub enum Error {
6 #[error("Database error: {0}")]
8 Database(String),
9 #[error("SQLite error: {0}")]
11 Rusqlite(#[from] rusqlite::Error),
12 #[error("Migration error: {0}")]
14 Refinery(#[from] refinery::Error),
15 #[error("OpenMLS error: {0}")]
17 OpenMls(String),
18 #[error("{field_name} exceeds maximum length of {max_size} bytes (got {actual_size} bytes)")]
20 Validation {
21 field_name: String,
23 max_size: usize,
25 actual_size: usize,
27 },
28
29 #[error("Invalid encryption key length: expected 32 bytes, got {0} bytes")]
32 InvalidKeyLength(usize),
33
34 #[error("Wrong encryption key: database cannot be decrypted with the provided key")]
36 WrongEncryptionKey,
37
38 #[error(
40 "Cannot open unencrypted database with encryption: database was created without encryption"
41 )]
42 UnencryptedDatabaseWithEncryption,
43
44 #[error("Failed to generate encryption key: {0}")]
46 KeyGeneration(String),
47
48 #[error("File permission error: {0}")]
50 FilePermission(String),
51
52 #[error("Keyring error: {0}")]
55 Keyring(String),
56
57 #[error(
63 "Keyring store not initialized. The host application must call keyring_core::set_default_store() with a platform-specific store before using encrypted storage. Details: {0}"
64 )]
65 KeyringNotInitialized(String),
66
67 #[error(
74 "Database exists at '{db_path}' but no encryption key found in keyring (service='{service_id}', key='{db_key_id}'). The database cannot be opened without the original encryption key. If the key was lost, the database data is unrecoverable."
75 )]
76 KeyringEntryMissingForExistingDatabase {
77 db_path: String,
79 service_id: String,
81 db_key_id: String,
83 },
84}
85
86impl From<std::io::Error> for Error {
87 fn from(e: std::io::Error) -> Self {
88 Self::Database(format!("IO error: {}", e))
89 }
90}
91
92impl From<Error> for rusqlite::Error {
93 fn from(err: Error) -> Self {
94 rusqlite::Error::FromSqlConversionFailure(
95 0,
96 rusqlite::types::Type::Text,
97 Box::new(std::io::Error::new(
98 std::io::ErrorKind::InvalidData,
99 err.to_string(),
100 )),
101 )
102 }
103}
104
105#[cfg(test)]
106mod tests {
107 use super::*;
108
109 #[test]
110 fn test_error_display_database() {
111 let err = Error::Database("connection failed".to_string());
112 assert!(err.to_string().contains("connection failed"));
113 assert!(err.to_string().contains("Database error"));
114 }
115
116 #[test]
117 fn test_error_display_openmls() {
118 let err = Error::OpenMls("key generation failed".to_string());
119 assert_eq!(err.to_string(), "OpenMLS error: key generation failed");
120 }
121
122 #[test]
123 fn test_error_display_invalid_key_length() {
124 let err = Error::InvalidKeyLength(16);
125 let msg = err.to_string();
126 assert!(msg.contains("16"));
127 assert!(msg.contains("32"));
128 }
129
130 #[test]
131 fn test_error_display_wrong_encryption_key() {
132 let err = Error::WrongEncryptionKey;
133 let msg = err.to_string();
134 assert!(msg.contains("Wrong encryption key"));
135 }
136
137 #[test]
138 fn test_error_display_unencrypted_database_with_encryption() {
139 let err = Error::UnencryptedDatabaseWithEncryption;
140 let msg = err.to_string();
141 assert!(msg.contains("unencrypted database"));
142 }
143
144 #[test]
145 fn test_error_display_key_generation() {
146 let err = Error::KeyGeneration("entropy failure".to_string());
147 let msg = err.to_string();
148 assert!(msg.contains("entropy failure"));
149 assert!(msg.contains("generate encryption key"));
150 }
151
152 #[test]
153 fn test_error_display_file_permission() {
154 let err = Error::FilePermission("access denied".to_string());
155 let msg = err.to_string();
156 assert!(msg.contains("access denied"));
157 assert!(msg.contains("permission"));
158 }
159
160 #[test]
161 fn test_error_display_keyring() {
162 let err = Error::Keyring("keychain locked".to_string());
163 let msg = err.to_string();
164 assert!(msg.contains("keychain locked"));
165 }
166
167 #[test]
168 fn test_error_display_keyring_not_initialized() {
169 let err = Error::KeyringNotInitialized("no store configured".to_string());
170 let msg = err.to_string();
171 assert!(msg.contains("not initialized"));
172 assert!(msg.contains("set_default_store"));
173 }
174
175 #[test]
176 fn test_error_display_validation() {
177 let err = Error::Validation {
178 field_name: "name".to_string(),
179 max_size: 100,
180 actual_size: 150,
181 };
182 let msg = err.to_string();
183 assert!(msg.contains("name"));
184 assert!(msg.contains("100"));
185 assert!(msg.contains("150"));
186 }
187
188 #[test]
189 fn test_error_debug() {
190 let err = Error::Database("test".to_string());
191 let debug_str = format!("{:?}", err);
192 assert!(debug_str.contains("Database"));
193 assert!(debug_str.contains("test"));
194 }
195
196 #[test]
197 fn test_error_from_io_error() {
198 let io_err = std::io::Error::new(std::io::ErrorKind::NotFound, "file not found");
199 let err: Error = io_err.into();
200 match err {
201 Error::Database(msg) => assert!(msg.contains("file not found")),
202 _ => panic!("Expected Database error variant"),
203 }
204 }
205
206 #[test]
207 fn test_error_from_rusqlite_error() {
208 let rusqlite_err = rusqlite::Error::InvalidQuery;
209 let err: Error = rusqlite_err.into();
210
211 match err {
212 Error::Rusqlite(_) => {}
213 _ => panic!("Expected Rusqlite variant"),
214 }
215 }
216
217 #[test]
218 fn test_error_into_rusqlite_error() {
219 let err = Error::WrongEncryptionKey;
220 let rusqlite_err: rusqlite::Error = err.into();
221 let msg = rusqlite_err.to_string();
223 assert!(!msg.is_empty());
224 }
225
226 #[test]
227 fn test_error_to_rusqlite_error() {
228 let err = Error::Database("test error".to_string());
229 let rusqlite_err: rusqlite::Error = err.into();
230
231 match rusqlite_err {
232 rusqlite::Error::FromSqlConversionFailure(_, _, _) => {}
233 _ => panic!("Expected FromSqlConversionFailure variant"),
234 }
235 }
236
237 #[test]
238 fn test_error_debug_format() {
239 let err = Error::InvalidKeyLength(24);
240 let debug_str = format!("{:?}", err);
241 assert!(debug_str.contains("InvalidKeyLength"));
242 assert!(debug_str.contains("24"));
243 }
244
245 #[test]
246 fn test_error_display_keyring_entry_missing_for_existing_database() {
247 let err = Error::KeyringEntryMissingForExistingDatabase {
248 db_path: "/path/to/db.sqlite".to_string(),
249 service_id: "com.example.app".to_string(),
250 db_key_id: "mdk.db.key".to_string(),
251 };
252 let msg = err.to_string();
253 assert!(msg.contains("/path/to/db.sqlite"));
254 assert!(msg.contains("com.example.app"));
255 assert!(msg.contains("mdk.db.key"));
256 assert!(msg.contains("no encryption key found"));
257 assert!(msg.contains("unrecoverable"));
258 }
259
260 #[test]
261 fn test_validation_error_fields() {
262 let err = Error::Validation {
263 field_name: "description".to_string(),
264 max_size: 1024,
265 actual_size: 2048,
266 };
267
268 if let Error::Validation {
269 field_name,
270 max_size,
271 actual_size,
272 } = err
273 {
274 assert_eq!(field_name, "description");
275 assert_eq!(max_size, 1024);
276 assert_eq!(actual_size, 2048);
277 } else {
278 panic!("Expected Validation variant");
279 }
280 }
281}