use crate::utils::{EmbedExt, TestResources, archive, setup};
use clap::Parser;
use portable_network_archive::cli;
use std::{collections::HashSet, fs};
#[test]
fn update_with_recursive() {
setup();
let _ = fs::remove_dir_all("update_recursive");
TestResources::extract_in("raw/", "update_recursive/in/").unwrap();
cli::Cli::try_parse_from([
"pna",
"--quiet",
"c",
"-f",
"update_recursive/archive.pna",
"--overwrite",
"update_recursive/in/",
])
.unwrap()
.execute()
.unwrap();
fs::create_dir_all("update_recursive/in/raw/subdir/nested").unwrap();
fs::write("update_recursive/in/raw/subdir/file1.txt", "file in subdir").unwrap();
fs::write(
"update_recursive/in/raw/subdir/nested/file2.txt",
"file in nested",
)
.unwrap();
cli::Cli::try_parse_from([
"pna",
"--quiet",
"experimental",
"update",
"-f",
"update_recursive/archive.pna",
"update_recursive/in/",
])
.unwrap()
.execute()
.unwrap();
let mut seen = HashSet::new();
archive::for_each_entry("update_recursive/archive.pna", |entry| {
seen.insert(entry.header().path().to_string());
})
.unwrap();
assert!(
seen.contains("update_recursive/in/raw/subdir/file1.txt"),
"file in subdir should be included with recursive: {seen:?}"
);
assert!(
seen.contains("update_recursive/in/raw/subdir/nested/file2.txt"),
"file in nested dir should be included with recursive: {seen:?}"
);
}
#[test]
fn update_with_no_recursive_keep_dir() {
setup();
let _ = fs::remove_dir_all("update_no_recursive_keep_dir");
fs::create_dir_all("update_no_recursive_keep_dir/in/mydir").unwrap();
fs::write(
"update_no_recursive_keep_dir/in/mydir/file.txt",
"file inside dir",
)
.unwrap();
fs::write(
"update_no_recursive_keep_dir/in/placeholder.txt",
"placeholder",
)
.unwrap();
cli::Cli::try_parse_from([
"pna",
"--quiet",
"c",
"-f",
"update_no_recursive_keep_dir/archive.pna",
"--overwrite",
"update_no_recursive_keep_dir/in/placeholder.txt",
])
.unwrap()
.execute()
.unwrap();
cli::Cli::try_parse_from([
"pna",
"--quiet",
"experimental",
"update",
"-f",
"update_no_recursive_keep_dir/archive.pna",
"--no-recursive",
"--keep-dir",
"update_no_recursive_keep_dir/in/mydir/",
])
.unwrap()
.execute()
.unwrap();
let mut seen = HashSet::new();
archive::for_each_entry("update_no_recursive_keep_dir/archive.pna", |entry| {
seen.insert(entry.header().path().to_string());
})
.unwrap();
assert!(
seen.contains("update_no_recursive_keep_dir/in/mydir"),
"directory entry should be included with --no-recursive --keep-dir: {seen:?}"
);
assert!(
!seen.contains("update_no_recursive_keep_dir/in/mydir/file.txt"),
"file inside directory should NOT be included with --no-recursive: {seen:?}"
);
}
#[test]
fn update_with_explicit_recursive_flag() {
setup();
let _ = fs::remove_dir_all("update_explicit_recursive");
TestResources::extract_in("raw/", "update_explicit_recursive/in/").unwrap();
cli::Cli::try_parse_from([
"pna",
"--quiet",
"c",
"-f",
"update_explicit_recursive/archive.pna",
"--overwrite",
"update_explicit_recursive/in/",
])
.unwrap()
.execute()
.unwrap();
fs::create_dir_all("update_explicit_recursive/in/raw/deep/dir/structure").unwrap();
fs::write(
"update_explicit_recursive/in/raw/deep/dir/structure/deep_file.txt",
"deep file content",
)
.unwrap();
cli::Cli::try_parse_from([
"pna",
"--quiet",
"experimental",
"update",
"-f",
"update_explicit_recursive/archive.pna",
"--recursive",
"update_explicit_recursive/in/",
])
.unwrap()
.execute()
.unwrap();
let mut seen = HashSet::new();
archive::for_each_entry("update_explicit_recursive/archive.pna", |entry| {
seen.insert(entry.header().path().to_string());
})
.unwrap();
assert!(
seen.contains("update_explicit_recursive/in/raw/deep/dir/structure/deep_file.txt"),
"deeply nested file should be included with --recursive: {seen:?}"
);
}
#[test]
fn update_no_recursive_preserves_existing_entries() {
setup();
let _ = fs::remove_dir_all("update_no_recursive_preserve");
fs::create_dir_all("update_no_recursive_preserve/in/subdir").unwrap();
fs::write(
"update_no_recursive_preserve/in/toplevel.txt",
"original top",
)
.unwrap();
fs::write(
"update_no_recursive_preserve/in/subdir/nested.txt",
"original nested",
)
.unwrap();
cli::Cli::try_parse_from([
"pna",
"--quiet",
"c",
"-f",
"update_no_recursive_preserve/archive.pna",
"--overwrite",
"update_no_recursive_preserve/in/",
])
.unwrap()
.execute()
.unwrap();
let mut initial_seen = HashSet::new();
archive::for_each_entry("update_no_recursive_preserve/archive.pna", |entry| {
initial_seen.insert(entry.header().path().to_string());
})
.unwrap();
assert!(
initial_seen.contains("update_no_recursive_preserve/in/subdir/nested.txt"),
"nested file should be in initial archive"
);
assert!(
initial_seen.contains("update_no_recursive_preserve/in/toplevel.txt"),
"toplevel file should be in initial archive"
);
fs::write(
"update_no_recursive_preserve/in/subdir/new_nested.txt",
"new nested file",
)
.unwrap();
cli::Cli::try_parse_from([
"pna",
"--quiet",
"experimental",
"update",
"-f",
"update_no_recursive_preserve/archive.pna",
"--no-recursive",
"--keep-dir",
"update_no_recursive_preserve/in/subdir/",
])
.unwrap()
.execute()
.unwrap();
let mut final_seen = HashSet::new();
archive::for_each_entry("update_no_recursive_preserve/archive.pna", |entry| {
final_seen.insert(entry.header().path().to_string());
})
.unwrap();
assert!(
final_seen.contains("update_no_recursive_preserve/in/subdir/nested.txt"),
"existing nested file should be preserved: {final_seen:?}"
);
assert!(
final_seen.contains("update_no_recursive_preserve/in/toplevel.txt"),
"existing toplevel file should be preserved: {final_seen:?}"
);
assert!(
!final_seen.contains("update_no_recursive_preserve/in/subdir/new_nested.txt"),
"new nested file should NOT be added with --no-recursive: {final_seen:?}"
);
}
#[test]
fn update_no_recursive_with_file_args() {
setup();
let _ = fs::remove_dir_all("update_no_recursive_files");
fs::create_dir_all("update_no_recursive_files/in/dir").unwrap();
fs::write("update_no_recursive_files/in/file1.txt", "file1").unwrap();
fs::write("update_no_recursive_files/in/file2.txt", "file2").unwrap();
fs::write("update_no_recursive_files/in/dir/nested.txt", "nested").unwrap();
cli::Cli::try_parse_from([
"pna",
"--quiet",
"c",
"-f",
"update_no_recursive_files/archive.pna",
"--overwrite",
"update_no_recursive_files/in/",
])
.unwrap()
.execute()
.unwrap();
fs::write("update_no_recursive_files/in/file1.txt", "file1 updated").unwrap();
fs::write("update_no_recursive_files/in/file2.txt", "file2 updated").unwrap();
fs::write(
"update_no_recursive_files/in/dir/nested.txt",
"nested updated",
)
.unwrap();
cli::Cli::try_parse_from([
"pna",
"--quiet",
"experimental",
"update",
"-f",
"update_no_recursive_files/archive.pna",
"--no-recursive",
"update_no_recursive_files/in/file1.txt",
])
.unwrap()
.execute()
.unwrap();
cli::Cli::try_parse_from([
"pna",
"--quiet",
"x",
"-f",
"update_no_recursive_files/archive.pna",
"--overwrite",
"--out-dir",
"update_no_recursive_files/out/",
"--strip-components",
"2",
])
.unwrap()
.execute()
.unwrap();
let file1_content = fs::read_to_string("update_no_recursive_files/out/file1.txt").unwrap();
assert_eq!(
file1_content, "file1 updated",
"file1.txt should be updated"
);
let file2_content = fs::read_to_string("update_no_recursive_files/out/file2.txt").unwrap();
assert_eq!(
file2_content, "file2",
"file2.txt should NOT be updated when not specified"
);
}
#[test]
fn update_recursive_vs_no_recursive_comparison() {
setup();
let _ = fs::remove_dir_all("update_recursive_compare");
fs::create_dir_all("update_recursive_compare/in/level1/level2/level3").unwrap();
fs::write("update_recursive_compare/in/root.txt", "root").unwrap();
fs::write("update_recursive_compare/in/level1/l1.txt", "level1").unwrap();
fs::write("update_recursive_compare/in/level1/level2/l2.txt", "level2").unwrap();
fs::write(
"update_recursive_compare/in/level1/level2/level3/l3.txt",
"level3",
)
.unwrap();
cli::Cli::try_parse_from([
"pna",
"--quiet",
"c",
"-f",
"update_recursive_compare/archive.pna",
"--overwrite",
"update_recursive_compare/in/root.txt",
])
.unwrap()
.execute()
.unwrap();
cli::Cli::try_parse_from([
"pna",
"--quiet",
"experimental",
"update",
"-f",
"update_recursive_compare/archive.pna",
"--no-recursive",
"--keep-dir",
"update_recursive_compare/in/level1/",
])
.unwrap()
.execute()
.unwrap();
let mut after_no_recursive = HashSet::new();
archive::for_each_entry("update_recursive_compare/archive.pna", |entry| {
after_no_recursive.insert(entry.header().path().to_string());
})
.unwrap();
assert!(
after_no_recursive.contains("update_recursive_compare/in/root.txt"),
"root.txt should be present"
);
assert!(
after_no_recursive.contains("update_recursive_compare/in/level1"),
"level1 directory should be present with --keep-dir: {after_no_recursive:?}"
);
assert!(
!after_no_recursive.contains("update_recursive_compare/in/level1/l1.txt"),
"l1.txt should NOT be present with --no-recursive: {after_no_recursive:?}"
);
assert!(
!after_no_recursive.contains("update_recursive_compare/in/level1/level2/l2.txt"),
"l2.txt should NOT be present: {after_no_recursive:?}"
);
cli::Cli::try_parse_from([
"pna",
"--quiet",
"experimental",
"update",
"-f",
"update_recursive_compare/archive.pna",
"--recursive",
"update_recursive_compare/in/level1/",
])
.unwrap()
.execute()
.unwrap();
let mut after_recursive = HashSet::new();
archive::for_each_entry("update_recursive_compare/archive.pna", |entry| {
after_recursive.insert(entry.header().path().to_string());
})
.unwrap();
assert!(
after_recursive.contains("update_recursive_compare/in/level1/l1.txt"),
"l1.txt should be present after recursive update: {after_recursive:?}"
);
assert!(
after_recursive.contains("update_recursive_compare/in/level1/level2/l2.txt"),
"l2.txt should be present after recursive update: {after_recursive:?}"
);
assert!(
after_recursive.contains("update_recursive_compare/in/level1/level2/level3/l3.txt"),
"l3.txt should be present after recursive update: {after_recursive:?}"
);
}