#![expect(clippy::unwrap_used, clippy::indexing_slicing, reason = "test code")]
use super::*;
use crate::fs::StdFs;
use test_log::test;
#[test]
fn write_then_read_roundtrips_the_bound() {
let dir = tempfile::tempdir().unwrap();
let sst = dir.path().join("7");
write(&StdFs, &sst, None, 7, b"k00130", SyncMode::Normal).unwrap();
match read(&StdFs, &sst, None).unwrap() {
SidecarRead::Present(id, bound) => {
assert_eq!(id, 7);
assert_eq!(bound, b"k00130");
}
_ => panic!("expected Present"),
}
}
#[test]
fn write_then_read_roundtrips_a_foreign_table_id() {
let dir = tempfile::tempdir().unwrap();
let sst = dir.path().join("7");
let foreign_id = 0xDEAD_BEEF_u64;
write(&StdFs, &sst, None, foreign_id, b"k00130", SyncMode::Normal).unwrap();
match read(&StdFs, &sst, None).unwrap() {
SidecarRead::Present(id, bound) => {
assert_eq!(
id, foreign_id,
"the stored id round-trips, not the file name"
);
assert_eq!(bound, b"k00130");
}
_ => panic!("expected Present"),
}
}
#[test]
fn absent_sidecar_reads_missing() {
let dir = tempfile::tempdir().unwrap();
let sst = dir.path().join("7");
assert!(matches!(
read(&StdFs, &sst, None).unwrap(),
SidecarRead::Missing
));
}
#[test]
fn corrupted_payload_reads_corrupt() {
let dir = tempfile::tempdir().unwrap();
let sst = dir.path().join("7");
write(&StdFs, &sst, None, 7, b"k00130", SyncMode::Normal).unwrap();
let path = sidecar_path(&sst);
let mut bytes = std::fs::read(&path).unwrap();
let mid = bytes.len() / 2;
bytes[mid] ^= 0xFF;
std::fs::write(&path, &bytes).unwrap();
assert!(matches!(
read(&StdFs, &sst, None).unwrap(),
SidecarRead::Corrupt
));
}
#[test]
fn truncated_sidecar_reads_corrupt() {
let dir = tempfile::tempdir().unwrap();
let sst = dir.path().join("7");
write(&StdFs, &sst, None, 7, b"k00130", SyncMode::Normal).unwrap();
let path = sidecar_path(&sst);
std::fs::write(&path, b"short").unwrap();
assert!(matches!(
read(&StdFs, &sst, None).unwrap(),
SidecarRead::Corrupt
));
}
#[test]
fn oversized_sidecar_reads_corrupt() {
let dir = tempfile::tempdir().unwrap();
let sst = dir.path().join("7");
write(&StdFs, &sst, None, 7, b"k00130", SyncMode::Normal).unwrap();
let over = HEADER_LEN + u16::MAX as usize + CHECKSUM_LEN + 1;
std::fs::write(sidecar_path(&sst), vec![0u8; over]).unwrap();
assert!(matches!(
read(&StdFs, &sst, None).unwrap(),
SidecarRead::Corrupt
));
}
#[test]
fn remove_deletes_the_sidecar() {
let dir = tempfile::tempdir().unwrap();
let sst = dir.path().join("7");
write(&StdFs, &sst, None, 7, b"k00130", SyncMode::Normal).unwrap();
assert!(exists(&StdFs, &sst).unwrap());
remove(&StdFs, &sst, SyncMode::Normal);
assert!(!exists(&StdFs, &sst).unwrap());
assert!(matches!(
read(&StdFs, &sst, None).unwrap(),
SidecarRead::Missing
));
}
#[test]
fn failed_publish_cleans_up_the_temp() {
use crate::fs::{Fault, FaultFs, FaultOp, FaultRule};
use crate::io::ErrorKind;
let dir = tempfile::tempdir().unwrap();
let sst = dir.path().join("7");
let fault = FaultFs::new(StdFs);
fault.injector().arm(
FaultRule::new(FaultOp::Rename, Fault::Error(ErrorKind::Other)).on_path("restrict-bound"),
);
assert!(write(&fault, &sst, None, 7, b"k00130", SyncMode::Normal).is_err());
assert!(
!sidecar_tmp_path(&sst).exists(),
"the synced temp must be cleaned up after a failed publish",
);
assert!(matches!(
read(&StdFs, &sst, None).unwrap(),
SidecarRead::Missing
));
}
#[cfg(feature = "encryption")]
#[test]
fn encrypted_sidecar_roundtrips_and_rejects_a_wrong_key() {
use crate::encryption::{Aes256GcmProvider, EncryptionProvider};
use std::sync::Arc;
let dir = tempfile::tempdir().unwrap();
let sst = dir.path().join("7");
let provider: Arc<dyn EncryptionProvider> = Arc::new(Aes256GcmProvider::new(&[3u8; 32]));
write(
&StdFs,
&sst,
Some(provider.as_ref()),
7,
b"k00130",
SyncMode::Normal,
)
.unwrap();
match read(&StdFs, &sst, Some(provider.as_ref())).unwrap() {
SidecarRead::Present(id, bound) => {
assert_eq!(id, 7);
assert_eq!(bound, b"k00130");
}
_ => panic!("expected Present with the right key"),
}
let wrong: Arc<dyn EncryptionProvider> = Arc::new(Aes256GcmProvider::new(&[9u8; 32]));
assert!(matches!(
read(&StdFs, &sst, Some(wrong.as_ref())).unwrap(),
SidecarRead::Corrupt,
));
}