#![allow(clippy::cast_possible_wrap)]
use crate::column_store::{ColumnStore, ColumnType, ColumnValue, VacuumConfig};
fn store_with_id_rows(count: usize) -> ColumnStore {
let mut store = ColumnStore::with_schema(&[("id", ColumnType::Int)]);
for i in 0..count {
store.push_row(&[("id", ColumnValue::Int(i as i64))]);
}
store
}
#[test]
fn vacuum_removes_tombstones() {
let mut store = store_with_id_rows(10);
store.deleted_rows.insert(3);
store.deleted_rows.insert(7);
if let Ok(idx) = u32::try_from(3_usize) {
store.deletion_bitmap.insert(idx);
}
if let Ok(idx) = u32::try_from(7_usize) {
store.deletion_bitmap.insert(idx);
}
let stats = store.vacuum(VacuumConfig::default());
assert!(stats.completed);
assert_eq!(stats.tombstones_found, 2);
assert_eq!(stats.tombstones_removed, 2);
assert_eq!(store.row_count(), 8);
assert_eq!(store.deleted_row_count(), 0);
}
#[test]
fn vacuum_no_tombstones_is_noop() {
let mut store = store_with_id_rows(5);
let stats = store.vacuum(VacuumConfig::default());
assert!(stats.completed);
assert_eq!(stats.tombstones_found, 0);
assert_eq!(store.row_count(), 5);
}
#[test]
fn vacuum_preserves_remaining_data() {
let mut store = store_with_id_rows(5);
store.deleted_rows.insert(2);
if let Ok(idx) = u32::try_from(2_usize) {
store.deletion_bitmap.insert(idx);
}
store.vacuum(VacuumConfig::default());
let ids = store.filter_eq_int("id", 2);
assert!(ids.is_empty(), "deleted id=2 must not appear after vacuum");
let all_rows = store.row_count();
assert_eq!(all_rows, 4);
}
#[test]
fn should_vacuum_below_threshold() {
let mut store = store_with_id_rows(100);
for i in 0..10 {
store.deleted_rows.insert(i);
}
assert!(!store.should_vacuum(0.20));
}
#[test]
fn should_vacuum_at_threshold() {
let mut store = store_with_id_rows(100);
for i in 0..20 {
store.deleted_rows.insert(i);
}
assert!(store.should_vacuum(0.20));
}
#[test]
fn should_vacuum_above_threshold() {
let mut store = store_with_id_rows(100);
for i in 0..50 {
store.deleted_rows.insert(i);
}
assert!(store.should_vacuum(0.20));
}
#[test]
fn should_vacuum_empty_store() {
let store = ColumnStore::new();
assert!(!store.should_vacuum(0.20));
}
#[test]
fn is_row_deleted_bitmap_true_for_deleted() {
let mut store = store_with_id_rows(5);
store.deleted_rows.insert(2);
store.deletion_bitmap.insert(2);
assert!(store.is_row_deleted_bitmap(2));
}
#[test]
fn is_row_deleted_bitmap_false_for_live() {
let store = store_with_id_rows(5);
assert!(!store.is_row_deleted_bitmap(0));
assert!(!store.is_row_deleted_bitmap(4));
}
#[test]
fn live_row_indices_excludes_deleted() {
let mut store = store_with_id_rows(5);
store.deleted_rows.insert(1);
store.deletion_bitmap.insert(1);
store.deleted_rows.insert(3);
store.deletion_bitmap.insert(3);
let live: Vec<usize> = store.live_row_indices().collect();
assert_eq!(live, vec![0, 2, 4]);
}
#[test]
fn deleted_count_bitmap_matches_set() {
let mut store = store_with_id_rows(10);
for i in [0, 2, 4, 6] {
store.deleted_rows.insert(i);
if let Ok(idx) = u32::try_from(i) {
store.deletion_bitmap.insert(idx);
}
}
assert_eq!(store.deleted_count_bitmap(), 4);
}