diff --git a/src/utils.rs b/src/utils.rs index 62542b9..977dfe0 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -104,3 +104,185 @@ pub fn validate_ssh_key(key: &Option) -> Result<(), DrawerError> { .map_err(|_| DrawerError::KeyNotValidSsh)?; Ok(()) } + +#[cfg(test)] +mod tests { + use super::*; + + const TEST_KEY: &[u8] = include_bytes!("../tests/fixtures/test.key"); + const TEST_DRAWER: &[u8] = include_bytes!("../tests/fixtures/test.drawer"); + + fn temp_path(suffix: &str) -> PathBuf { + let nanos = std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap() + .subsec_nanos(); + std::env::temp_dir().join(format!("drawer_test_{nanos}{suffix}")) + } + + fn write_fixture(data: &[u8], suffix: &str) -> PathBuf { + let p = temp_path(suffix); + std::fs::write(&p, data).unwrap(); + p + } + + fn make_test_archive() -> Vec { + use flate2::{write::GzEncoder, Compression}; + let mut buf = Vec::new(); + let gz = GzEncoder::new(&mut buf, Compression::default()); + let mut ar = tar::Builder::new(gz); + let data = b"hello archive"; + let mut header = tar::Header::new_gnu(); + header.set_size(data.len() as u64); + header.set_mode(0o644); + header.set_cksum(); + ar.append_data(&mut header, "hello.txt", data.as_ref()).unwrap(); + ar.into_inner().unwrap().finish().unwrap(); + buf + } + + // --- parent_or_dot --- + + #[test] + fn parent_or_dot_with_parent() { + assert_eq!(parent_or_dot(Path::new("foo/bar")), Path::new("foo")); + } + + #[test] + fn parent_or_dot_no_directory_component() { + assert_eq!(parent_or_dot(Path::new("foo")), Path::new(".")); + } + + #[test] + fn parent_or_dot_empty_path() { + assert_eq!(parent_or_dot(Path::new("")), Path::new(".")); + } + + // --- default_target --- + + #[test] + fn default_target_single_file() { + let result = default_target(Path::new("unused.drawer"), Some(Path::new("archive/file.txt"))); + assert_eq!(result, PathBuf::from("file.txt")); + } + + #[test] + fn default_target_directory_stem() { + let result = default_target(Path::new("myproject.drawer"), None); + assert_eq!(result, PathBuf::from("myproject")); + } + + #[test] + fn default_target_directory_stem_nested_drawer() { + let result = default_target(Path::new("a/b/myproject.drawer"), None); + assert_eq!(result, PathBuf::from("myproject")); + } + + // --- validate_drawer_extension --- + + #[test] + fn validate_extension_valid() { + assert!(validate_drawer_extension(Path::new("foo.drawer")).is_ok()); + } + + #[test] + fn validate_extension_wrong() { + assert!(validate_drawer_extension(Path::new("foo.tar")).is_err()); + } + + #[test] + fn validate_extension_missing() { + assert!(validate_drawer_extension(Path::new("foo")).is_err()); + } + + // --- key_path --- + + #[test] + fn key_path_explicit() { + let result = key_path(&Some(PathBuf::from("/some/key"))); + assert_eq!(result.unwrap(), PathBuf::from("/some/key")); + } + + #[test] + fn key_path_none_no_env_var() { + // Remove the env var for this check; if it happens to be set in the + // test environment this test would be misleading, so we clear it first. + unsafe { std::env::remove_var("DRAWER_KEY") }; + assert!(key_path(&None).is_err()); + } + + // --- validate_drawer_file_exists --- + + #[test] + fn validate_file_exists_present() { + assert!(validate_drawer_file_exists(Path::new("Cargo.toml")).is_ok()); + } + + #[test] + fn validate_file_exists_missing() { + assert!(validate_drawer_file_exists( + Path::new("/tmp/drawer_definitely_missing_xyz123.drawer") + ).is_err()); + } + + // --- archive_reader_for --- + + #[test] + fn archive_reader_for_reads_entry() { + let data = make_test_archive(); + let mut archive = archive_reader_for(&data); + let entries: Vec<_> = archive.entries().unwrap() + .filter_map(|e| e.ok()) + .collect(); + assert_eq!(entries.len(), 1); + assert_eq!(entries[0].path().unwrap().as_ref(), Path::new("hello.txt")); + } + + // --- validate_ssh_key --- + + #[test] + fn validate_ssh_key_valid() { + let key = write_fixture(TEST_KEY, ".key"); + assert!(validate_ssh_key(&Some(key)).is_ok()); + } + + #[test] + fn validate_ssh_key_garbage() { + let key = write_fixture(b"not a real key", ".key"); + assert!(validate_ssh_key(&Some(key)).is_err()); + } + + #[test] + fn validate_ssh_key_no_path() { + unsafe { std::env::remove_var("DRAWER_KEY") }; + assert!(validate_ssh_key(&None).is_err()); + } + + // --- identity --- + + #[test] + fn identity_loads_valid_key() { + let key = write_fixture(TEST_KEY, ".key"); + let result = identity(&Some(key.clone()), Path::new("dummy.drawer")); + let (returned_path, _id) = result.unwrap(); + assert_eq!(returned_path, key); + } + + // --- encrypt / decrypt --- + + #[test] + fn encrypt_decrypt_round_trip() { + let key = write_fixture(TEST_KEY, ".key"); + let plaintext = b"round trip test"; + let ciphertext = encrypt(plaintext, &Some(key.clone()), Path::new("dummy.drawer")).unwrap(); + let decrypted = decrypt(ciphertext, &Some(key), Path::new("dummy.drawer")).unwrap(); + assert_eq!(decrypted, plaintext); + } + + #[test] + fn decrypt_test_drawer_fixture() { + let key = write_fixture(TEST_KEY, ".key"); + let result = decrypt(TEST_DRAWER.to_vec(), &Some(key), Path::new("test.drawer")); + assert!(result.is_ok()); + } +} diff --git a/test.sh b/test.sh index 567495f..8c7ac80 100755 --- a/test.sh +++ b/test.sh @@ -2,7 +2,7 @@ set -euo pipefail DRAWER=./target/debug/drawer -TEST_DIR="$(cd "$(dirname "$0")/test" && pwd)" +TEST_DIR="$(cd "$(dirname "$0")/tests/testcontents" && pwd)" WORK="$TMPDIR/drawer_test_$$" mkdir -p "$WORK" diff --git a/tests/fixtures/test.drawer b/tests/fixtures/test.drawer new file mode 100644 index 0000000..bf1d53d Binary files /dev/null and b/tests/fixtures/test.drawer differ diff --git a/tests/fixtures/test.key b/tests/fixtures/test.key new file mode 100644 index 0000000..232ae20 --- /dev/null +++ b/tests/fixtures/test.key @@ -0,0 +1,7 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW +QyNTUxOQAAACDX3jE2g37mBoQTky3PYwlrgyJQ2rfBZQoEVXNDhMRl3AAAAIjd219V3dtf +VQAAAAtzc2gtZWQyNTUxOQAAACDX3jE2g37mBoQTky3PYwlrgyJQ2rfBZQoEVXNDhMRl3A +AAAEB8nkWm+dRgovs/PoFmnFPWinK757HlccvMUgXk3UMtl9feMTaDfuYGhBOTLc9jCWuD +IlDat8FlCgRVc0OExGXcAAAAAAECAwQF +-----END OPENSSH PRIVATE KEY----- diff --git a/test/file1.md b/tests/testcontents/file1.md similarity index 100% rename from test/file1.md rename to tests/testcontents/file1.md diff --git a/test/file2.md b/tests/testcontents/file2.md similarity index 100% rename from test/file2.md rename to tests/testcontents/file2.md