#[cfg(test)]
mod tests {
use crate::wal::Wal;
use std::fs;
use tempfile::TempDir;
#[test]
fn rapid_successive_rotations() {
let tmp = TempDir::new().unwrap();
let path = tmp.path().join("000000.log");
let mut wal: Wal<u64> = Wal::open(&path, None).unwrap();
let rotations = 20;
let records_per_segment = 5;
for _ in 0..=rotations {
for j in 0..records_per_segment {
wal.append(&(j as u64)).unwrap();
}
if wal.wal_seq() < rotations as u64 {
wal.rotate_next().unwrap();
}
}
let mut files: Vec<String> = fs::read_dir(tmp.path())
.unwrap()
.filter_map(|e| {
let name = e.ok()?.file_name().to_string_lossy().to_string();
if name.ends_with(".log") {
Some(name)
} else {
None
}
})
.collect();
files.sort();
assert_eq!(
files.len(),
rotations + 1,
"Expected {} WAL files",
rotations + 1
);
for (i, f) in files.iter().enumerate() {
assert_eq!(f, &format!("{i:06}.log"), "Unexpected WAL filename");
}
let mut total = 0;
for seq in 0..=rotations {
let p = tmp.path().join(format!("{seq:06}.log"));
let reader: Wal<u64> = Wal::open(&p, None).unwrap();
for record in reader.replay_iter().unwrap() {
record.unwrap();
total += 1;
}
}
assert_eq!(
total,
(rotations + 1) * records_per_segment,
"Expected {} total records",
(rotations + 1) * records_per_segment
);
}
#[test]
fn append_works_after_rotation() {
let tmp = TempDir::new().unwrap();
let path = tmp.path().join("000000.log");
let mut wal: Wal<u64> = Wal::open(&path, None).unwrap();
for i in 0..3u64 {
wal.append(&i).unwrap();
}
let new_seq = wal.rotate_next().unwrap();
assert_eq!(new_seq, 1);
for i in 10..13u64 {
wal.append(&i).unwrap();
}
let new_path = tmp.path().join("000001.log");
let reader: Wal<u64> = Wal::open(&new_path, None).unwrap();
let records: Vec<u64> = reader.replay_iter().unwrap().map(|r| r.unwrap()).collect();
assert_eq!(records, vec![10, 11, 12]);
}
#[test]
fn wal_sequence_gap_segments_open_independently() {
let tmp = TempDir::new().unwrap();
let path = tmp.path().join("000000.log");
let mut wal: Wal<u64> = Wal::open(&path, None).unwrap();
for seg in 0..4u64 {
for i in 0..3 {
wal.append(&(seg * 10 + i)).unwrap();
}
if seg < 3 {
wal.rotate_next().unwrap();
}
}
let gap_path = tmp.path().join("000002.log");
assert!(gap_path.exists());
fs::remove_file(&gap_path).unwrap();
for seq in [0, 1, 3] {
let p = tmp.path().join(format!("{seq:06}.log"));
let reader: Wal<u64> = Wal::open(&p, None).unwrap();
let records: Vec<u64> = reader.replay_iter().unwrap().map(|r| r.unwrap()).collect();
assert_eq!(records.len(), 3, "Segment {seq} should have 3 records");
}
}
#[test]
fn rotation_preserves_max_record_size() {
let tmp = TempDir::new().unwrap();
let path = tmp.path().join("000000.log");
let custom_max = 512u32;
let mut wal: Wal<u64> = Wal::open(&path, Some(custom_max)).unwrap();
assert_eq!(wal.max_record_size(), custom_max);
for _ in 0..5 {
wal.rotate_next().unwrap();
assert_eq!(
wal.max_record_size(),
custom_max,
"max_record_size must be preserved across rotation"
);
}
let last_path = tmp.path().join("000005.log");
let reader: Wal<u64> = Wal::open(&last_path, None).unwrap();
assert_eq!(reader.max_record_size(), custom_max);
}
}