mod helpers;
use globset::GlobSet;
use helpers::{TestResult, temp_utf8_dir};
use rz_archive::error::Error;
use rz_archive::{CompressOpts, DecompressOpts};
fn compress_opts_with_password(password: &str) -> CompressOpts<'static> {
let mut opts = CompressOpts::new(None, GlobSet::empty());
opts.password = Some(password.to_owned());
opts
}
fn decompress_opts_with_password(password: &str) -> DecompressOpts<'static> {
let mut opts = DecompressOpts::new(true, 0, GlobSet::empty(), GlobSet::empty());
opts.password = Some(password.to_owned());
opts
}
fn decompress_opts_no_password() -> DecompressOpts<'static> {
DecompressOpts::new(true, 0, GlobSet::empty(), GlobSet::empty())
}
fn decompress_opts_wrong_password() -> DecompressOpts<'static> {
let mut opts = DecompressOpts::new(true, 0, GlobSet::empty(), GlobSet::empty());
opts.password = Some("wrong_password".to_owned());
opts
}
#[test]
fn zip_aes_round_trip() -> TestResult {
let (_guard, tmp) = temp_utf8_dir()?;
let src = tmp.join("secret.txt");
fs_err::write(&src, b"top secret content\n")?;
let archive = tmp.join("secret.zip");
rz_archive::zip::compress(&[src], &archive, &compress_opts_with_password("mypassword"))?;
let out = tmp.join("out");
fs_err::create_dir(&out)?;
rz_archive::zip::decompress(&archive, &out, &decompress_opts_with_password("mypassword"))?;
let content = fs_err::read_to_string(out.join("secret.txt"))?;
assert_eq!(content, "top secret content\n");
Ok(())
}
#[test]
fn zip_decrypt_wrong_password_errors() -> TestResult {
let (_guard, tmp) = temp_utf8_dir()?;
let src = tmp.join("secret.txt");
fs_err::write(&src, b"content\n")?;
let archive = tmp.join("secret.zip");
rz_archive::zip::compress(&[src], &archive, &compress_opts_with_password("correct"))?;
let out = tmp.join("out");
fs_err::create_dir(&out)?;
let result = rz_archive::zip::decompress(&archive, &out, &decompress_opts_wrong_password());
assert!(
result.is_err(),
"expected error with wrong password, got success"
);
Ok(())
}
#[test]
fn zip_decrypt_missing_password_errors() -> TestResult {
let (_guard, tmp) = temp_utf8_dir()?;
let src = tmp.join("secret.txt");
fs_err::write(&src, b"content\n")?;
let archive = tmp.join("secret.zip");
rz_archive::zip::compress(
&[src],
&archive,
&compress_opts_with_password("somepassword"),
)?;
let out = tmp.join("out");
fs_err::create_dir(&out)?;
let result = rz_archive::zip::decompress(&archive, &out, &decompress_opts_no_password());
assert!(
matches!(result, Err(Error::PasswordRequired)),
"expected PasswordRequired, got: {result:?}",
);
Ok(())
}
#[test]
fn zip_decrypt_unencrypted_with_password_works() -> TestResult {
let (_guard, tmp) = temp_utf8_dir()?;
let src = tmp.join("plain.txt");
fs_err::write(&src, b"plain content\n")?;
let archive = tmp.join("plain.zip");
rz_archive::zip::compress(&[src], &archive, &CompressOpts::new(None, GlobSet::empty()))?;
let out = tmp.join("out");
fs_err::create_dir(&out)?;
rz_archive::zip::decompress(&archive, &out, &decompress_opts_with_password("ignored"))?;
let content = fs_err::read_to_string(out.join("plain.txt"))?;
assert_eq!(content, "plain content\n");
Ok(())
}
#[test]
fn zip_test_encrypted() -> TestResult {
let (_guard, tmp) = temp_utf8_dir()?;
let src = tmp.join("secret.txt");
fs_err::write(&src, b"content\n")?;
let archive = tmp.join("secret.zip");
rz_archive::zip::compress(&[src], &archive, &compress_opts_with_password("pw"))?;
rz_archive::zip::test(&archive, Some("pw"), &rz_archive::progress::NoProgress)?;
let result = rz_archive::zip::test(&archive, None, &rz_archive::progress::NoProgress);
assert!(matches!(result, Err(Error::PasswordRequired)));
Ok(())
}
#[test]
fn seven_z_aes_round_trip() -> TestResult {
let (_guard, tmp) = temp_utf8_dir()?;
let src = tmp.join("secret.txt");
fs_err::write(&src, b"7z secret content\n")?;
let archive = tmp.join("secret.7z");
rz_archive::seven_z::compress(&[src], &archive, &compress_opts_with_password("mypassword"))?;
let out = tmp.join("out");
rz_archive::seven_z::decompress(&archive, &out, &decompress_opts_with_password("mypassword"))?;
let content = fs_err::read_to_string(out.join("secret.txt"))?;
assert_eq!(content, "7z secret content\n");
Ok(())
}
#[test]
fn seven_z_decrypt_wrong_password_errors() -> TestResult {
let (_guard, tmp) = temp_utf8_dir()?;
let src = tmp.join("secret.txt");
fs_err::write(&src, b"content\n")?;
let archive = tmp.join("secret.7z");
rz_archive::seven_z::compress(&[src], &archive, &compress_opts_with_password("correct"))?;
let out = tmp.join("out");
let result = rz_archive::seven_z::decompress(&archive, &out, &decompress_opts_wrong_password());
assert!(
result.is_err(),
"expected error with wrong password, got success"
);
Ok(())
}
#[test]
fn tar_gz_rejects_password_compress() -> TestResult {
let (_guard, tmp) = temp_utf8_dir()?;
let src = tmp.join("file.txt");
fs_err::write(&src, b"content\n")?;
let archive = tmp.join("archive.tar.gz");
let fmt = rz_archive::cmd::Format::TarGz;
let password = Some("test".to_owned());
let result: rz_archive::error::Result<()> = if password.is_some()
&& !matches!(
fmt,
rz_archive::cmd::Format::Zip | rz_archive::cmd::Format::SevenZ
) {
Err(Error::EncryptionUnsupported(fmt.to_string()))
} else {
rz_archive::tar_gz::compress(&[src], &archive, &CompressOpts::new(None, GlobSet::empty()))
};
assert!(matches!(result, Err(Error::EncryptionUnsupported(_))));
Ok(())
}
#[test]
fn tar_gz_rejects_password_decompress() -> TestResult {
let (_guard, tmp) = temp_utf8_dir()?;
let src = tmp.join("file.txt");
fs_err::write(&src, b"content\n")?;
let archive = tmp.join("archive.tar.gz");
rz_archive::tar_gz::compress(&[src], &archive, &CompressOpts::new(None, GlobSet::empty()))?;
let fmt = rz_archive::cmd::Format::TarGz;
let password = Some("test".to_owned());
let out = tmp.join("out");
fs_err::create_dir(&out)?;
let result: rz_archive::error::Result<()> = if password.is_some()
&& !matches!(
fmt,
rz_archive::cmd::Format::Zip | rz_archive::cmd::Format::SevenZ
) {
Err(Error::EncryptionUnsupported(fmt.to_string()))
} else {
rz_archive::tar_gz::decompress(
&archive,
&out,
&DecompressOpts::new(true, 0, GlobSet::empty(), GlobSet::empty()),
)
};
assert!(matches!(result, Err(Error::EncryptionUnsupported(_))));
Ok(())
}
#[test]
fn cli_password_args_mutually_exclusive() {
use clap::Parser;
use rz_archive::cmd::Cli;
let result = Cli::try_parse_from([
"rz_archive",
"compress",
"--password-stdin",
"--password",
"pw",
"file",
]);
assert!(result.is_err(), "expected clap error for conflicting flags");
let result = Cli::try_parse_from([
"rz_archive",
"compress",
"--password-file",
"/tmp/pw.txt",
"--password",
"pw",
"file",
]);
assert!(result.is_err(), "expected clap error for conflicting flags");
let result = Cli::try_parse_from(["rz_archive", "compress", "--password", "pw", "file"]);
assert!(result.is_ok(), "expected --password alone to parse fine");
}