use safe_unzip::{Driver, OverwriteMode, ValidationMode, ZipAdapter};
use std::io::Write;
use tempfile::tempdir;
use zip::write::FileOptions;
fn create_simple_zip(name: &str, content: &[u8]) -> std::fs::File {
let file = tempfile::tempfile().unwrap();
let mut zip = zip::ZipWriter::new(file);
let options: FileOptions<()> = FileOptions::default();
zip.start_file(name, options).unwrap();
zip.write_all(content).unwrap();
zip.finish().unwrap()
}
fn create_multi_file_zip(files: &[(&str, &[u8])]) -> std::fs::File {
let file = tempfile::tempfile().unwrap();
let mut zip = zip::ZipWriter::new(file);
let options: FileOptions<()> = FileOptions::default();
for (name, content) in files {
zip.start_file(*name, options).unwrap();
zip.write_all(content).unwrap();
}
zip.finish().unwrap()
}
#[test]
fn test_driver_basic_extraction() {
let dest = tempdir().unwrap();
let zip_file = create_simple_zip("hello.txt", b"Hello, World!");
let adapter = ZipAdapter::new(zip_file).unwrap();
let report = Driver::new(dest.path())
.unwrap()
.extract_zip(adapter)
.unwrap();
assert_eq!(report.files_extracted, 1);
assert_eq!(report.bytes_written, 13);
assert!(dest.path().join("hello.txt").exists());
let content = std::fs::read_to_string(dest.path().join("hello.txt")).unwrap();
assert_eq!(content, "Hello, World!");
println!("✅ Driver basic extraction works");
}
#[test]
fn test_driver_multiple_files() {
let dest = tempdir().unwrap();
let zip_file = create_multi_file_zip(&[
("a.txt", b"aaa"),
("b.txt", b"bbb"),
("subdir/c.txt", b"ccc"),
]);
let adapter = ZipAdapter::new(zip_file).unwrap();
let report = Driver::new(dest.path())
.unwrap()
.extract_zip(adapter)
.unwrap();
assert_eq!(report.files_extracted, 3);
assert!(dest.path().join("a.txt").exists());
assert!(dest.path().join("subdir/c.txt").exists());
println!("✅ Driver multiple files extraction works");
}
#[test]
fn test_driver_blocks_path_traversal() {
let dest = tempdir().unwrap();
let zip_file = create_simple_zip("../../etc/passwd", b"evil");
let adapter = ZipAdapter::new(zip_file).unwrap();
let result = Driver::new(dest.path()).unwrap().extract_zip(adapter);
assert!(result.is_err());
println!("✅ Driver blocks path traversal");
}
#[test]
fn test_driver_validate_first_mode() {
let dest = tempdir().unwrap();
let file = tempfile::tempfile().unwrap();
let mut zip = zip::ZipWriter::new(file);
let options: FileOptions<()> = FileOptions::default();
zip.start_file("good.txt", options).unwrap();
zip.write_all(b"This is fine").unwrap();
zip.start_file("../../evil.txt", options).unwrap();
zip.write_all(b"pwned").unwrap();
let zip_file = zip.finish().unwrap();
let adapter = ZipAdapter::new(zip_file).unwrap();
let result = Driver::new(dest.path())
.unwrap()
.validation(ValidationMode::ValidateFirst)
.extract_zip(adapter);
assert!(result.is_err());
assert!(
!dest.path().join("good.txt").exists(),
"ValidateFirst should not write good.txt before failing"
);
println!("✅ Driver ValidateFirst mode works");
}
#[test]
fn test_driver_overwrite_error() {
let dest = tempdir().unwrap();
let zip1 = create_simple_zip("test.txt", b"original");
let adapter1 = ZipAdapter::new(zip1).unwrap();
Driver::new(dest.path())
.unwrap()
.extract_zip(adapter1)
.unwrap();
let zip2 = create_simple_zip("test.txt", b"modified");
let adapter2 = ZipAdapter::new(zip2).unwrap();
let result = Driver::new(dest.path())
.unwrap()
.overwrite(OverwriteMode::Error)
.extract_zip(adapter2);
assert!(result.is_err());
let content = std::fs::read_to_string(dest.path().join("test.txt")).unwrap();
assert_eq!(content, "original");
println!("✅ Driver OverwriteMode::Error works");
}
#[test]
fn test_driver_overwrite_skip() {
let dest = tempdir().unwrap();
let zip1 = create_simple_zip("test.txt", b"original");
let adapter1 = ZipAdapter::new(zip1).unwrap();
Driver::new(dest.path())
.unwrap()
.extract_zip(adapter1)
.unwrap();
let zip2 = create_simple_zip("test.txt", b"modified");
let adapter2 = ZipAdapter::new(zip2).unwrap();
let report = Driver::new(dest.path())
.unwrap()
.overwrite(OverwriteMode::Skip)
.extract_zip(adapter2)
.unwrap();
assert_eq!(report.entries_skipped, 1);
let content = std::fs::read_to_string(dest.path().join("test.txt")).unwrap();
assert_eq!(content, "original");
println!("✅ Driver OverwriteMode::Skip works");
}
#[test]
fn test_driver_overwrite_overwrite() {
let dest = tempdir().unwrap();
let zip1 = create_simple_zip("test.txt", b"original");
let adapter1 = ZipAdapter::new(zip1).unwrap();
Driver::new(dest.path())
.unwrap()
.extract_zip(adapter1)
.unwrap();
let zip2 = create_simple_zip("test.txt", b"modified");
let adapter2 = ZipAdapter::new(zip2).unwrap();
let report = Driver::new(dest.path())
.unwrap()
.overwrite(OverwriteMode::Overwrite)
.extract_zip(adapter2)
.unwrap();
assert_eq!(report.files_extracted, 1);
let content = std::fs::read_to_string(dest.path().join("test.txt")).unwrap();
assert_eq!(content, "modified");
println!("✅ Driver OverwriteMode::Overwrite works");
}
#[test]
fn test_driver_filter() {
let dest = tempdir().unwrap();
let zip_file = create_multi_file_zip(&[
("image.png", b"png data"),
("document.txt", b"text data"),
("photo.jpg", b"jpg data"),
]);
let adapter = ZipAdapter::new(zip_file).unwrap();
let report = Driver::new(dest.path())
.unwrap()
.filter(|info| info.name.ends_with(".txt"))
.extract_zip(adapter)
.unwrap();
assert_eq!(report.files_extracted, 1);
assert!(dest.path().join("document.txt").exists());
assert!(!dest.path().join("image.png").exists());
assert!(!dest.path().join("photo.jpg").exists());
println!("✅ Driver filter works");
}
#[test]
fn test_driver_atomic_file_creation() {
let dest = tempdir().unwrap();
std::fs::write(dest.path().join("existing.txt"), "original").unwrap();
let zip_file = create_simple_zip("existing.txt", b"new content");
let adapter = ZipAdapter::new(zip_file).unwrap();
let result = Driver::new(dest.path())
.unwrap()
.overwrite(OverwriteMode::Error)
.extract_zip(adapter);
assert!(matches!(
result,
Err(safe_unzip::Error::AlreadyExists { .. })
));
let content = std::fs::read_to_string(dest.path().join("existing.txt")).unwrap();
assert_eq!(content, "original");
println!("✅ Driver atomic file creation works");
}