#[cfg(test)]
mod error_handling_tests {
use keeper_secrets_manager_core::crypto::CryptoUtils;
use keeper_secrets_manager_core::custom_error::KSMRError;
use keeper_secrets_manager_core::utils;
#[test]
fn test_invalid_base64_error() {
let invalid_base64 = "This is not valid base64!@#$";
let result = utils::url_safe_str_to_bytes(invalid_base64);
assert!(result.is_err());
}
#[test]
fn test_decrypt_invalid_data() {
let key = utils::generate_random_bytes(32);
let invalid_data = vec![1, 2, 3];
let result = CryptoUtils::decrypt_aes_cbc(&invalid_data, &key);
assert!(result.is_err());
}
#[test]
fn test_decrypt_wrong_key_length() {
let wrong_key = vec![1, 2, 3, 4, 5]; let data = vec![0u8; 48];
let result = CryptoUtils::decrypt_aes_cbc(&data, &wrong_key);
assert!(result.is_err());
}
#[test]
fn test_encrypt_wrong_key_length() {
let wrong_key = vec![1, 2, 3, 4]; let data = b"Test data";
let result = CryptoUtils::encrypt_aes_cbc(data, &wrong_key, None);
assert!(result.is_err());
}
#[test]
fn test_decrypt_empty_data() {
let key = utils::generate_random_bytes(32);
let empty_data = vec![];
let result = CryptoUtils::decrypt_aes_cbc(&empty_data, &key);
assert!(result.is_err());
}
#[test]
fn test_encrypt_empty_key() {
let empty_key = vec![];
let data = b"Test data";
let result = CryptoUtils::encrypt_aes_cbc(data, &empty_key, None);
assert!(result.is_err());
}
#[test]
fn test_base64_decode_corrupted() {
let corrupted = "ABC!!!XYZ";
let result = utils::url_safe_str_to_bytes(corrupted);
assert!(result.is_err());
}
#[test]
fn test_url_safe_base64_invalid_chars() {
let invalid = "Contains/Slashes+AndPlus=";
let result = utils::url_safe_str_to_bytes(invalid);
assert!(result.is_ok() || result.is_err());
}
#[test]
fn test_string_to_bytes_empty() {
let empty_string = "";
let result = utils::string_to_bytes(empty_string);
assert_eq!(result, Vec::<u8>::new());
}
#[test]
fn test_bytes_to_string_roundtrip() {
let original = "Test string with UTF-8: 世界 Привет Ù…Ø±ØØ¨Ø§";
let bytes = utils::string_to_bytes(original);
let result = utils::bytes_to_string(&bytes);
assert!(result.is_ok());
assert_eq!(result.unwrap(), original);
}
#[test]
fn test_invalid_utf8_to_string() {
let invalid_utf8 = vec![0xFF, 0xFE, 0xFD]; let result = utils::bytes_to_string(&invalid_utf8);
assert!(result.is_err());
}
#[test]
fn test_totp_invalid_url() {
let invalid_url = "not-a-valid-url";
let result = utils::get_totp_code(invalid_url);
assert!(result.is_err());
}
#[test]
fn test_totp_non_otpauth_url() {
let http_url = "https://example.com/secret";
let result = utils::get_totp_code(http_url);
assert!(result.is_err());
}
#[test]
fn test_totp_empty_url() {
let empty_url = "";
let result = utils::get_totp_code(empty_url);
assert!(result.is_err());
}
#[test]
fn test_generate_password_default() {
let result = utils::generate_password();
assert!(result.is_ok());
let password = result.unwrap();
assert!(!password.is_empty());
assert!(password.len() >= 32); }
#[test]
fn test_aes_gcm_encrypt_invalid_key() {
let wrong_key = vec![1, 2, 3]; let data = b"Test data";
let nonce = Some(vec![0u8; 12]);
let result = CryptoUtils::encrypt_aes_gcm(data, &wrong_key, nonce.as_deref());
assert!(result.is_err());
}
#[test]
fn test_bytes_to_int_insufficient() {
let insufficient_bytes = vec![1, 2]; let result = utils::bytes_to_int(&insufficient_bytes);
assert!(result.is_ok() || result.is_err());
}
#[test]
fn test_bytes_to_int_empty() {
let empty_bytes = vec![];
let result = utils::bytes_to_int(&empty_bytes);
assert!(result.is_ok() || result.is_err());
}
#[test]
fn test_url_safe_str_to_int_invalid() {
let invalid = "!!!INVALID!!!";
let result = utils::url_safe_str_to_int(invalid);
assert!(result.is_err());
}
#[test]
fn test_json_to_dict_invalid() {
let invalid_json = "{invalid json}";
let result = utils::json_to_dict(invalid_json);
assert!(result.is_none());
}
#[test]
fn test_json_to_dict_empty() {
let empty = "";
let result = utils::json_to_dict(empty);
assert!(result.is_none());
}
#[test]
fn test_dict_to_json_complex() {
use serde_json::json;
use std::collections::HashMap;
let mut dict = HashMap::new();
dict.insert("key1".to_string(), json!("value1"));
dict.insert("key2".to_string(), json!({"nested": "object"}));
dict.insert("key3".to_string(), json!([1, 2, 3]));
let result = utils::dict_to_json(&dict);
assert!(result.is_ok());
let json_str = result.unwrap();
assert!(json_str.contains("key1"));
assert!(json_str.contains("nested"));
}
#[test]
fn test_base64_to_bytes_standard() {
let standard_b64 = "SGVsbG8rV29ybGQv"; let result = utils::base64_to_bytes(standard_b64);
assert!(result.is_ok());
}
#[test]
fn test_string_with_null_bytes() {
let data_with_null = vec![72, 101, 108, 108, 111, 0, 87, 111, 114, 108, 100]; let result = utils::bytes_to_string(&data_with_null);
assert!(result.is_ok() || result.is_err());
}
#[test]
fn test_encrypt_maximum_data() {
let key = utils::generate_random_bytes(32);
let large_data = vec![42u8; 1024 * 1024];
let result = CryptoUtils::encrypt_aes_cbc(&large_data, &key, None);
assert!(result.is_ok());
}
#[test]
fn test_decrypt_maximum_data() {
let key = utils::generate_random_bytes(32);
let test_data = b"Test data for encryption roundtrip validation";
let large_data = test_data.repeat(20000);
let encrypted = CryptoUtils::encrypt_aes_cbc(&large_data, &key, None).unwrap();
let result = CryptoUtils::decrypt_aes_cbc(&encrypted, &key);
assert!(result.is_ok());
let decrypted = result.unwrap();
assert!(decrypted.len() >= large_data.len());
}
#[test]
fn test_error_display() {
let errors = vec![
KSMRError::InvalidBase64,
KSMRError::DecodedBytesTooShort,
KSMRError::NotImplemented("feature".to_string()),
KSMRError::InvalidLength("field".to_string()),
KSMRError::CryptoError("encryption failed".to_string()),
KSMRError::CustomError("custom error".to_string()),
KSMRError::FileError("file not found".to_string()),
];
for error in errors {
let error_string = format!("{}", error);
assert!(!error_string.is_empty());
}
}
#[test]
fn test_error_propagation() {
fn level3() -> Result<(), KSMRError> {
Err(KSMRError::CustomError("Level 3 error".to_string()))
}
fn level2() -> Result<(), KSMRError> {
level3()?;
Ok(())
}
fn level1() -> Result<(), KSMRError> {
level2()?;
Ok(())
}
let result = level1();
assert!(result.is_err());
if let Err(KSMRError::CustomError(msg)) = result {
assert_eq!(msg, "Level 3 error");
} else {
panic!("Expected CustomError");
}
}
#[test]
fn test_crypto_error_message() {
let error = KSMRError::CryptoError("AES decryption failed: invalid padding".to_string());
let message = format!("{}", error);
assert!(message.contains("AES decryption"));
assert!(message.contains("padding"));
}
#[test]
fn test_storage_error_scenarios() {
let errors = vec![
KSMRError::StorageError("Failed to read config".to_string()),
KSMRError::StorageError("Failed to write config".to_string()),
KSMRError::StorageError("Config file not found".to_string()),
];
for error in errors {
assert!(format!("{}", error).contains("Storage Error"));
}
}
#[test]
fn test_cache_error_scenarios() {
let errors = vec![
KSMRError::CacheSaveError("Disk full".to_string()),
KSMRError::CacheRetrieveError("Corrupted cache".to_string()),
KSMRError::CachePurgeError("Permission denied".to_string()),
];
for error in errors {
let msg = format!("{}", error);
assert!(msg.contains("Error"));
}
}
#[test]
fn test_serialization_error() {
let error = KSMRError::SerializationError("Invalid JSON structure".to_string());
let message = format!("{}", error);
assert!(message.contains("JSON"));
}
#[test]
fn test_http_error() {
let error = KSMRError::HTTPError("Connection timeout".to_string());
let message = format!("{}", error);
assert!(message.contains("Connection timeout"));
}
#[test]
fn test_notation_error() {
let error = KSMRError::NotationError("Invalid notation syntax".to_string());
let message = format!("{}", error);
assert!(message.contains("notation"));
}
#[test]
fn test_totp_error() {
let error = KSMRError::TOTPError("Invalid TOTP secret".to_string());
let message = format!("{}", error);
assert!(message.contains("TOTP"));
}
#[test]
fn test_password_creation_error() {
let error = KSMRError::PasswordCreationError("No character sets provided".to_string());
let message = format!("{}", error);
assert!(message.contains("Password"));
}
#[test]
fn test_file_error() {
let error = KSMRError::FileError("File not found".to_string());
let message = format!("{}", error);
assert!(message.contains("File"));
}
#[test]
fn test_all_error_variants_creatable() {
let errors: Vec<KSMRError> = vec![
KSMRError::InvalidBase64,
KSMRError::DecodedBytesTooShort,
KSMRError::NotImplemented("test".to_string()),
KSMRError::InvalidLength("test".to_string()),
KSMRError::InsufficientBytes("test".to_string()),
KSMRError::CacheSaveError("test".to_string()),
KSMRError::CacheRetrieveError("test".to_string()),
KSMRError::CachePurgeError("test".to_string()),
KSMRError::SecretManagerCreationError("test".to_string()),
KSMRError::StorageError("test".to_string()),
KSMRError::SerializationError("test".to_string()),
KSMRError::DeserializationError("test".to_string()),
KSMRError::HTTPError("test".to_string()),
KSMRError::DataConversionError("test".to_string()),
KSMRError::CustomError("test".to_string()),
KSMRError::DecodeError("test".to_string()),
KSMRError::StringConversionError("test".to_string()),
KSMRError::CryptoError("test".to_string()),
KSMRError::RecordDataError("test".to_string()),
KSMRError::InvalidPayloadError("test".to_string()),
KSMRError::IOError("test".to_string()),
KSMRError::PathError("test".to_string()),
KSMRError::KeyNotFoundError("test".to_string()),
KSMRError::FileError("test".to_string()),
KSMRError::PasswordCreationError("test".to_string()),
KSMRError::TOTPError("test".to_string()),
KSMRError::NotationError("test".to_string()),
];
for error in errors {
let message = format!("{}", error);
assert!(!message.is_empty());
}
}
#[test]
fn test_crypto_buffer_mismatch() {
let key = utils::generate_random_bytes(32);
let short_data = vec![1, 2, 3, 4, 5];
let result = CryptoUtils::decrypt_aes_cbc(&short_data, &key);
assert!(result.is_err());
}
#[test]
fn test_encryption_various_sizes() {
let key = utils::generate_random_bytes(32);
let test_sizes = vec![16, 32, 64, 128, 256, 512, 1024];
for size in test_sizes {
let data: Vec<u8> = (0..size).map(|i| (i % 256) as u8).collect();
let encrypted = CryptoUtils::encrypt_aes_cbc(&data, &key, None);
assert!(encrypted.is_ok(), "Encryption failed for size {}", size);
let decrypted = CryptoUtils::decrypt_aes_cbc(&encrypted.unwrap(), &key);
assert!(decrypted.is_ok(), "Decryption failed for size {}", size);
let result_data = decrypted.unwrap();
assert!(
!result_data.is_empty(),
"Decrypted data empty for size {}",
size
);
}
}
#[test]
fn test_corrupt_record_key_filtered_out() {
use keeper_secrets_manager_core::dto::Record;
use serde_json::json;
use std::collections::HashMap;
let mut record_dict = HashMap::new();
record_dict.insert("recordUid".to_string(), json!("test-uid-corrupt-key"));
let corrupt_key_bytes = vec![1u8, 2, 3, 4, 5]; let corrupt_key_b64 = utils::bytes_to_base64(&corrupt_key_bytes);
record_dict.insert("recordKey".to_string(), json!(corrupt_key_b64));
record_dict.insert("data".to_string(), json!("dmFsaWRfZGF0YQ=="));
let secret_key = utils::generate_random_bytes(32);
let result = Record::new_from_json(record_dict, &secret_key, None);
assert!(
result.is_err(),
"Expected error for corrupt record key, got Ok with blank data"
);
if let Err(KSMRError::CryptoError(msg)) = result {
assert!(
msg.contains("Error decrypting record key"),
"Error message should mention record key decryption"
);
assert!(
msg.contains("test-uid-corrupt-key"),
"Error message should include record UID"
);
} else {
panic!("Expected CryptoError, got different error type");
}
}
#[test]
fn test_corrupt_record_data_filtered_out() {
use keeper_secrets_manager_core::dto::Record;
use serde_json::json;
use std::collections::HashMap;
let mut record_dict = HashMap::new();
record_dict.insert("recordUid".to_string(), json!("test-uid-corrupt-data"));
let corrupt_data = vec![1u8, 2, 3, 4, 5]; let corrupt_data_b64 = utils::bytes_to_base64(&corrupt_data);
record_dict.insert("data".to_string(), json!(corrupt_data_b64));
let secret_key = utils::generate_random_bytes(32);
let result = Record::new_from_json(record_dict, &secret_key, None);
assert!(
result.is_err(),
"Expected error for corrupt record data, got Ok with blank data"
);
if let Err(KSMRError::CryptoError(msg)) = result {
assert!(
msg.contains("Error decrypting record data"),
"Error message should mention record data decryption"
);
assert!(
msg.contains("test-uid-corrupt-data"),
"Error message should include record UID"
);
} else {
panic!("Expected CryptoError, got different error type");
}
}
#[test]
fn test_valid_record_without_encryption_still_works() {
use keeper_secrets_manager_core::dto::Record;
use serde_json::json;
use std::collections::HashMap;
let mut record_dict = HashMap::new();
record_dict.insert("recordUid".to_string(), json!("test-uid-valid"));
record_dict.insert("recordKey".to_string(), json!(""));
let secret_key = utils::generate_random_bytes(32);
let result = Record::new_from_json(record_dict, &secret_key, None);
assert!(
result.is_ok(),
"Valid record structure should not return error"
);
let record = result.unwrap();
assert_eq!(record.uid, "test-uid-valid");
}
}