use super::*;
use crate::fs::MemFs;
use crate::runtime_config::RuntimeConfig;
fn open_writer(fs: &dyn Fs, path: &Path, runtime: RuntimeConfig) -> ManifestArchiveWriter {
ManifestArchiveWriter::create(path, fs, Arc::new(runtime), None, SyncMode::Normal)
.expect("manifest writer opens cleanly on a fresh path")
}
#[test]
fn writer_reserves_head_region_on_create() {
let fs = MemFs::new();
fs.create_dir_all(Path::new("/m")).unwrap();
let path = Path::new("/m/manifest");
let writer = open_writer(&fs, path, RuntimeConfig::default());
drop(writer);
let mut file = fs.open(path, &FsOpenOptions::new().read(true)).unwrap();
let mut buf = vec![0u8; HEAD_FOOTER_RESERVED_SIZE as usize];
use std::io::Read;
file.read_exact(&mut buf).unwrap();
assert!(buf.iter().all(|&b| b == 0));
}
#[test]
fn writer_rejects_empty_section_name() {
let fs = MemFs::new();
fs.create_dir_all(Path::new("/m")).unwrap();
let mut w = open_writer(&fs, Path::new("/m/manifest"), RuntimeConfig::default());
let err = w.start("").expect_err("empty name must be rejected");
assert!(matches!(err, crate::Error::ManifestFooterInvalid(_)));
}
#[test]
fn writer_rejects_duplicate_section_name() {
let fs = MemFs::new();
fs.create_dir_all(Path::new("/m")).unwrap();
let mut w = open_writer(&fs, Path::new("/m/manifest"), RuntimeConfig::default());
w.start("tables").unwrap();
w.write_all(&[1, 2, 3]).unwrap();
let err = w
.start("tables")
.expect_err("duplicate section name must be rejected");
assert!(matches!(err, crate::Error::ManifestFooterInvalid(_)));
}
#[test]
fn writer_finish_with_no_sections_writes_only_footer() {
let fs = MemFs::new();
fs.create_dir_all(Path::new("/m")).unwrap();
let path = Path::new("/m/empty");
let writer = open_writer(&fs, path, RuntimeConfig::default());
writer.finish().expect("finish on empty writer succeeds");
let meta = fs.metadata(path).unwrap();
assert!(meta.len > HEAD_FOOTER_RESERVED_SIZE);
}
#[test]
fn writer_finish_writes_head_mirror_when_enabled() {
let fs = MemFs::new();
fs.create_dir_all(Path::new("/m")).unwrap();
let path = Path::new("/m/mirrored");
let mut w = open_writer(&fs, path, RuntimeConfig::default());
w.start("format_version").unwrap();
w.write_all(&[5]).unwrap();
w.finish().unwrap();
let mut file = fs.open(path, &FsOpenOptions::new().read(true)).unwrap();
use std::io::Read;
let mut head = vec![0u8; HEAD_FOOTER_RESERVED_SIZE as usize];
file.read_exact(&mut head).unwrap();
assert!(
!head.iter().take(64).all(|&b| b == 0),
"head mirror should contain the footer Block bytes, got all-zero prefix"
);
}
#[test]
fn writer_finish_leaves_head_region_zero_when_mirror_disabled() {
let fs = MemFs::new();
fs.create_dir_all(Path::new("/m")).unwrap();
let path = Path::new("/m/no_mirror");
let runtime = RuntimeConfig {
manifest_footer_mirror: false,
..RuntimeConfig::default()
};
let mut w = open_writer(&fs, path, runtime);
w.start("format_version").unwrap();
w.write_all(&[5]).unwrap();
w.finish().unwrap();
let mut file = fs.open(path, &FsOpenOptions::new().read(true)).unwrap();
use std::io::Read;
let mut head = vec![0u8; HEAD_FOOTER_RESERVED_SIZE as usize];
file.read_exact(&mut head).unwrap();
assert!(
head.iter().all(|&b| b == 0),
"head reservation should remain zeroed when manifest_footer_mirror=false"
);
}
#[test]
fn writer_section_block_offsets_advance_past_head_reservation() {
let fs = MemFs::new();
fs.create_dir_all(Path::new("/m")).unwrap();
let path = Path::new("/m/cursor");
let mut w = open_writer(&fs, path, RuntimeConfig::default());
w.start("a").unwrap();
w.write_all(&[1, 2, 3]).unwrap();
w.start("b").unwrap();
w.write_all(&[4, 5, 6, 7]).unwrap();
w.finish().unwrap();
let mut file = fs.open(path, &FsOpenOptions::new().read(true)).unwrap();
file.seek(SeekFrom::Start(HEAD_FOOTER_RESERVED_SIZE))
.unwrap();
let mut byte = [0u8; 1];
use std::io::Read;
file.read_exact(&mut byte).unwrap();
assert_ne!(
byte[0], 0,
"first section Block should start at HEAD_FOOTER_RESERVED_SIZE"
);
}