#![cfg(target_arch = "wasm32")]
use absurder_sql::storage::wasm_indexeddb::persist_to_indexeddb_event_based;
use absurder_sql::types::DatabaseError;
use absurder_sql::{Database, DatabaseConfig};
use wasm_bindgen_test::*;
wasm_bindgen_test_configure!(run_in_browser);
#[wasm_bindgen_test]
async fn test_quota_exceeded_error_detection() {
let error = DatabaseError::new("QuotaExceededError", "Storage quota has been exceeded");
assert!(
error.code.contains("Quota"),
"Should detect quota exceeded error"
);
web_sys::console::log_1(&"Quota exceeded error detection works".into());
}
#[wasm_bindgen_test]
async fn test_retry_transient_failures() {
let config = DatabaseConfig {
name: "test_retry_db.db".to_string(),
cache_size: Some(1000),
..Default::default()
};
let mut db = Database::new(config).await.expect("Should create database");
let result = db
.execute("CREATE TABLE IF NOT EXISTS test_retry (id INTEGER PRIMARY KEY, data TEXT)")
.await;
assert!(result.is_ok(), "Should succeed after retries");
db.close().await.expect("Should close");
web_sys::console::log_1(&"Retry transient failures works".into());
}
#[wasm_bindgen_test]
async fn test_exponential_backoff_timing() {
use js_sys::Date;
let start = Date::now();
let expected_delays = vec![100.0, 200.0, 400.0];
let mut actual_delays = vec![];
for (i, expected_delay) in expected_delays.iter().enumerate() {
let attempt_start = Date::now();
let promise = js_sys::Promise::new(&mut |resolve, _reject| {
web_sys::window()
.unwrap()
.set_timeout_with_callback_and_timeout_and_arguments_0(
&resolve,
*expected_delay as i32,
)
.unwrap();
});
wasm_bindgen_futures::JsFuture::from(promise).await.ok();
let actual_delay = Date::now() - attempt_start;
actual_delays.push(actual_delay);
web_sys::console::log_1(
&format!(
"Attempt {}: expected {}ms, actual {}ms",
i + 1,
expected_delay,
actual_delay
)
.into(),
);
}
let total_time = Date::now() - start;
assert!(
total_time >= 700.0,
"Should follow exponential backoff pattern"
);
web_sys::console::log_1(
&format!(
"Exponential backoff timing verified: {}ms total",
total_time
)
.into(),
);
}
#[wasm_bindgen_test]
async fn test_max_retry_attempts() {
let mut attempt_count = 0;
let max_attempts = 3;
loop {
attempt_count += 1;
web_sys::console::log_1(&format!("Attempt {}/{}", attempt_count, max_attempts).into());
if attempt_count >= max_attempts {
break;
}
let delay_ms = 100 * 2_i32.pow(attempt_count - 1);
let promise = js_sys::Promise::new(&mut |resolve, _reject| {
web_sys::window()
.unwrap()
.set_timeout_with_callback_and_timeout_and_arguments_0(&resolve, delay_ms)
.unwrap();
});
wasm_bindgen_futures::JsFuture::from(promise).await.ok();
}
assert_eq!(attempt_count, 3, "Should stop after exactly 3 attempts");
web_sys::console::log_1(&"Max retry attempts (3) enforced".into());
}
#[wasm_bindgen_test]
async fn test_quota_exceeded_no_retry() {
let error = DatabaseError::new("QuotaExceededError", "Storage quota exceeded");
let is_retriable = !error.code.contains("Quota");
assert!(
!is_retriable,
"Quota exceeded errors should NOT be retriable"
);
web_sys::console::log_1(&"Quota exceeded errors are not retried".into());
}
#[wasm_bindgen_test]
async fn test_success_on_first_attempt() {
let config = DatabaseConfig {
name: "test_first_attempt_db.db".to_string(),
cache_size: Some(1000),
..Default::default()
};
let mut db = Database::new(config).await.expect("Should create database");
let result = db.execute("SELECT 1").await;
assert!(result.is_ok(), "Should succeed on first attempt");
db.close().await.expect("Should close");
web_sys::console::log_1(&"Success on first attempt (no retries) works".into());
}
#[wasm_bindgen_test]
async fn test_transient_error_identification() {
let transient_errors = vec![
DatabaseError::new("TRANSACTION_ERROR", "Transaction failed"),
DatabaseError::new("INDEXEDDB_ERROR", "IndexedDB error"),
DatabaseError::new("NETWORK_ERROR", "Network error"),
];
let permanent_errors = vec![
DatabaseError::new("QuotaExceededError", "Quota exceeded"),
DatabaseError::new("INVALID_STATE_ERROR", "Invalid state"),
DatabaseError::new("NOT_FOUND_ERROR", "Not found"),
];
for error in transient_errors {
let is_retriable = !error.code.contains("Quota")
&& !error.code.contains("INVALID_STATE")
&& !error.code.contains("NOT_FOUND");
assert!(is_retriable, "Error {} should be retriable", error.code);
}
for error in permanent_errors {
let is_retriable = !error.code.contains("Quota")
&& !error.code.contains("INVALID_STATE")
&& !error.code.contains("NOT_FOUND");
assert!(
!is_retriable,
"Error {} should NOT be retriable",
error.code
);
}
web_sys::console::log_1(&"Transient error identification works".into());
}
#[wasm_bindgen_test]
async fn test_persist_with_retry_on_failure() {
use js_sys::Date;
let db_name = "test_persist_with_retry";
let blocks = vec![(1u64, vec![1u8, 2, 3, 4])];
let metadata = vec![(1u64, 100u64)];
let start = Date::now();
let result = persist_to_indexeddb_event_based(
db_name,
blocks,
metadata,
100,
#[cfg(feature = "telemetry")]
None,
#[cfg(feature = "telemetry")]
None,
)
.await;
let duration = Date::now() - start;
assert!(
result.is_ok(),
"Persist should succeed even with potential transient failures"
);
web_sys::console::log_1(&format!("Persist with retry completed in {}ms", duration).into());
}
#[wasm_bindgen_test]
async fn test_quota_exceeded_surfaces_immediately() {
let error = DatabaseError::new("QuotaExceededError", "Storage quota exceeded");
let should_retry = !error.code.contains("QuotaExceededError") && !error.code.contains("Quota");
assert!(!should_retry, "Quota errors should not be retried");
web_sys::console::log_1(&"Quota exceeded errors surface immediately without retry".into());
}