use crate::utils::{EmbedExt, TestResources, archive, setup};
use clap::Parser;
use portable_network_archive::cli;
use std::{collections::HashSet, fs, io::prelude::*, path::Path, time};
const DURATION_24_HOURS: time::Duration = time::Duration::from_secs(24 * 60 * 60);
#[test]
fn update_multipart_archive() {
setup();
TestResources::extract_in("raw/", "update_multipart/in/").unwrap();
cli::Cli::try_parse_from([
"pna",
"--quiet",
"c",
"-f",
"update_multipart/archive.pna",
"--overwrite",
"update_multipart/in/",
"--keep-timestamp",
])
.unwrap()
.execute()
.unwrap();
cli::Cli::try_parse_from([
"pna",
"--quiet",
"split",
"-f",
"update_multipart/archive.pna",
"--overwrite",
"--max-size",
"1kb",
"--out-dir",
"update_multipart/split/",
])
.unwrap()
.execute()
.unwrap();
assert!(
Path::new("update_multipart/split/archive.part2.pna").exists(),
"precondition: the archive should be split into multiple parts"
);
let mut initial_entries = HashSet::new();
archive::for_each_entry("update_multipart/archive.pna", |entry| {
initial_entries.insert(entry.header().path().to_string());
})
.unwrap();
let mut file = fs::File::options()
.write(true)
.truncate(true)
.open("update_multipart/in/raw/text.txt")
.unwrap();
file.write_all(b"updated content for multipart test")
.unwrap();
file.set_modified(time::SystemTime::now() + DURATION_24_HOURS)
.unwrap();
cli::Cli::try_parse_from([
"pna",
"--quiet",
"experimental",
"update",
"-f",
"update_multipart/split/archive.part1.pna",
"update_multipart/in/",
"--keep-timestamp",
])
.unwrap()
.execute()
.unwrap();
assert!(
Path::new("update_multipart/split/archive.pna").exists(),
"update on a multipart archive should write a single unsplit archive"
);
assert!(
Path::new("update_multipart/split/archive.part1.pna").exists()
&& Path::new("update_multipart/split/archive.part2.pna").exists(),
"original part files should remain on disk"
);
let mut seen = HashSet::new();
archive::for_each_entry("update_multipart/split/archive.pna", |entry| {
seen.insert(entry.header().path().to_string());
})
.unwrap();
assert_eq!(
seen, initial_entries,
"entry set should be unchanged after update"
);
cli::Cli::try_parse_from([
"pna",
"--quiet",
"x",
"-f",
"update_multipart/split/archive.pna",
"--overwrite",
"--out-dir",
"update_multipart/out/",
"--strip-components",
"2",
])
.unwrap()
.execute()
.unwrap();
assert_eq!(
fs::read("update_multipart/out/raw/text.txt").unwrap(),
b"updated content for multipart test",
"extraction should yield the updated content"
);
}
#[test]
fn update_multipart_archive_with_sync() {
setup();
TestResources::extract_in("raw/", "update_multipart_sync/in/").unwrap();
cli::Cli::try_parse_from([
"pna",
"--quiet",
"c",
"-f",
"update_multipart_sync/archive.pna",
"--overwrite",
"update_multipart_sync/in/",
"--keep-timestamp",
])
.unwrap()
.execute()
.unwrap();
cli::Cli::try_parse_from([
"pna",
"--quiet",
"split",
"-f",
"update_multipart_sync/archive.pna",
"--overwrite",
"--max-size",
"1kb",
"--out-dir",
"update_multipart_sync/split/",
])
.unwrap()
.execute()
.unwrap();
assert!(
Path::new("update_multipart_sync/split/archive.part2.pna").exists(),
"precondition: the archive should be split into multiple parts"
);
let mut initial_entries = HashSet::new();
archive::for_each_entry("update_multipart_sync/archive.pna", |entry| {
initial_entries.insert(entry.header().path().to_string());
})
.unwrap();
fs::remove_file("update_multipart_sync/in/raw/empty.txt").unwrap();
cli::Cli::try_parse_from([
"pna",
"--quiet",
"experimental",
"update",
"--sync",
"-f",
"update_multipart_sync/split/archive.part1.pna",
"update_multipart_sync/in/",
"--keep-timestamp",
])
.unwrap()
.execute()
.unwrap();
let mut seen = HashSet::new();
archive::for_each_entry("update_multipart_sync/split/archive.pna", |entry| {
seen.insert(entry.header().path().to_string());
})
.unwrap();
let mut expected = initial_entries;
assert!(
expected.remove("update_multipart_sync/in/raw/empty.txt"),
"precondition: initial archive should contain the deleted file"
);
assert_eq!(
seen, expected,
"only the entry missing on disk should be pruned from the merged archive"
);
}