#![cfg(not(target_arch = "wasm32"))]
use absurder_sql::storage::{BLOCK_SIZE, BlockStorage};
use serial_test::serial;
use tempfile::TempDir;
use tokio::time::{Duration, sleep};
#[path = "common/mod.rs"]
mod common;
#[tokio::test(flavor = "current_thread")]
#[serial]
async fn test_version_and_last_modified_progression_across_syncs() {
let tmp = TempDir::new().expect("tempdir");
common::set_var("ABSURDERSQL_FS_BASE", tmp.path());
let mut storage = BlockStorage::new_with_capacity("test_meta_version_ts_progression", 4)
.await
.expect("create storage");
let d1 = vec![1u8; BLOCK_SIZE];
storage.write_block(1, d1).await.expect("write 1 d1");
let meta_pre = storage.get_block_metadata_for_testing();
assert!(
!meta_pre.contains_key(&1),
"metadata should not be persisted before sync"
);
storage.sync().await.expect("first sync");
let meta1 = storage.get_block_metadata_for_testing();
let (cs1, ver1, ts1) = meta1
.get(&1)
.copied()
.expect("metadata for block 1 after first sync");
assert!(cs1 > 0, "checksum should be non-zero after write");
assert_eq!(
ver1, 1,
"version should start at 1 after first persisted write"
);
assert!(ts1 > 0, "last_modified_ms should be set");
sleep(Duration::from_millis(5)).await;
let mut d2 = vec![0u8; BLOCK_SIZE];
d2[0] = 2;
storage.write_block(1, d2).await.expect("write 1 d2");
storage.sync().await.expect("second sync");
let meta2 = storage.get_block_metadata_for_testing();
let (_cs2, ver2, ts2) = meta2.get(&1).copied().expect("metadata after second sync");
assert_eq!(
ver2, 2,
"version should increment to 2 after second persisted write"
);
assert!(
ts2 > ts1,
"last_modified_ms should increase after subsequent syncs"
);
}
#[tokio::test(flavor = "current_thread")]
#[serial]
async fn test_metadata_persists_across_instances_with_version_retained() {
let db_name = "test_meta_version_ts_across_instances";
let tmp = TempDir::new().expect("tempdir");
common::set_var("ABSURDERSQL_FS_BASE", tmp.path());
{
let mut storage_a = BlockStorage::new_with_capacity(db_name, 4)
.await
.expect("create storage A");
let d = vec![7u8; BLOCK_SIZE];
storage_a.write_block(1, d).await.expect("write 1");
storage_a.sync().await.expect("sync A");
let meta_a = storage_a.get_block_metadata_for_testing();
let (_cs_a, ver_a, _ts_a) = meta_a.get(&1).copied().expect("metadata in A");
assert_eq!(
ver_a, 1,
"version should be 1 after first sync in instance A"
);
}
{
let mut storage_b = BlockStorage::new_with_capacity(db_name, 4)
.await
.expect("create storage B");
let meta_b1 = storage_b.get_block_metadata_for_testing();
let (_cs_b1, ver_b1, _ts_b1) = meta_b1.get(&1).copied().expect("restored metadata in B");
assert_eq!(ver_b1, 1, "restored version should be 1 in new instance");
let mut d2 = vec![0u8; BLOCK_SIZE];
d2[1] = 3;
storage_b.write_block(1, d2).await.expect("write 1 in B");
storage_b.sync().await.expect("sync B");
let meta_b2 = storage_b.get_block_metadata_for_testing();
let (_cs_b2, ver_b2, _ts_b2) = meta_b2
.get(&1)
.copied()
.expect("metadata in B after second sync");
assert_eq!(
ver_b2, 2,
"version should increment across instances after new persisted write"
);
}
}
#[tokio::test(flavor = "current_thread")]
#[serial]
async fn test_sync_without_new_writes_does_not_bump_version_or_timestamp() {
let tmp = TempDir::new().expect("tempdir");
common::set_var("ABSURDERSQL_FS_BASE", tmp.path());
let mut storage = BlockStorage::new_with_capacity("test_meta_no_bump_on_idle_sync", 4)
.await
.expect("create storage");
storage
.write_block(11, vec![2u8; BLOCK_SIZE])
.await
.expect("write 11");
storage.sync().await.expect("sync");
let meta1 = storage.get_block_metadata_for_testing();
let (_c1, v1, t1) = meta1.get(&11).copied().expect("meta after first sync");
sleep(Duration::from_millis(5)).await; storage.sync().await.expect("second sync without writes");
let meta2 = storage.get_block_metadata_for_testing();
let (_c2, v2, t2) = meta2.get(&11).copied().expect("meta after second sync");
assert_eq!(
v1, v2,
"version should not change when no new writes occurred"
);
assert_eq!(
t1, t2,
"timestamp should not change when no new writes occurred"
);
}
#[tokio::test(flavor = "current_thread")]
#[serial]
async fn test_metadata_removed_on_deallocate_persists_across_instances() {
let db_name = "test_meta_remove_on_deallocate";
let tmp = TempDir::new().expect("tempdir");
common::set_var("ABSURDERSQL_FS_BASE", tmp.path());
let dealloc_id: u64 = {
let mut a = BlockStorage::new_with_capacity(db_name, 4)
.await
.expect("create A");
let id = a.allocate_block().await.expect("allocate");
a.write_block(id, vec![3u8; BLOCK_SIZE])
.await
.expect("write");
a.sync().await.expect("sync A");
let meta_a_before = a.get_block_metadata_for_testing();
assert!(
meta_a_before.contains_key(&id),
"metadata present before deallocate"
);
a.deallocate_block(id).await.expect("deallocate");
let meta_a_after = a.get_block_metadata_for_testing();
assert!(
!meta_a_after.contains_key(&id),
"metadata should be removed after deallocate"
);
id
};
let b_meta = {
let mut b = BlockStorage::new_with_capacity(db_name, 4)
.await
.expect("create B");
b.get_block_metadata_for_testing()
};
assert!(
!b_meta.contains_key(&dealloc_id),
"metadata removal should persist across instances"
);
}
#[tokio::test(flavor = "current_thread")]
#[serial]
async fn test_same_data_write_still_bumps_version_and_timestamp() {
let tmp = TempDir::new().expect("tempdir");
common::set_var("ABSURDERSQL_FS_BASE", tmp.path());
let mut storage = BlockStorage::new_with_capacity("test_meta_same_data_bumps", 4)
.await
.expect("create storage");
let payload = vec![9u8; BLOCK_SIZE];
storage
.write_block(5, payload.clone())
.await
.expect("initial write");
storage.sync().await.expect("first sync");
let meta1 = storage.get_block_metadata_for_testing();
let (_c1, v1, t1) = meta1.get(&5).copied().expect("meta after first sync");
tokio::time::sleep(Duration::from_millis(5)).await; storage
.write_block(5, payload.clone())
.await
.expect("same data write");
storage.sync().await.expect("second sync");
let meta2 = storage.get_block_metadata_for_testing();
let (_c2, v2, t2) = meta2.get(&5).copied().expect("meta after second sync");
assert_eq!(
v2,
v1 + 1,
"version should increment even when data is unchanged"
);
assert!(t2 > t1, "last_modified_ms should update on persisted write");
}
#[tokio::test(flavor = "current_thread")]
#[serial]
async fn test_batch_write_only_updates_touched_blocks() {
let tmp = TempDir::new().expect("tempdir");
common::set_var("ABSURDERSQL_FS_BASE", tmp.path());
let mut storage = BlockStorage::new_with_capacity("test_meta_batch_updates", 4)
.await
.expect("create storage");
storage
.write_block(21, vec![1u8; BLOCK_SIZE])
.await
.expect("write 21");
storage
.write_block(22, vec![2u8; BLOCK_SIZE])
.await
.expect("write 22");
storage.sync().await.expect("first sync");
let meta1 = storage.get_block_metadata_for_testing();
let (_c21_1, v21_1, t21_1) = meta1.get(&21).copied().expect("meta 21 after first sync");
let (_c22_1, v22_1, t22_1) = meta1.get(&22).copied().expect("meta 22 after first sync");
tokio::time::sleep(Duration::from_millis(5)).await;
let mut new21 = vec![1u8; BLOCK_SIZE];
new21[0] = 3; storage.write_block(21, new21).await.expect("rewrite 21");
storage.sync().await.expect("second sync");
let meta2 = storage.get_block_metadata_for_testing();
let (_c21_2, v21_2, t21_2) = meta2.get(&21).copied().expect("meta 21 after second sync");
let (_c22_2, v22_2, t22_2) = meta2.get(&22).copied().expect("meta 22 after second sync");
assert_eq!(v21_2, v21_1 + 1, "touched block 21 should bump version");
assert!(t21_2 > t21_1, "touched block 21 should update timestamp");
assert_eq!(
v22_2, v22_1,
"untouched block 22 version should remain unchanged"
);
assert_eq!(
t22_2, t22_1,
"untouched block 22 timestamp should remain unchanged"
);
}