use std::collections::BTreeSet;
use crate::{BtCursor, BtreeCursorOps, MemPageStore};
use fsqlite_types::PageNumber;
use fsqlite_types::cx::Cx;
fn key_for(i: u32) -> Vec<u8> {
let mut key = vec![0u8; 100];
key[0..4].copy_from_slice(&i.to_be_bytes());
key
}
fn key_prefix_u32(key: &[u8]) -> u32 {
let mut buf = [0u8; 4];
buf.copy_from_slice(&key[0..4]);
u32::from_be_bytes(buf)
}
#[test]
fn test_index_delete_interior() {
let cx = Cx::new();
let root = PageNumber::new(2).unwrap();
let store = MemPageStore::with_empty_index(root, 512);
let mut cursor = BtCursor::new(store, root, 512, false);
let mut expected = BTreeSet::new();
for i in 0..100_u32 {
let key = key_for(i);
cursor.index_insert(&cx, &key).unwrap();
expected.insert(i);
}
for i in 0..100_u32 {
let key = key_for(i);
let seek = cursor.index_move_to(&cx, &key).unwrap();
assert!(seek.is_found());
cursor.delete(&cx).unwrap();
assert!(
expected.remove(&i),
"deleted key must exist in expected set"
);
if let Some(next_expected) = expected.iter().next().copied() {
assert!(
!cursor.eof(),
"cursor should be positioned at successor after delete"
);
let next_key = cursor.payload(&cx).unwrap();
assert_eq!(
key_prefix_u32(&next_key),
next_expected,
"cursor should point at immediate successor",
);
} else {
assert!(cursor.eof(), "cursor should be EOF after final delete");
}
}
assert!(
!cursor.first(&cx).unwrap(),
"first() on empty tree should be false"
);
assert!(cursor.eof(), "cursor must remain at EOF for empty tree");
}
#[test]
fn test_index_delete_interior_needs_rebalance() {
let cx = Cx::new();
let root = PageNumber::new(2).unwrap();
let store = MemPageStore::with_empty_index(root, 512);
let mut cursor = BtCursor::new(store, root, 512, false);
for i in 0..100_u32 {
let mut key = vec![0u8; if i % 2 == 0 { 10 } else { 200 }];
key[0..4].copy_from_slice(&i.to_be_bytes());
cursor.index_insert(&cx, &key).unwrap();
}
for i in 0..100_u32 {
let mut key = vec![0u8; if i % 2 == 0 { 10 } else { 200 }];
key[0..4].copy_from_slice(&i.to_be_bytes());
if cursor.index_move_to(&cx, &key).unwrap().is_found() {
cursor.delete(&cx).unwrap();
}
}
}