1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
#![cfg(target_arch = "wasm32")]
use absurder_sql::{Database, DatabaseConfig};
use wasm_bindgen_test::*;
wasm_bindgen_test_configure!(run_in_browser);
/// Test that leader election gracefully handles localStorage unavailable
/// This simulates private browsing mode in Safari/Firefox where localStorage returns None
#[wasm_bindgen_test]
async fn test_leader_election_without_localstorage() {
// Note: This test will currently PANIC due to .unwrap().unwrap() in leader_election.rs
// After fix, it should return a proper error
let config = DatabaseConfig {
name: "test_private_browsing.db".to_string(),
..Default::default()
};
// In real private browsing, window.local_storage() returns Ok(None)
// This will trigger the double unwrap panic in leader_election.rs line 90
let result = Database::new(config).await;
// After fix, this should be an error, not a panic
match result {
Ok(_) => {
// If it succeeds, multi-tab features should be disabled
log::info!("Database created successfully - multi-tab features may be disabled");
}
Err(e) => {
// Expected: graceful error about localStorage unavailable
let error_msg = format!("{:?}", e);
assert!(
error_msg.contains("localStorage")
|| error_msg.contains("STORAGE_ERROR")
|| error_msg.contains("private browsing"),
"Error should mention localStorage unavailability: {}",
error_msg
);
log::info!(
"Correctly returned error for localStorage unavailable: {:?}",
e
);
}
}
}
/// Test that sync operations handle IndexedDB unavailable gracefully
/// This simulates private browsing or when user has disabled IndexedDB
#[wasm_bindgen_test]
async fn test_sync_without_indexeddb() {
// Note: This test will currently PANIC due to .unwrap().unwrap() in sync_operations.rs
// After fix, it should return a proper error
let config = DatabaseConfig {
name: "test_no_indexeddb.db".to_string(),
..Default::default()
};
let db = Database::new(config).await;
if let Ok(mut database) = db {
// Try to create a table and sync
let create_result = database.execute("CREATE TABLE test (id INT)").await;
if let Ok(_) = create_result {
// Try to sync - this is where IndexedDB unavailability will be hit
let sync_result = database.sync().await;
// After fix, this should return an error, not panic
match sync_result {
Ok(_) => {
log::info!("Sync succeeded or gracefully skipped");
}
Err(e) => {
// Expected: graceful error about IndexedDB unavailable
let error_msg = format!("{:?}", e);
assert!(
error_msg.contains("IndexedDB")
|| error_msg.contains("INDEXEDDB_ERROR")
|| error_msg.contains("private browsing"),
"Error should mention IndexedDB unavailability: {}",
error_msg
);
log::info!(
"Correctly returned error for IndexedDB unavailable: {:?}",
e
);
}
}
}
}
}
/// Test that window() access in async contexts is handled safely
#[wasm_bindgen_test]
async fn test_window_access_safety() {
// This tests the window().unwrap() pattern in spawn_local contexts
// While less likely to fail, it should still be handled gracefully
let config = DatabaseConfig {
name: "test_window_safety.db".to_string(),
..Default::default()
};
let result = Database::new(config).await;
// Should not panic even if window is unavailable in some contexts
assert!(
result.is_ok() || result.is_err(),
"Should return Ok or Err, not panic"
);
}
/// Test graceful degradation message for users
#[wasm_bindgen_test]
async fn test_private_browsing_user_friendly_error() {
let config = DatabaseConfig {
name: "test_user_message.db".to_string(),
..Default::default()
};
let result = Database::new(config).await;
if let Err(e) = result {
let error_str = format!("{:?}", e);
// Error message should be user-friendly, not cryptic
// Should NOT be: "unreachable" or "__rust_start_panic"
assert!(
!error_str.contains("unreachable") && !error_str.contains("rust_panic"),
"Error should be user-friendly, not a panic message: {}",
error_str
);
// Should mention one of these user-understandable terms
let is_user_friendly = error_str.contains("private browsing")
|| error_str.contains("storage unavailable")
|| error_str.contains("localStorage")
|| error_str.contains("IndexedDB")
|| error_str.contains("disabled")
|| error_str.contains("not available");
assert!(
is_user_friendly,
"Error should mention what's unavailable in user-friendly terms: {}",
error_str
);
}
}
/// Test that leader election handles localStorage.getItem errors
#[wasm_bindgen_test]
async fn test_localstorage_getitem_error_handling() {
// localStorage.getItem() can fail with Err in restricted environments
// Should handle gracefully, not panic
let config = DatabaseConfig {
name: "test_getitem_error.db".to_string(),
..Default::default()
};
// This will exercise the localStorage access in leader election
let result = Database::new(config).await;
// Should handle any localStorage errors gracefully
match result {
Ok(_) => log::info!("Database created successfully"),
Err(e) => {
let error_msg = format!("{:?}", e);
// Should be a proper error, not a panic
assert!(
!error_msg.contains("unwrap"),
"Should not expose internal unwrap errors: {}",
error_msg
);
}
}
}
/// Test multiple database instances handle storage errors
#[wasm_bindgen_test]
async fn test_multiple_instances_with_storage_errors() {
// Multiple instances when storage is unavailable
// Should all fail gracefully, not panic
for i in 0..3 {
let config = DatabaseConfig {
name: format!("test_concurrent_{}.db", i),
..Default::default()
};
let result = Database::new(config).await;
// Each should handle errors gracefully
match result {
Ok(_) => log::info!("Instance {} created successfully", i),
Err(e) => log::info!("Instance {} got expected error: {:?}", i, e),
}
}
log::info!("All database creation attempts completed without panic");
}