#[cfg(target_arch = "wasm32")]
use wasm_bindgen_test::*;
#[cfg(target_arch = "wasm32")]
wasm_bindgen_test_configure!(run_in_browser);
#[cfg(target_arch = "wasm32")]
use absurder_sql::storage::vfs_sync::{with_global_commit_marker, with_global_storage};
#[cfg(target_arch = "wasm32")]
use absurder_sql::vfs::indexeddb_vfs::IndexedDBVFS;
#[cfg(target_arch = "wasm32")]
use absurder_sql::{ColumnValue, Database};
#[cfg(target_arch = "wasm32")]
#[wasm_bindgen_test]
async fn test_writes_are_buffered_during_transaction() {
web_sys::console::log_1(&"=== TEST: Writes are buffered during transaction ===".into());
let db_name = "write_buffer_test.db";
with_global_storage(|gs| gs.borrow_mut().clear());
with_global_commit_marker(|cm| cm.borrow_mut().clear());
let vfs = IndexedDBVFS::new(db_name).await.expect("Should create VFS");
let vfs_name = "indexeddb";
vfs.register(vfs_name).expect("Should register VFS");
let mut db = Database::open_with_vfs(db_name, vfs_name)
.await
.expect("Should open database");
db.execute_internal("CREATE TABLE test (id INTEGER PRIMARY KEY, value TEXT)")
.await
.expect("Should create table");
db.execute_internal("BEGIN TRANSACTION")
.await
.expect("Should begin transaction");
let initial_block_count = with_global_storage(|gs| {
gs.borrow()
.values()
.map(|blocks| blocks.len())
.sum::<usize>()
});
web_sys::console::log_1(&format!("Initial block count: {}", initial_block_count).into());
for i in 1..=10 {
db.execute_internal(&format!(
"INSERT INTO test (id, value) VALUES ({}, 'value_{}')",
i, i
))
.await
.expect(&format!("Should insert row {}", i));
}
let during_tx_block_count = with_global_storage(|gs| {
gs.borrow()
.values()
.map(|blocks| blocks.len())
.sum::<usize>()
});
web_sys::console::log_1(
&format!("Block count during transaction: {}", during_tx_block_count).into(),
);
let blocks_added_during_tx = during_tx_block_count - initial_block_count;
web_sys::console::log_1(
&format!(
"Blocks added during transaction: {}",
blocks_added_during_tx
)
.into(),
);
assert!(
blocks_added_during_tx < 5,
"Expected < 5 blocks written during transaction (buffered), but got {}. \
This indicates writes are NOT being buffered!",
blocks_added_during_tx
);
db.execute_internal("COMMIT")
.await
.expect("Should commit transaction");
let after_commit_block_count = with_global_storage(|gs| {
gs.borrow()
.values()
.map(|blocks| blocks.len())
.sum::<usize>()
});
web_sys::console::log_1(
&format!("Block count after commit: {}", after_commit_block_count).into(),
);
web_sys::console::log_1(
&"SUCCESS: Writes were buffered during transaction and flushed on commit!".into(),
);
let result = db
.execute_internal("SELECT COUNT(*) FROM test")
.await
.expect("Should count rows");
let rows = result.rows;
assert_eq!(rows.len(), 1, "Should have one result row");
if let ColumnValue::Integer(count) = &rows[0].values[0] {
assert_eq!(*count, 10, "Should have 10 rows inserted");
} else {
panic!("Expected integer count");
}
web_sys::console::log_1(&"TEST PASSED: Writes are buffered during transaction".into());
}
#[cfg(target_arch = "wasm32")]
#[wasm_bindgen_test]
async fn test_batch_commit_performance() {
web_sys::console::log_1(&"=== TEST: Batch commit performance - Finding our limits ===".into());
let db_name = "batch_perf_test.db";
with_global_storage(|gs| gs.borrow_mut().clear());
with_global_commit_marker(|cm| cm.borrow_mut().clear());
let vfs = IndexedDBVFS::new(db_name).await.expect("Should create VFS");
let vfs_name = "indexeddb";
vfs.register(vfs_name).expect("Should register VFS");
let mut db = Database::open_with_vfs(db_name, vfs_name)
.await
.expect("Should open database");
db.execute_internal("CREATE TABLE perf_test (id INTEGER PRIMARY KEY, value TEXT)")
.await
.expect("Should create table");
let batch_sizes = vec![10, 50, 100, 500, 1000, 5000];
let mut results = Vec::new();
web_sys::console::log_1(&"\nPERFORMANCE SCALING TEST:".into());
web_sys::console::log_1(&"─────────────────────────────────────────────────────".into());
for &batch_size in &batch_sizes {
let start = js_sys::Date::now();
db.execute_internal("BEGIN TRANSACTION")
.await
.expect("Should begin transaction");
for i in 1..=batch_size {
db.execute_internal(&format!(
"INSERT INTO perf_test (id, value) VALUES ({}, 'value_{}')",
i, i
))
.await
.expect(&format!("Should insert row {}", i));
}
db.execute_internal("COMMIT")
.await
.expect("Should commit transaction");
let batch_time = js_sys::Date::now() - start;
let per_insert_ms = batch_time / batch_size as f64;
results.push((batch_size, batch_time, per_insert_ms));
web_sys::console::log_1(
&format!(
" {:>5} inserts: {:>8.2}ms total | {:>6.3}ms per insert | {:>6.0} inserts/sec",
batch_size,
batch_time,
per_insert_ms,
1000.0 / per_insert_ms
)
.into(),
);
db.execute_internal(&format!("DELETE FROM perf_test WHERE id <= {}", batch_size))
.await
.expect("Should delete rows");
}
web_sys::console::log_1(&"─────────────────────────────────────────────────────".into());
let best = results
.iter()
.min_by(|a, b| a.2.partial_cmp(&b.2).unwrap())
.unwrap();
web_sys::console::log_1(
&format!(
"\n🏆 BEST: {:.3}ms per insert at {} batch size ({:.0} inserts/sec)",
best.2,
best.0,
1000.0 / best.2
)
.into(),
);
let absurd_sql_ms = 5.9;
let speedup = absurd_sql_ms / best.2;
web_sys::console::log_1(&format!("vs absurd-sql (5.9ms): {}x FASTER\n", speedup as i32).into());
assert!(
best.2 < absurd_sql_ms,
"Expected to beat absurd-sql (5.9ms), but best was {:.3}ms",
best.2
);
}
#[cfg(target_arch = "wasm32")]
#[wasm_bindgen_test]
async fn test_no_borrow_panic_during_buffered_write() {
web_sys::console::log_1(&"=== TEST: No borrow panic during buffered write ===".into());
let db_name = "borrow_panic_test.db";
with_global_storage(|gs| gs.borrow_mut().clear());
with_global_commit_marker(|cm| cm.borrow_mut().clear());
let vfs = IndexedDBVFS::new(db_name).await.expect("Should create VFS");
let vfs_name = "indexeddb";
vfs.register(vfs_name).expect("Should register VFS");
let mut db = Database::open_with_vfs(db_name, vfs_name)
.await
.expect("Should open database");
db.execute_internal("CREATE TABLE test (id INTEGER PRIMARY KEY, value TEXT)")
.await
.expect("Should create table");
db.execute_internal("BEGIN TRANSACTION")
.await
.expect("Should begin transaction");
db.execute_internal("INSERT INTO test (id, value) VALUES (1, 'test')")
.await
.expect("Should insert without borrow panic");
db.execute_internal("COMMIT").await.expect("Should commit");
let result = db
.execute_internal("SELECT COUNT(*) FROM test")
.await
.expect("Should count rows");
let rows = result.rows;
if let ColumnValue::Integer(count) = &rows[0].values[0] {
assert_eq!(*count, 1, "Should have 1 row");
} else {
panic!("Expected integer count");
}
web_sys::console::log_1(&"TEST PASSED: No borrow panic during buffered write".into());
}
#[cfg(target_arch = "wasm32")]
#[wasm_bindgen_test]
async fn test_rollback_discards_buffered_writes() {
web_sys::console::log_1(&"=== TEST: Rollback discards buffered writes ===".into());
let db_name = "rollback_test.db";
with_global_storage(|gs| gs.borrow_mut().clear());
with_global_commit_marker(|cm| cm.borrow_mut().clear());
let vfs = IndexedDBVFS::new(db_name).await.expect("Should create VFS");
let vfs_name = "indexeddb";
vfs.register(vfs_name).expect("Should register VFS");
let mut db = Database::open_with_vfs(db_name, vfs_name)
.await
.expect("Should open database");
db.execute_internal("CREATE TABLE rollback_test (id INTEGER PRIMARY KEY, value TEXT)")
.await
.expect("Should create table");
db.execute_internal("INSERT INTO rollback_test (id, value) VALUES (1, 'initial')")
.await
.expect("Should insert initial row");
db.execute_internal("BEGIN TRANSACTION")
.await
.expect("Should begin transaction");
for i in 2..=5 {
db.execute_internal(&format!(
"INSERT INTO rollback_test (id, value) VALUES ({}, 'rollback_{}')",
i, i
))
.await
.expect(&format!("Should insert row {}", i));
}
db.execute_internal("ROLLBACK")
.await
.expect("Should rollback transaction");
let result = db
.execute_internal("SELECT COUNT(*) FROM rollback_test")
.await
.expect("Should count rows");
let rows = result.rows;
if let ColumnValue::Integer(count) = &rows[0].values[0] {
assert_eq!(
*count, 1,
"Should only have initial row after rollback, buffered writes should be discarded"
);
} else {
panic!("Expected integer count");
}
web_sys::console::log_1(&"TEST PASSED: Rollback discards buffered writes".into());
}
#[cfg(target_arch = "wasm32")]
#[wasm_bindgen_test]
async fn test_nested_transactions_with_buffering() {
web_sys::console::log_1(&"=== TEST: Nested transactions with buffering ===".into());
let db_name = "nested_tx_test.db";
with_global_storage(|gs| gs.borrow_mut().clear());
with_global_commit_marker(|cm| cm.borrow_mut().clear());
let vfs = IndexedDBVFS::new(db_name).await.expect("Should create VFS");
let vfs_name = "indexeddb";
vfs.register(vfs_name).expect("Should register VFS");
let mut db = Database::open_with_vfs(db_name, vfs_name)
.await
.expect("Should open database");
db.execute_internal("CREATE TABLE nested_test (id INTEGER PRIMARY KEY, value TEXT)")
.await
.expect("Should create table");
db.execute_internal("BEGIN TRANSACTION")
.await
.expect("Should begin outer transaction");
db.execute_internal("INSERT INTO nested_test (id, value) VALUES (1, 'outer')")
.await
.expect("Should insert in outer transaction");
db.execute_internal("SAVEPOINT inner")
.await
.expect("Should create savepoint");
db.execute_internal("INSERT INTO nested_test (id, value) VALUES (2, 'inner')")
.await
.expect("Should insert in savepoint");
db.execute_internal("ROLLBACK TO inner")
.await
.expect("Should rollback to savepoint");
db.execute_internal("COMMIT")
.await
.expect("Should commit outer transaction");
let result = db
.execute_internal("SELECT COUNT(*) FROM nested_test")
.await
.expect("Should count rows");
let rows = result.rows;
if let ColumnValue::Integer(count) = &rows[0].values[0] {
assert_eq!(*count, 1, "Should only have outer transaction row");
} else {
panic!("Expected integer count");
}
web_sys::console::log_1(
&"TEST PASSED: Nested transactions work correctly with buffering".into(),
);
}