mod common;
use std::fs;
use std::path::Path;
use std::time::{Duration, UNIX_EPOCH};
use common::{TEST_PASSWORD, decrypt_files, encrypt_files, find_shadow_files};
use tempfile::TempDir;
fn build_tree(parent: &Path) -> std::path::PathBuf {
let root = parent.join("photos");
fs::create_dir_all(root.join("sub")).unwrap();
fs::create_dir_all(root.join("empty-dir")).unwrap();
fs::write(root.join("a.txt"), b"alpha").unwrap();
fs::write(root.join("empty.bin"), b"").unwrap();
fs::write(root.join("sub").join("b.txt"), b"bravo bravo").unwrap();
root
}
fn assert_tree_restored(root: &Path) {
assert_eq!(fs::read(root.join("a.txt")).unwrap(), b"alpha");
assert_eq!(fs::read(root.join("empty.bin")).unwrap(), b"");
assert_eq!(
fs::read(root.join("sub").join("b.txt")).unwrap(),
b"bravo bravo"
);
assert!(root.join("empty-dir").is_dir());
}
#[test]
fn test_directory_archive_round_trip() {
let src_dir = TempDir::new().unwrap();
let root = build_tree(src_dir.path());
let mtime = UNIX_EPOCH + Duration::new(1_500_000_000, 0);
fs::File::open(root.join("sub").join("b.txt"))
.unwrap()
.set_modified(mtime)
.unwrap();
#[cfg(unix)]
{
use std::os::unix::fs::PermissionsExt;
fs::set_permissions(root.join("a.txt"), fs::Permissions::from_mode(0o600)).unwrap();
}
let enc_dir = TempDir::new().unwrap();
encrypt_files(&[&root], TEST_PASSWORD, enc_dir.path()).unwrap();
let shadows = find_shadow_files(enc_dir.path());
assert_eq!(shadows.len(), 1);
let out_dir = TempDir::new().unwrap();
decrypt_files(&[&shadows[0]], TEST_PASSWORD, out_dir.path()).unwrap();
let restored = out_dir.path().join("photos");
assert_tree_restored(&restored);
assert_eq!(
fs::metadata(restored.join("sub").join("b.txt"))
.unwrap()
.modified()
.unwrap(),
mtime
);
#[cfg(unix)]
{
use std::os::unix::fs::PermissionsExt;
assert_eq!(
fs::metadata(restored.join("a.txt"))
.unwrap()
.permissions()
.mode()
& 0o7777,
0o600
);
}
}
#[test]
fn test_directory_archive_no_overwrite_without_force() {
let src_dir = TempDir::new().unwrap();
let root = build_tree(src_dir.path());
let enc_dir = TempDir::new().unwrap();
encrypt_files(&[&root], TEST_PASSWORD, enc_dir.path()).unwrap();
let shadows = find_shadow_files(enc_dir.path());
let out_dir = TempDir::new().unwrap();
decrypt_files(&[&shadows[0]], TEST_PASSWORD, out_dir.path()).unwrap();
assert!(decrypt_files(&[&shadows[0]], TEST_PASSWORD, out_dir.path()).is_err());
}
#[test]
fn test_empty_directory_archive_round_trip() {
let src_dir = TempDir::new().unwrap();
let root = src_dir.path().join("hollow");
fs::create_dir(&root).unwrap();
let enc_dir = TempDir::new().unwrap();
encrypt_files(&[&root], TEST_PASSWORD, enc_dir.path()).unwrap();
let shadows = find_shadow_files(enc_dir.path());
assert_eq!(shadows.len(), 1);
let out_dir = TempDir::new().unwrap();
decrypt_files(&[&shadows[0]], TEST_PASSWORD, out_dir.path()).unwrap();
assert!(out_dir.path().join("hollow").is_dir());
}
#[test]
fn test_delete_removes_original_file_after_encryption() {
let temp_dir = TempDir::new().unwrap();
let input_file = temp_dir.path().join("secret.txt");
fs::write(&input_file, b"delete me after").unwrap();
common::encrypt_with_options(&[&input_file], TEST_PASSWORD, temp_dir.path(), true).unwrap();
assert!(!input_file.exists(), "original must be deleted");
let shadows = find_shadow_files(temp_dir.path());
assert_eq!(shadows.len(), 1);
decrypt_files(&[&shadows[0]], TEST_PASSWORD, temp_dir.path()).unwrap();
assert_eq!(fs::read(&input_file).unwrap(), b"delete me after");
}
#[test]
fn test_delete_removes_original_directory_after_archiving() {
let src_dir = TempDir::new().unwrap();
let root = build_tree(src_dir.path());
let enc_dir = TempDir::new().unwrap();
common::encrypt_with_options(&[&root], TEST_PASSWORD, enc_dir.path(), true).unwrap();
assert!(!root.exists(), "original tree must be deleted");
let shadows = find_shadow_files(enc_dir.path());
let out_dir = TempDir::new().unwrap();
decrypt_files(&[&shadows[0]], TEST_PASSWORD, out_dir.path()).unwrap();
assert_tree_restored(&out_dir.path().join("photos"));
}