test(12-01): update all tests for explicit key args, add key input tests
- Replace KEY import in golden.rs with local constant - Replace KEY import in crypto.rs tests with local TEST_KEY constant - Add --key to all CLI round-trip tests via cmd_with_key() helper - Add test_key_file_roundtrip: pack/unpack with --key-file - Add test_rejects_wrong_key: wrong key causes decryption failure - Add test_rejects_bad_hex: too-short hex produces clear error - Add test_rejects_missing_key: pack without key arg fails - Add test_inspect_without_key: shows header only, not TOC - Add test_inspect_with_key: shows full entry listing - All 47 tests pass (25 unit + 7 golden + 15 integration) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -80,15 +80,22 @@ pub fn sha256_hash(data: &[u8]) -> [u8; 32] {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::key::KEY;
|
|
||||||
use hex_literal::hex;
|
use hex_literal::hex;
|
||||||
|
|
||||||
|
/// Test key matching legacy hardcoded value
|
||||||
|
const TEST_KEY: [u8; 32] = [
|
||||||
|
0x7A, 0x35, 0xC1, 0xD9, 0x4F, 0xE8, 0x2B, 0x6A,
|
||||||
|
0x91, 0x0D, 0xF3, 0x58, 0xBC, 0x74, 0xA6, 0x1E,
|
||||||
|
0x42, 0x8F, 0xD0, 0x63, 0xE5, 0x17, 0x9B, 0x2C,
|
||||||
|
0xFA, 0x84, 0x06, 0xCD, 0x3E, 0x79, 0xB5, 0x50,
|
||||||
|
];
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_encrypt_decrypt_roundtrip() {
|
fn test_encrypt_decrypt_roundtrip() {
|
||||||
let plaintext = b"Hello, World!";
|
let plaintext = b"Hello, World!";
|
||||||
let iv = [0u8; 16];
|
let iv = [0u8; 16];
|
||||||
let ciphertext = encrypt_data(plaintext, &KEY, &iv);
|
let ciphertext = encrypt_data(plaintext, &TEST_KEY, &iv);
|
||||||
let decrypted = decrypt_data(&ciphertext, &KEY, &iv).unwrap();
|
let decrypted = decrypt_data(&ciphertext, &TEST_KEY, &iv).unwrap();
|
||||||
assert_eq!(decrypted, plaintext);
|
assert_eq!(decrypted, plaintext);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -96,8 +103,8 @@ mod tests {
|
|||||||
fn test_encrypt_decrypt_empty() {
|
fn test_encrypt_decrypt_empty() {
|
||||||
let plaintext = b"";
|
let plaintext = b"";
|
||||||
let iv = [0u8; 16];
|
let iv = [0u8; 16];
|
||||||
let ciphertext = encrypt_data(plaintext, &KEY, &iv);
|
let ciphertext = encrypt_data(plaintext, &TEST_KEY, &iv);
|
||||||
let decrypted = decrypt_data(&ciphertext, &KEY, &iv).unwrap();
|
let decrypted = decrypt_data(&ciphertext, &TEST_KEY, &iv).unwrap();
|
||||||
assert_eq!(decrypted, plaintext.as_slice());
|
assert_eq!(decrypted, plaintext.as_slice());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,23 +112,23 @@ mod tests {
|
|||||||
fn test_encrypted_size_formula() {
|
fn test_encrypted_size_formula() {
|
||||||
let iv = [0u8; 16];
|
let iv = [0u8; 16];
|
||||||
// 5 bytes -> ((5/16)+1)*16 = 16
|
// 5 bytes -> ((5/16)+1)*16 = 16
|
||||||
assert_eq!(encrypt_data(b"Hello", &KEY, &iv).len(), 16);
|
assert_eq!(encrypt_data(b"Hello", &TEST_KEY, &iv).len(), 16);
|
||||||
// 16 bytes -> ((16/16)+1)*16 = 32 (full padding block)
|
// 16 bytes -> ((16/16)+1)*16 = 32 (full padding block)
|
||||||
assert_eq!(encrypt_data(&[0u8; 16], &KEY, &iv).len(), 32);
|
assert_eq!(encrypt_data(&[0u8; 16], &TEST_KEY, &iv).len(), 32);
|
||||||
// 0 bytes -> ((0/16)+1)*16 = 16
|
// 0 bytes -> ((0/16)+1)*16 = 16
|
||||||
assert_eq!(encrypt_data(b"", &KEY, &iv).len(), 16);
|
assert_eq!(encrypt_data(b"", &TEST_KEY, &iv).len(), 16);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_hmac_compute_verify() {
|
fn test_hmac_compute_verify() {
|
||||||
let iv = [0xAA; 16];
|
let iv = [0xAA; 16];
|
||||||
let ciphertext = b"some ciphertext data here!!12345";
|
let ciphertext = b"some ciphertext data here!!12345";
|
||||||
let hmac_tag = compute_hmac(&KEY, &iv, ciphertext);
|
let hmac_tag = compute_hmac(&TEST_KEY, &iv, ciphertext);
|
||||||
// Verify with correct tag
|
// Verify with correct tag
|
||||||
assert!(verify_hmac(&KEY, &iv, ciphertext, &hmac_tag));
|
assert!(verify_hmac(&TEST_KEY, &iv, ciphertext, &hmac_tag));
|
||||||
// Verify with wrong tag
|
// Verify with wrong tag
|
||||||
let wrong_tag = [0u8; 32];
|
let wrong_tag = [0u8; 32];
|
||||||
assert!(!verify_hmac(&KEY, &iv, ciphertext, &wrong_tag));
|
assert!(!verify_hmac(&TEST_KEY, &iv, ciphertext, &wrong_tag));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@@ -4,9 +4,16 @@
|
|||||||
//! during 03-RESEARCH. These tests use fixed IVs for deterministic output.
|
//! during 03-RESEARCH. These tests use fixed IVs for deterministic output.
|
||||||
|
|
||||||
use encrypted_archive::crypto;
|
use encrypted_archive::crypto;
|
||||||
use encrypted_archive::key::KEY;
|
|
||||||
use hex_literal::hex;
|
use hex_literal::hex;
|
||||||
|
|
||||||
|
// Use the legacy hardcoded key for golden test vectors
|
||||||
|
const KEY: [u8; 32] = [
|
||||||
|
0x7A, 0x35, 0xC1, 0xD9, 0x4F, 0xE8, 0x2B, 0x6A,
|
||||||
|
0x91, 0x0D, 0xF3, 0x58, 0xBC, 0x74, 0xA6, 0x1E,
|
||||||
|
0x42, 0x8F, 0xD0, 0x63, 0xE5, 0x17, 0x9B, 0x2C,
|
||||||
|
0xFA, 0x84, 0x06, 0xCD, 0x3E, 0x79, 0xB5, 0x50,
|
||||||
|
];
|
||||||
|
|
||||||
/// AES-256-CBC encryption of "Hello" with project KEY and fixed IV.
|
/// AES-256-CBC encryption of "Hello" with project KEY and fixed IV.
|
||||||
///
|
///
|
||||||
/// Cross-verified:
|
/// Cross-verified:
|
||||||
|
|||||||
@@ -10,7 +10,17 @@ use std::fs;
|
|||||||
use std::os::unix::fs::PermissionsExt;
|
use std::os::unix::fs::PermissionsExt;
|
||||||
use tempfile::tempdir;
|
use tempfile::tempdir;
|
||||||
|
|
||||||
/// Helper: get a Command for the encrypted_archive binary.
|
/// Hex-encoded 32-byte key for test archives (matches legacy hardcoded key)
|
||||||
|
const TEST_KEY_HEX: &str = "7a35c1d94fe82b6a910df358bc74a61e428fd063e5179b2cfa8406cd3e79b550";
|
||||||
|
|
||||||
|
/// Helper: get a Command for the encrypted_archive binary with --key pre-set.
|
||||||
|
fn cmd_with_key() -> Command {
|
||||||
|
let mut c = Command::new(assert_cmd::cargo::cargo_bin!("encrypted_archive"));
|
||||||
|
c.args(["--key", TEST_KEY_HEX]);
|
||||||
|
c
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Helper: get a Command for the encrypted_archive binary without a key.
|
||||||
fn cmd() -> Command {
|
fn cmd() -> Command {
|
||||||
Command::new(assert_cmd::cargo::cargo_bin!("encrypted_archive"))
|
Command::new(assert_cmd::cargo::cargo_bin!("encrypted_archive"))
|
||||||
}
|
}
|
||||||
@@ -25,12 +35,12 @@ fn test_roundtrip_single_text_file() {
|
|||||||
|
|
||||||
fs::write(&input_file, b"Hello").unwrap();
|
fs::write(&input_file, b"Hello").unwrap();
|
||||||
|
|
||||||
cmd()
|
cmd_with_key()
|
||||||
.args(["pack", input_file.to_str().unwrap(), "-o", archive.to_str().unwrap()])
|
.args(["pack", input_file.to_str().unwrap(), "-o", archive.to_str().unwrap()])
|
||||||
.assert()
|
.assert()
|
||||||
.success();
|
.success();
|
||||||
|
|
||||||
cmd()
|
cmd_with_key()
|
||||||
.args(["unpack", archive.to_str().unwrap(), "-o", output_dir.to_str().unwrap()])
|
.args(["unpack", archive.to_str().unwrap(), "-o", output_dir.to_str().unwrap()])
|
||||||
.assert()
|
.assert()
|
||||||
.success();
|
.success();
|
||||||
@@ -51,7 +61,7 @@ fn test_roundtrip_multiple_files() {
|
|||||||
fs::write(&text_file, b"Some text content").unwrap();
|
fs::write(&text_file, b"Some text content").unwrap();
|
||||||
fs::write(&binary_file, &[0x42u8; 256]).unwrap();
|
fs::write(&binary_file, &[0x42u8; 256]).unwrap();
|
||||||
|
|
||||||
cmd()
|
cmd_with_key()
|
||||||
.args([
|
.args([
|
||||||
"pack",
|
"pack",
|
||||||
text_file.to_str().unwrap(),
|
text_file.to_str().unwrap(),
|
||||||
@@ -62,7 +72,7 @@ fn test_roundtrip_multiple_files() {
|
|||||||
.assert()
|
.assert()
|
||||||
.success();
|
.success();
|
||||||
|
|
||||||
cmd()
|
cmd_with_key()
|
||||||
.args(["unpack", archive.to_str().unwrap(), "-o", output_dir.to_str().unwrap()])
|
.args(["unpack", archive.to_str().unwrap(), "-o", output_dir.to_str().unwrap()])
|
||||||
.assert()
|
.assert()
|
||||||
.success();
|
.success();
|
||||||
@@ -88,12 +98,12 @@ fn test_roundtrip_empty_file() {
|
|||||||
|
|
||||||
fs::write(&input_file, b"").unwrap();
|
fs::write(&input_file, b"").unwrap();
|
||||||
|
|
||||||
cmd()
|
cmd_with_key()
|
||||||
.args(["pack", input_file.to_str().unwrap(), "-o", archive.to_str().unwrap()])
|
.args(["pack", input_file.to_str().unwrap(), "-o", archive.to_str().unwrap()])
|
||||||
.assert()
|
.assert()
|
||||||
.success();
|
.success();
|
||||||
|
|
||||||
cmd()
|
cmd_with_key()
|
||||||
.args(["unpack", archive.to_str().unwrap(), "-o", output_dir.to_str().unwrap()])
|
.args(["unpack", archive.to_str().unwrap(), "-o", output_dir.to_str().unwrap()])
|
||||||
.assert()
|
.assert()
|
||||||
.success();
|
.success();
|
||||||
@@ -114,12 +124,12 @@ fn test_roundtrip_cyrillic_filename() {
|
|||||||
let content = "Содержимое".as_bytes();
|
let content = "Содержимое".as_bytes();
|
||||||
fs::write(&input_file, content).unwrap();
|
fs::write(&input_file, content).unwrap();
|
||||||
|
|
||||||
cmd()
|
cmd_with_key()
|
||||||
.args(["pack", input_file.to_str().unwrap(), "-o", archive.to_str().unwrap()])
|
.args(["pack", input_file.to_str().unwrap(), "-o", archive.to_str().unwrap()])
|
||||||
.assert()
|
.assert()
|
||||||
.success();
|
.success();
|
||||||
|
|
||||||
cmd()
|
cmd_with_key()
|
||||||
.args(["unpack", archive.to_str().unwrap(), "-o", output_dir.to_str().unwrap()])
|
.args(["unpack", archive.to_str().unwrap(), "-o", output_dir.to_str().unwrap()])
|
||||||
.assert()
|
.assert()
|
||||||
.success();
|
.success();
|
||||||
@@ -144,7 +154,7 @@ fn test_roundtrip_large_file() {
|
|||||||
fs::write(&input_file, &data).unwrap();
|
fs::write(&input_file, &data).unwrap();
|
||||||
|
|
||||||
// Pack with --no-compress bin (skip compression for binary extension)
|
// Pack with --no-compress bin (skip compression for binary extension)
|
||||||
cmd()
|
cmd_with_key()
|
||||||
.args([
|
.args([
|
||||||
"pack",
|
"pack",
|
||||||
input_file.to_str().unwrap(),
|
input_file.to_str().unwrap(),
|
||||||
@@ -156,7 +166,7 @@ fn test_roundtrip_large_file() {
|
|||||||
.assert()
|
.assert()
|
||||||
.success();
|
.success();
|
||||||
|
|
||||||
cmd()
|
cmd_with_key()
|
||||||
.args(["unpack", archive.to_str().unwrap(), "-o", output_dir.to_str().unwrap()])
|
.args(["unpack", archive.to_str().unwrap(), "-o", output_dir.to_str().unwrap()])
|
||||||
.assert()
|
.assert()
|
||||||
.success();
|
.success();
|
||||||
@@ -179,12 +189,12 @@ fn test_roundtrip_no_compress_flag() {
|
|||||||
let data: Vec<u8> = (0..100u8).collect();
|
let data: Vec<u8> = (0..100u8).collect();
|
||||||
fs::write(&input_file, &data).unwrap();
|
fs::write(&input_file, &data).unwrap();
|
||||||
|
|
||||||
cmd()
|
cmd_with_key()
|
||||||
.args(["pack", input_file.to_str().unwrap(), "-o", archive.to_str().unwrap()])
|
.args(["pack", input_file.to_str().unwrap(), "-o", archive.to_str().unwrap()])
|
||||||
.assert()
|
.assert()
|
||||||
.success();
|
.success();
|
||||||
|
|
||||||
cmd()
|
cmd_with_key()
|
||||||
.args(["unpack", archive.to_str().unwrap(), "-o", output_dir.to_str().unwrap()])
|
.args(["unpack", archive.to_str().unwrap(), "-o", output_dir.to_str().unwrap()])
|
||||||
.assert()
|
.assert()
|
||||||
.success();
|
.success();
|
||||||
@@ -218,13 +228,13 @@ fn test_roundtrip_directory() {
|
|||||||
fs::set_permissions(&emptydir, fs::Permissions::from_mode(0o700)).unwrap();
|
fs::set_permissions(&emptydir, fs::Permissions::from_mode(0o700)).unwrap();
|
||||||
|
|
||||||
// Pack directory
|
// Pack directory
|
||||||
cmd()
|
cmd_with_key()
|
||||||
.args(["pack", testdir.to_str().unwrap(), "-o", archive.to_str().unwrap()])
|
.args(["pack", testdir.to_str().unwrap(), "-o", archive.to_str().unwrap()])
|
||||||
.assert()
|
.assert()
|
||||||
.success();
|
.success();
|
||||||
|
|
||||||
// Unpack
|
// Unpack
|
||||||
cmd()
|
cmd_with_key()
|
||||||
.args(["unpack", archive.to_str().unwrap(), "-o", output_dir.to_str().unwrap()])
|
.args(["unpack", archive.to_str().unwrap(), "-o", output_dir.to_str().unwrap()])
|
||||||
.assert()
|
.assert()
|
||||||
.success();
|
.success();
|
||||||
@@ -272,7 +282,7 @@ fn test_roundtrip_mixed_files_and_dirs() {
|
|||||||
fs::write(mydir.join("inner.txt"), b"Inner").unwrap();
|
fs::write(mydir.join("inner.txt"), b"Inner").unwrap();
|
||||||
|
|
||||||
// Pack both file and directory
|
// Pack both file and directory
|
||||||
cmd()
|
cmd_with_key()
|
||||||
.args([
|
.args([
|
||||||
"pack",
|
"pack",
|
||||||
standalone.to_str().unwrap(),
|
standalone.to_str().unwrap(),
|
||||||
@@ -284,7 +294,7 @@ fn test_roundtrip_mixed_files_and_dirs() {
|
|||||||
.success();
|
.success();
|
||||||
|
|
||||||
// Unpack
|
// Unpack
|
||||||
cmd()
|
cmd_with_key()
|
||||||
.args(["unpack", archive.to_str().unwrap(), "-o", output_dir.to_str().unwrap()])
|
.args(["unpack", archive.to_str().unwrap(), "-o", output_dir.to_str().unwrap()])
|
||||||
.assert()
|
.assert()
|
||||||
.success();
|
.success();
|
||||||
@@ -301,6 +311,7 @@ fn test_roundtrip_mixed_files_and_dirs() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Inspect shows directory info: entry type and permissions for directory entries.
|
/// Inspect shows directory info: entry type and permissions for directory entries.
|
||||||
|
/// Now requires --key to see full TOC listing.
|
||||||
#[test]
|
#[test]
|
||||||
fn test_inspect_shows_directory_info() {
|
fn test_inspect_shows_directory_info() {
|
||||||
let dir = tempdir().unwrap();
|
let dir = tempdir().unwrap();
|
||||||
@@ -310,13 +321,13 @@ fn test_inspect_shows_directory_info() {
|
|||||||
fs::create_dir_all(&testdir).unwrap();
|
fs::create_dir_all(&testdir).unwrap();
|
||||||
fs::write(testdir.join("file.txt"), b"content").unwrap();
|
fs::write(testdir.join("file.txt"), b"content").unwrap();
|
||||||
|
|
||||||
cmd()
|
cmd_with_key()
|
||||||
.args(["pack", testdir.to_str().unwrap(), "-o", archive.to_str().unwrap()])
|
.args(["pack", testdir.to_str().unwrap(), "-o", archive.to_str().unwrap()])
|
||||||
.assert()
|
.assert()
|
||||||
.success();
|
.success();
|
||||||
|
|
||||||
// Inspect and check output contains directory info
|
// Inspect with key: shows full TOC entry listing
|
||||||
cmd()
|
cmd_with_key()
|
||||||
.args(["inspect", archive.to_str().unwrap()])
|
.args(["inspect", archive.to_str().unwrap()])
|
||||||
.assert()
|
.assert()
|
||||||
.success()
|
.success()
|
||||||
@@ -325,3 +336,169 @@ fn test_inspect_shows_directory_info() {
|
|||||||
.stdout(predicate::str::contains("0755").or(predicate::str::contains("0775")))
|
.stdout(predicate::str::contains("0755").or(predicate::str::contains("0775")))
|
||||||
.stdout(predicate::str::contains("Permissions:"));
|
.stdout(predicate::str::contains("Permissions:"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ========== New tests for key input ==========
|
||||||
|
|
||||||
|
/// Key file round-trip: create a 32-byte key file, pack with --key-file, unpack with --key-file.
|
||||||
|
#[test]
|
||||||
|
fn test_key_file_roundtrip() {
|
||||||
|
let dir = tempdir().unwrap();
|
||||||
|
let input_file = dir.path().join("data.txt");
|
||||||
|
let key_file = dir.path().join("test.key");
|
||||||
|
let archive = dir.path().join("archive.bin");
|
||||||
|
let output_dir = dir.path().join("output");
|
||||||
|
|
||||||
|
fs::write(&input_file, b"Key file test data").unwrap();
|
||||||
|
|
||||||
|
// Write a 32-byte key file (raw bytes)
|
||||||
|
let key_bytes: [u8; 32] = [
|
||||||
|
0x7A, 0x35, 0xC1, 0xD9, 0x4F, 0xE8, 0x2B, 0x6A,
|
||||||
|
0x91, 0x0D, 0xF3, 0x58, 0xBC, 0x74, 0xA6, 0x1E,
|
||||||
|
0x42, 0x8F, 0xD0, 0x63, 0xE5, 0x17, 0x9B, 0x2C,
|
||||||
|
0xFA, 0x84, 0x06, 0xCD, 0x3E, 0x79, 0xB5, 0x50,
|
||||||
|
];
|
||||||
|
fs::write(&key_file, key_bytes).unwrap();
|
||||||
|
|
||||||
|
// Pack with --key-file
|
||||||
|
cmd()
|
||||||
|
.args([
|
||||||
|
"--key-file", key_file.to_str().unwrap(),
|
||||||
|
"pack", input_file.to_str().unwrap(), "-o", archive.to_str().unwrap(),
|
||||||
|
])
|
||||||
|
.assert()
|
||||||
|
.success();
|
||||||
|
|
||||||
|
// Unpack with --key-file
|
||||||
|
cmd()
|
||||||
|
.args([
|
||||||
|
"--key-file", key_file.to_str().unwrap(),
|
||||||
|
"unpack", archive.to_str().unwrap(), "-o", output_dir.to_str().unwrap(),
|
||||||
|
])
|
||||||
|
.assert()
|
||||||
|
.success();
|
||||||
|
|
||||||
|
let extracted = fs::read(output_dir.join("data.txt")).unwrap();
|
||||||
|
assert_eq!(extracted, b"Key file test data");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Wrong key: pack with one key, try unpack with different key, expect HMAC failure.
|
||||||
|
#[test]
|
||||||
|
fn test_rejects_wrong_key() {
|
||||||
|
let dir = tempdir().unwrap();
|
||||||
|
let input_file = dir.path().join("secret.txt");
|
||||||
|
let archive = dir.path().join("archive.bin");
|
||||||
|
let output_dir = dir.path().join("output");
|
||||||
|
|
||||||
|
fs::write(&input_file, b"Secret data").unwrap();
|
||||||
|
|
||||||
|
// Pack with the test key
|
||||||
|
cmd_with_key()
|
||||||
|
.args(["pack", input_file.to_str().unwrap(), "-o", archive.to_str().unwrap()])
|
||||||
|
.assert()
|
||||||
|
.success();
|
||||||
|
|
||||||
|
// Try to unpack with a different key (all zeros).
|
||||||
|
// The wrong key causes TOC decryption to fail (invalid padding) or HMAC verification
|
||||||
|
// to fail on individual files, depending on where the decryption error surfaces first.
|
||||||
|
let wrong_key = "0000000000000000000000000000000000000000000000000000000000000000";
|
||||||
|
cmd()
|
||||||
|
.args([
|
||||||
|
"--key", wrong_key,
|
||||||
|
"unpack", archive.to_str().unwrap(), "-o", output_dir.to_str().unwrap(),
|
||||||
|
])
|
||||||
|
.assert()
|
||||||
|
.failure()
|
||||||
|
.stderr(
|
||||||
|
predicate::str::contains("HMAC")
|
||||||
|
.or(predicate::str::contains("verification"))
|
||||||
|
.or(predicate::str::contains("Decryption failed"))
|
||||||
|
.or(predicate::str::contains("wrong key"))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Bad hex: --key with too-short hex string should produce a clear error.
|
||||||
|
#[test]
|
||||||
|
fn test_rejects_bad_hex() {
|
||||||
|
let dir = tempdir().unwrap();
|
||||||
|
let input_file = dir.path().join("data.txt");
|
||||||
|
let archive = dir.path().join("archive.bin");
|
||||||
|
|
||||||
|
fs::write(&input_file, b"data").unwrap();
|
||||||
|
|
||||||
|
cmd()
|
||||||
|
.args([
|
||||||
|
"--key", "abcd",
|
||||||
|
"pack", input_file.to_str().unwrap(), "-o", archive.to_str().unwrap(),
|
||||||
|
])
|
||||||
|
.assert()
|
||||||
|
.failure()
|
||||||
|
.stderr(predicate::str::contains("32 bytes").or(predicate::str::contains("hex")));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Missing key: running pack without any key arg should produce a clear error.
|
||||||
|
#[test]
|
||||||
|
fn test_rejects_missing_key() {
|
||||||
|
let dir = tempdir().unwrap();
|
||||||
|
let input_file = dir.path().join("data.txt");
|
||||||
|
let archive = dir.path().join("archive.bin");
|
||||||
|
|
||||||
|
fs::write(&input_file, b"data").unwrap();
|
||||||
|
|
||||||
|
cmd()
|
||||||
|
.args(["pack", input_file.to_str().unwrap(), "-o", archive.to_str().unwrap()])
|
||||||
|
.assert()
|
||||||
|
.failure()
|
||||||
|
.stderr(predicate::str::contains("required for pack"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Inspect without key: should succeed and show header metadata but NOT entry listing.
|
||||||
|
#[test]
|
||||||
|
fn test_inspect_without_key() {
|
||||||
|
let dir = tempdir().unwrap();
|
||||||
|
let input_file = dir.path().join("data.txt");
|
||||||
|
let archive = dir.path().join("archive.bin");
|
||||||
|
|
||||||
|
fs::write(&input_file, b"Hello inspect").unwrap();
|
||||||
|
|
||||||
|
// Pack with key
|
||||||
|
cmd_with_key()
|
||||||
|
.args(["pack", input_file.to_str().unwrap(), "-o", archive.to_str().unwrap()])
|
||||||
|
.assert()
|
||||||
|
.success();
|
||||||
|
|
||||||
|
// Inspect without key: should show header metadata, print TOC encrypted message
|
||||||
|
cmd()
|
||||||
|
.args(["inspect", archive.to_str().unwrap()])
|
||||||
|
.assert()
|
||||||
|
.success()
|
||||||
|
.stdout(predicate::str::contains("Version:"))
|
||||||
|
.stdout(predicate::str::contains("Flags:"))
|
||||||
|
.stdout(predicate::str::contains("Entries:"))
|
||||||
|
.stdout(predicate::str::contains("TOC is encrypted, provide a key to see entry listing"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Inspect with key: should succeed and show full TOC entry listing.
|
||||||
|
#[test]
|
||||||
|
fn test_inspect_with_key() {
|
||||||
|
let dir = tempdir().unwrap();
|
||||||
|
let input_file = dir.path().join("data.txt");
|
||||||
|
let archive = dir.path().join("archive.bin");
|
||||||
|
|
||||||
|
fs::write(&input_file, b"Hello inspect with key").unwrap();
|
||||||
|
|
||||||
|
// Pack with key
|
||||||
|
cmd_with_key()
|
||||||
|
.args(["pack", input_file.to_str().unwrap(), "-o", archive.to_str().unwrap()])
|
||||||
|
.assert()
|
||||||
|
.success();
|
||||||
|
|
||||||
|
// Inspect with key: should show full entry listing
|
||||||
|
cmd_with_key()
|
||||||
|
.args(["inspect", archive.to_str().unwrap()])
|
||||||
|
.assert()
|
||||||
|
.success()
|
||||||
|
.stdout(predicate::str::contains("Version:"))
|
||||||
|
.stdout(predicate::str::contains("data.txt"))
|
||||||
|
.stdout(predicate::str::contains("Original:"))
|
||||||
|
.stdout(predicate::str::contains("SHA-256:"));
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user