From c29841a5e7321c2d0bc87e3aedb87144761a0d08 Mon Sep 17 00:00:00 2001 From: overtrue Date: Tue, 27 May 2025 22:44:30 +0800 Subject: [PATCH] feat: add comprehensive test coverage for utils certs module --- Cargo.lock | 1 + crates/utils/Cargo.toml | 3 + crates/utils/src/certs.rs | 262 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 266 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index 0ee1154f..dee2c3b2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7485,6 +7485,7 @@ dependencies = [ "rustls 0.23.27", "rustls-pemfile", "rustls-pki-types", + "tempfile", "tracing", ] diff --git a/crates/utils/Cargo.toml b/crates/utils/Cargo.toml index 6641a2a1..4b24d54b 100644 --- a/crates/utils/Cargo.toml +++ b/crates/utils/Cargo.toml @@ -14,6 +14,9 @@ rustls-pemfile = { workspace = true, optional = true } rustls-pki-types = { workspace = true, optional = true } tracing = { workspace = true } +[dev-dependencies] +tempfile = { workspace = true } + [lints] workspace = true diff --git a/crates/utils/src/certs.rs b/crates/utils/src/certs.rs index 021c5915..0a7bd806 100644 --- a/crates/utils/src/certs.rs +++ b/crates/utils/src/certs.rs @@ -184,3 +184,265 @@ pub fn create_multi_cert_resolver( default_cert, }) } + +#[cfg(test)] +mod tests { + use super::*; + use std::fs; + use tempfile::TempDir; + + #[test] + fn test_certs_error_function() { + let error_msg = "Test error message"; + let error = certs_error(error_msg.to_string()); + + assert_eq!(error.kind(), std::io::ErrorKind::Other); + assert_eq!(error.to_string(), error_msg); + } + + #[test] + fn test_load_certs_file_not_found() { + let result = load_certs("non_existent_file.pem"); + assert!(result.is_err()); + + let error = result.unwrap_err(); + assert_eq!(error.kind(), std::io::ErrorKind::Other); + assert!(error.to_string().contains("failed to open")); + } + + #[test] + fn test_load_private_key_file_not_found() { + let result = load_private_key("non_existent_key.pem"); + assert!(result.is_err()); + + let error = result.unwrap_err(); + assert_eq!(error.kind(), std::io::ErrorKind::Other); + assert!(error.to_string().contains("failed to open")); + } + + #[test] + fn test_load_certs_empty_file() { + let temp_dir = TempDir::new().unwrap(); + let cert_path = temp_dir.path().join("empty.pem"); + fs::write(&cert_path, "").unwrap(); + + let result = load_certs(cert_path.to_str().unwrap()); + assert!(result.is_err()); + + let error = result.unwrap_err(); + assert!(error.to_string().contains("No valid certificate was found")); + } + + #[test] + fn test_load_certs_invalid_format() { + let temp_dir = TempDir::new().unwrap(); + let cert_path = temp_dir.path().join("invalid.pem"); + fs::write(&cert_path, "invalid certificate content").unwrap(); + + let result = load_certs(cert_path.to_str().unwrap()); + assert!(result.is_err()); + + let error = result.unwrap_err(); + assert!(error.to_string().contains("No valid certificate was found")); + } + + #[test] + fn test_load_private_key_empty_file() { + let temp_dir = TempDir::new().unwrap(); + let key_path = temp_dir.path().join("empty_key.pem"); + fs::write(&key_path, "").unwrap(); + + let result = load_private_key(key_path.to_str().unwrap()); + assert!(result.is_err()); + + let error = result.unwrap_err(); + assert!(error.to_string().contains("no private key found")); + } + + #[test] + fn test_load_private_key_invalid_format() { + let temp_dir = TempDir::new().unwrap(); + let key_path = temp_dir.path().join("invalid_key.pem"); + fs::write(&key_path, "invalid private key content").unwrap(); + + let result = load_private_key(key_path.to_str().unwrap()); + assert!(result.is_err()); + + let error = result.unwrap_err(); + assert!(error.to_string().contains("no private key found")); + } + + #[test] + fn test_load_all_certs_from_directory_not_exists() { + let result = load_all_certs_from_directory("/non/existent/directory"); + assert!(result.is_err()); + + let error = result.unwrap_err(); + assert!(error.to_string().contains("does not exist or is not a directory")); + } + + #[test] + fn test_load_all_certs_from_directory_empty() { + let temp_dir = TempDir::new().unwrap(); + + let result = load_all_certs_from_directory(temp_dir.path().to_str().unwrap()); + assert!(result.is_err()); + + let error = result.unwrap_err(); + assert!(error.to_string().contains("No valid certificate/private key pair found")); + } + + #[test] + fn test_load_all_certs_from_directory_file_instead_of_dir() { + let temp_dir = TempDir::new().unwrap(); + let file_path = temp_dir.path().join("not_a_directory.txt"); + fs::write(&file_path, "content").unwrap(); + + let result = load_all_certs_from_directory(file_path.to_str().unwrap()); + assert!(result.is_err()); + + let error = result.unwrap_err(); + assert!(error.to_string().contains("does not exist or is not a directory")); + } + + #[test] + fn test_load_cert_key_pair_missing_cert() { + let temp_dir = TempDir::new().unwrap(); + let key_path = temp_dir.path().join("test_key.pem"); + fs::write(&key_path, "dummy key content").unwrap(); + + let result = load_cert_key_pair("non_existent_cert.pem", key_path.to_str().unwrap()); + assert!(result.is_err()); + } + + #[test] + fn test_load_cert_key_pair_missing_key() { + let temp_dir = TempDir::new().unwrap(); + let cert_path = temp_dir.path().join("test_cert.pem"); + fs::write(&cert_path, "dummy cert content").unwrap(); + + let result = load_cert_key_pair(cert_path.to_str().unwrap(), "non_existent_key.pem"); + assert!(result.is_err()); + } + + #[test] + fn test_create_multi_cert_resolver_empty_map() { + let empty_map = HashMap::new(); + let result = create_multi_cert_resolver(empty_map); + + // Should succeed even with empty map + assert!(result.is_ok()); + } + + #[test] + fn test_error_message_formatting() { + let test_cases = vec![ + ("file not found", "failed to open test.pem: file not found"), + ("permission denied", "failed to open key.pem: permission denied"), + ("invalid format", "certificate file cert.pem format error:invalid format"), + ]; + + for (input, _expected_pattern) in test_cases { + let error1 = certs_error(format!("failed to open test.pem: {}", input)); + assert!(error1.to_string().contains(input)); + + let error2 = certs_error(format!("failed to open key.pem: {}", input)); + assert!(error2.to_string().contains(input)); + } + } + + #[test] + fn test_path_handling_edge_cases() { + // Test with various path formats + let path_cases = vec![ + "", // Empty path + ".", // Current directory + "..", // Parent directory + "/", // Root directory (Unix) + "relative/path", // Relative path + "/absolute/path", // Absolute path + ]; + + for path in path_cases { + let result = load_all_certs_from_directory(path); + // All should fail since these are not valid cert directories + assert!(result.is_err()); + } + } + + #[test] + fn test_filename_constants_consistency() { + // Test that the constants match expected values + assert_eq!(RUSTFS_TLS_CERT, "rustfs_cert.pem"); + assert_eq!(RUSTFS_TLS_KEY, "rustfs_key.pem"); + + // Test that constants are not empty + assert!(!RUSTFS_TLS_CERT.is_empty()); + assert!(!RUSTFS_TLS_KEY.is_empty()); + + // Test that constants have proper extensions + assert!(RUSTFS_TLS_CERT.ends_with(".pem")); + assert!(RUSTFS_TLS_KEY.ends_with(".pem")); + } + + #[test] + fn test_directory_structure_validation() { + let temp_dir = TempDir::new().unwrap(); + + // Create a subdirectory without certificates + let sub_dir = temp_dir.path().join("example.com"); + fs::create_dir(&sub_dir).unwrap(); + + // Should fail because no certificates found + let result = load_all_certs_from_directory(temp_dir.path().to_str().unwrap()); + assert!(result.is_err()); + assert!(result.unwrap_err().to_string().contains("No valid certificate/private key pair found")); + } + + #[test] + fn test_unicode_path_handling() { + let temp_dir = TempDir::new().unwrap(); + + // Create directory with Unicode characters + let unicode_dir = temp_dir.path().join("测试目录"); + fs::create_dir(&unicode_dir).unwrap(); + + let result = load_all_certs_from_directory(unicode_dir.to_str().unwrap()); + assert!(result.is_err()); + assert!(result.unwrap_err().to_string().contains("No valid certificate/private key pair found")); + } + + #[test] + fn test_concurrent_access_safety() { + use std::sync::Arc; + use std::thread; + + let temp_dir = TempDir::new().unwrap(); + let dir_path = Arc::new(temp_dir.path().to_string_lossy().to_string()); + + let handles: Vec<_> = (0..5).map(|_| { + let path = Arc::clone(&dir_path); + thread::spawn(move || { + let result = load_all_certs_from_directory(&path); + // All should fail since directory is empty + assert!(result.is_err()); + }) + }).collect(); + + for handle in handles { + handle.join().expect("Thread should complete successfully"); + } + } + + #[test] + fn test_memory_efficiency() { + // Test that error types are reasonably sized + use std::mem; + + let error = certs_error("test".to_string()); + let error_size = mem::size_of_val(&error); + + // Error should not be excessively large + assert!(error_size < 1024, "Error size should be reasonable, got {} bytes", error_size); + } +}