#![cfg(feature = "fs_persist")]
use absurder_sql::storage::BlockStorage;
use serial_test::serial;
use std::fs;
use std::path::PathBuf;
use tempfile::TempDir;
#[path = "common/mod.rs"]
mod common;
fn make_bytes(val: u8) -> Vec<u8> {
vec![val; 4096]
}
#[tokio::test]
#[serial]
async fn fs_persist_metadata_and_data_across_instances() {
let tmp = TempDir::new().expect("tempdir");
common::set_var("ABSURDERSQL_FS_BASE", tmp.path());
let mut s1 = BlockStorage::new("fs_meta_data_test")
.await
.expect("create s1");
let b1 = s1.allocate_block().await.expect("alloc1");
let d1 = make_bytes(7);
s1.write_block(b1, d1.clone()).await.expect("write1");
s1.sync().await.expect("sync1");
let mut base: PathBuf = tmp.path().into();
base.push("fs_meta_data_test");
let mut blocks_dir = base.clone();
blocks_dir.push("blocks");
let mut block_path = blocks_dir.clone();
block_path.push(format!("block_{}.bin", b1));
let mut meta_path = base.clone();
meta_path.push("metadata.json");
assert!(
fs::metadata(&blocks_dir).is_ok(),
"blocks dir should exist: {:?}",
blocks_dir
);
assert!(
fs::metadata(&block_path).is_ok(),
"block file should exist: {:?}",
block_path
);
assert!(
fs::metadata(&meta_path).is_ok(),
"metadata file should exist: {:?}",
meta_path
);
drop(s1);
let mut s2 = BlockStorage::new("fs_meta_data_test")
.await
.expect("create s2");
let read_back = s2.read_block(b1).await.expect("read back");
assert_eq!(read_back, d1, "block data should persist across instances");
let meta2 = s2.get_block_metadata_for_testing();
let (checksum2, version2, last_ms2) =
meta2.get(&b1).copied().expect("meta exists after reload");
assert!(
version2 >= 1,
"version should be at least 1 after first sync"
);
assert!(last_ms2 > 0, "have last_modified_ms");
let expected_checksum = s2.get_block_checksum(b1).expect("checksum present");
assert_eq!(
checksum2 as u32, expected_checksum,
"checksum should match computed"
);
s2.write_block(b1, d1.clone())
.await
.expect("rewrite same-data");
s2.sync().await.expect("sync2");
drop(s2);
let mut s3 = BlockStorage::new("fs_meta_data_test")
.await
.expect("create s3");
let meta3 = s3.get_block_metadata_for_testing();
let (_, version3, _) = meta3.get(&b1).copied().expect("meta still exists");
assert!(
version3 > version2,
"version should bump across instances after second sync"
);
}
#[tokio::test]
#[serial]
async fn fs_persist_deallocate_removes_data_and_metadata() {
let tmp = TempDir::new().expect("tempdir");
common::set_var("ABSURDERSQL_FS_BASE", tmp.path());
let mut s1 = BlockStorage::new("fs_dealloc_test")
.await
.expect("create s1");
let b1 = s1.allocate_block().await.expect("alloc1");
s1.write_block(b1, make_bytes(9)).await.expect("write1");
s1.sync().await.expect("sync1");
drop(s1);
let mut s2 = BlockStorage::new("fs_dealloc_test")
.await
.expect("create s2");
s2.deallocate_block(b1).await.expect("dealloc");
s2.sync().await.expect("sync2");
let mut base: PathBuf = tmp.path().into();
base.push("fs_dealloc_test");
let mut blocks_dir = base.clone();
blocks_dir.push("blocks");
let mut block_path = blocks_dir.clone();
block_path.push(format!("block_{}.bin", b1));
let mut meta_path = base.clone();
meta_path.push("metadata.json");
assert!(
fs::metadata(&meta_path).is_ok(),
"metadata file should exist after dealloc: {:?}",
meta_path
);
assert!(
fs::metadata(&block_path).is_err(),
"block file should be removed after dealloc: {:?}",
block_path
);
drop(s2);
let mut s3 = BlockStorage::new("fs_dealloc_test")
.await
.expect("create s3");
let meta3 = s3.get_block_metadata_for_testing();
assert!(
!meta3.contains_key(&b1),
"metadata removed after deallocation"
);
let read_err = s3.read_block(b1).await;
assert!(read_err.is_err(), "reading deallocated block should error");
}