feat: improve test coverage and fix critical crypto bug - Translate all Chinese comments to English in utils/ip.rs and config/constants/app.rs - Add comprehensive test suite for crypto/encdec/id.rs module (14 new tests) - Fix critical bug in Argon2 key generation that was returning all-zero keys - Improve test coverage for IP utilities and configuration constants - Ensure all test cases follow English naming conventions and meaningful descriptions

This commit is contained in:
overtrue
2025-05-25 13:53:59 +08:00
parent e838f4292c
commit bc398ccf1b
4 changed files with 265 additions and 55 deletions
+25 -25
View File
@@ -96,7 +96,7 @@ mod tests {
#[test] #[test]
fn test_app_basic_constants() { fn test_app_basic_constants() {
// 测试应用基本常量 // Test application basic constants
assert_eq!(APP_NAME, "RustFs"); assert_eq!(APP_NAME, "RustFs");
assert!(!APP_NAME.is_empty(), "App name should not be empty"); assert!(!APP_NAME.is_empty(), "App name should not be empty");
assert!(!APP_NAME.contains(' '), "App name should not contain spaces"); assert!(!APP_NAME.contains(' '), "App name should not contain spaces");
@@ -110,7 +110,7 @@ mod tests {
#[test] #[test]
fn test_logging_constants() { fn test_logging_constants() {
// 测试日志相关常量 // Test logging related constants
assert_eq!(DEFAULT_LOG_LEVEL, "info"); assert_eq!(DEFAULT_LOG_LEVEL, "info");
assert!(["trace", "debug", "info", "warn", "error"].contains(&DEFAULT_LOG_LEVEL), assert!(["trace", "debug", "info", "warn", "error"].contains(&DEFAULT_LOG_LEVEL),
"Log level should be a valid tracing level"); "Log level should be a valid tracing level");
@@ -127,7 +127,7 @@ mod tests {
#[test] #[test]
fn test_environment_constants() { fn test_environment_constants() {
// 测试环境相关常量 // Test environment related constants
assert_eq!(ENVIRONMENT, "production"); assert_eq!(ENVIRONMENT, "production");
assert!(["development", "staging", "production", "test"].contains(&ENVIRONMENT), assert!(["development", "staging", "production", "test"].contains(&ENVIRONMENT),
"Environment should be a standard environment name"); "Environment should be a standard environment name");
@@ -135,7 +135,7 @@ mod tests {
#[test] #[test]
fn test_connection_constants() { fn test_connection_constants() {
// 测试连接相关常量 // Test connection related constants
assert_eq!(MAX_CONNECTIONS, 100); assert_eq!(MAX_CONNECTIONS, 100);
assert!(MAX_CONNECTIONS > 0, "Max connections should be positive"); assert!(MAX_CONNECTIONS > 0, "Max connections should be positive");
assert!(MAX_CONNECTIONS <= 10000, "Max connections should be reasonable"); assert!(MAX_CONNECTIONS <= 10000, "Max connections should be reasonable");
@@ -147,7 +147,7 @@ mod tests {
#[test] #[test]
fn test_security_constants() { fn test_security_constants() {
// 测试安全相关常量 // Test security related constants
assert_eq!(DEFAULT_ACCESS_KEY, "rustfsadmin"); assert_eq!(DEFAULT_ACCESS_KEY, "rustfsadmin");
assert!(!DEFAULT_ACCESS_KEY.is_empty(), "Access key should not be empty"); assert!(!DEFAULT_ACCESS_KEY.is_empty(), "Access key should not be empty");
assert!(DEFAULT_ACCESS_KEY.len() >= 8, "Access key should be at least 8 characters"); assert!(DEFAULT_ACCESS_KEY.len() >= 8, "Access key should be at least 8 characters");
@@ -156,14 +156,14 @@ mod tests {
assert!(!DEFAULT_SECRET_KEY.is_empty(), "Secret key should not be empty"); assert!(!DEFAULT_SECRET_KEY.is_empty(), "Secret key should not be empty");
assert!(DEFAULT_SECRET_KEY.len() >= 8, "Secret key should be at least 8 characters"); assert!(DEFAULT_SECRET_KEY.len() >= 8, "Secret key should be at least 8 characters");
// 在生产环境中,访问密钥和秘密密钥应该不同 // In production environment, access key and secret key should be different
// 这里是默认值,所以相同是可以接受的,但应该在文档中警告 // These are default values, so being the same is acceptable, but should be warned in documentation
println!("Warning: Default access key and secret key are the same. Change them in production!"); println!("Warning: Default access key and secret key are the same. Change them in production!");
} }
#[test] #[test]
fn test_file_path_constants() { fn test_file_path_constants() {
// 测试文件路径相关常量 // Test file path related constants
assert_eq!(DEFAULT_OBS_CONFIG, "./deploy/config/obs.toml"); assert_eq!(DEFAULT_OBS_CONFIG, "./deploy/config/obs.toml");
assert!(DEFAULT_OBS_CONFIG.ends_with(".toml"), "Config file should be TOML format"); assert!(DEFAULT_OBS_CONFIG.ends_with(".toml"), "Config file should be TOML format");
assert!(!DEFAULT_OBS_CONFIG.is_empty(), "Config path should not be empty"); assert!(!DEFAULT_OBS_CONFIG.is_empty(), "Config path should not be empty");
@@ -177,14 +177,14 @@ mod tests {
#[test] #[test]
fn test_port_constants() { fn test_port_constants() {
// 测试端口相关常量 // Test port related constants
assert_eq!(DEFAULT_PORT, 9000); assert_eq!(DEFAULT_PORT, 9000);
assert!(DEFAULT_PORT > 1024, "Default port should be above reserved range"); assert!(DEFAULT_PORT > 1024, "Default port should be above reserved range");
// u16类型自动保证端口在有效范围内(0-65535) // u16 type automatically ensures port is in valid range (0-65535)
assert_eq!(DEFAULT_CONSOLE_PORT, 9002); assert_eq!(DEFAULT_CONSOLE_PORT, 9002);
assert!(DEFAULT_CONSOLE_PORT > 1024, "Console port should be above reserved range"); assert!(DEFAULT_CONSOLE_PORT > 1024, "Console port should be above reserved range");
// u16类型自动保证端口在有效范围内(0-65535) // u16 type automatically ensures port is in valid range (0-65535)
assert_ne!(DEFAULT_PORT, DEFAULT_CONSOLE_PORT, assert_ne!(DEFAULT_PORT, DEFAULT_CONSOLE_PORT,
"Main port and console port should be different"); "Main port and console port should be different");
@@ -192,7 +192,7 @@ mod tests {
#[test] #[test]
fn test_address_constants() { fn test_address_constants() {
// 测试地址相关常量 // Test address related constants
assert_eq!(DEFAULT_ADDRESS, ":9000"); assert_eq!(DEFAULT_ADDRESS, ":9000");
assert!(DEFAULT_ADDRESS.starts_with(':'), "Address should start with colon"); assert!(DEFAULT_ADDRESS.starts_with(':'), "Address should start with colon");
assert!(DEFAULT_ADDRESS.contains(&DEFAULT_PORT.to_string()), assert!(DEFAULT_ADDRESS.contains(&DEFAULT_PORT.to_string()),
@@ -209,7 +209,7 @@ mod tests {
#[test] #[test]
fn test_const_str_concat_functionality() { fn test_const_str_concat_functionality() {
// 测试const_str::concat宏的功能 // Test const_str::concat macro functionality
let expected_address = format!(":{}", DEFAULT_PORT); let expected_address = format!(":{}", DEFAULT_PORT);
assert_eq!(DEFAULT_ADDRESS, expected_address); assert_eq!(DEFAULT_ADDRESS, expected_address);
@@ -219,7 +219,7 @@ mod tests {
#[test] #[test]
fn test_string_constants_validity() { fn test_string_constants_validity() {
// 测试字符串常量的有效性 // Test validity of string constants
let string_constants = [ let string_constants = [
APP_NAME, APP_NAME,
VERSION, VERSION,
@@ -244,7 +244,7 @@ mod tests {
#[test] #[test]
fn test_numeric_constants_validity() { fn test_numeric_constants_validity() {
// 测试数值常量的有效性 // Test validity of numeric constants
assert!(SAMPLE_RATIO.is_finite(), "Sample ratio should be finite"); assert!(SAMPLE_RATIO.is_finite(), "Sample ratio should be finite");
assert!(!SAMPLE_RATIO.is_nan(), "Sample ratio should not be NaN"); assert!(!SAMPLE_RATIO.is_nan(), "Sample ratio should not be NaN");
@@ -258,42 +258,42 @@ mod tests {
#[test] #[test]
fn test_security_best_practices() { fn test_security_best_practices() {
// 测试安全最佳实践 // Test security best practices
// 这些是默认值,在生产环境中应该被更改 // These are default values, should be changed in production environments
println!("Security Warning: Default credentials detected!"); println!("Security Warning: Default credentials detected!");
println!("Access Key: {}", DEFAULT_ACCESS_KEY); println!("Access Key: {}", DEFAULT_ACCESS_KEY);
println!("Secret Key: {}", DEFAULT_SECRET_KEY); println!("Secret Key: {}", DEFAULT_SECRET_KEY);
println!("These should be changed in production environments!"); println!("These should be changed in production environments!");
// 验证密钥长度符合最低安全要求 // Verify that key lengths meet minimum security requirements
assert!(DEFAULT_ACCESS_KEY.len() >= 8, "Access key should be at least 8 characters"); assert!(DEFAULT_ACCESS_KEY.len() >= 8, "Access key should be at least 8 characters");
assert!(DEFAULT_SECRET_KEY.len() >= 8, "Secret key should be at least 8 characters"); assert!(DEFAULT_SECRET_KEY.len() >= 8, "Secret key should be at least 8 characters");
// 检查默认凭据是否包含常见的不安全模式 // Check if default credentials contain common insecure patterns
let _insecure_patterns = ["admin", "password", "123456", "default"]; let _insecure_patterns = ["admin", "password", "123456", "default"];
let _access_key_lower = DEFAULT_ACCESS_KEY.to_lowercase(); let _access_key_lower = DEFAULT_ACCESS_KEY.to_lowercase();
let _secret_key_lower = DEFAULT_SECRET_KEY.to_lowercase(); let _secret_key_lower = DEFAULT_SECRET_KEY.to_lowercase();
// 注意:这里可以添加更多的安全检查逻辑 // Note: More security check logic can be added here
// 例如检查密钥是否包含不安全的模式 // For example, check if keys contain insecure patterns
} }
#[test] #[test]
fn test_configuration_consistency() { fn test_configuration_consistency() {
// 测试配置的一致性 // Test configuration consistency
// 版本一致性 // Version consistency
assert_eq!(VERSION, SERVICE_VERSION, "Application version should match service version"); assert_eq!(VERSION, SERVICE_VERSION, "Application version should match service version");
// 端口不冲突 // Port conflict check
let ports = [DEFAULT_PORT, DEFAULT_CONSOLE_PORT]; let ports = [DEFAULT_PORT, DEFAULT_CONSOLE_PORT];
let mut unique_ports = std::collections::HashSet::new(); let mut unique_ports = std::collections::HashSet::new();
for port in &ports { for port in &ports {
assert!(unique_ports.insert(port), "Port {} is duplicated", port); assert!(unique_ports.insert(port), "Port {} is duplicated", port);
} }
// 地址格式一致性 // Address format consistency
assert_eq!(DEFAULT_ADDRESS, format!(":{}", DEFAULT_PORT)); assert_eq!(DEFAULT_ADDRESS, format!(":{}", DEFAULT_PORT));
assert_eq!(DEFAULT_CONSOLE_ADDRESS, format!(":{}", DEFAULT_CONSOLE_PORT)); assert_eq!(DEFAULT_CONSOLE_ADDRESS, format!(":{}", DEFAULT_CONSOLE_PORT));
} }
+26 -26
View File
@@ -35,13 +35,13 @@ mod tests {
#[test] #[test]
fn test_get_local_ip_returns_some_ip() { fn test_get_local_ip_returns_some_ip() {
// 测试获取本地IP地址,应该返回Some值 // Test getting local IP address, should return Some value
let ip = get_local_ip(); let ip = get_local_ip();
assert!(ip.is_some(), "Should be able to get local IP address"); assert!(ip.is_some(), "Should be able to get local IP address");
if let Some(ip_addr) = ip { if let Some(ip_addr) = ip {
println!("Local IP address: {}", ip_addr); println!("Local IP address: {}", ip_addr);
// 验证返回的是有效的IP地址 // Verify that the returned IP address is valid
match ip_addr { match ip_addr {
IpAddr::V4(ipv4) => { IpAddr::V4(ipv4) => {
assert!(!ipv4.is_unspecified(), "IPv4 should not be unspecified (0.0.0.0)"); assert!(!ipv4.is_unspecified(), "IPv4 should not be unspecified (0.0.0.0)");
@@ -57,11 +57,11 @@ mod tests {
#[test] #[test]
fn test_get_local_ip_with_default_never_empty() { fn test_get_local_ip_with_default_never_empty() {
// 测试带默认值的函数永远不会返回空字符串 // Test that function with default value never returns empty string
let ip_string = get_local_ip_with_default(); let ip_string = get_local_ip_with_default();
assert!(!ip_string.is_empty(), "IP string should never be empty"); assert!(!ip_string.is_empty(), "IP string should never be empty");
// 验证返回的字符串可以解析为有效的IP地址 // Verify that the returned string can be parsed as a valid IP address
let parsed_ip: Result<IpAddr, _> = ip_string.parse(); let parsed_ip: Result<IpAddr, _> = ip_string.parse();
assert!(parsed_ip.is_ok(), "Returned string should be a valid IP address: {}", ip_string); assert!(parsed_ip.is_ok(), "Returned string should be a valid IP address: {}", ip_string);
@@ -70,38 +70,38 @@ mod tests {
#[test] #[test]
fn test_get_local_ip_with_default_fallback() { fn test_get_local_ip_with_default_fallback() {
// 测试默认值是否为127.0.0.1 // Test whether the default value is 127.0.0.1
let default_ip = IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)); let default_ip = IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1));
let ip_string = get_local_ip_with_default(); let ip_string = get_local_ip_with_default();
// 如果无法获取真实IP,应该返回默认值 // If unable to get real IP, should return default value
if get_local_ip().is_none() { if get_local_ip().is_none() {
assert_eq!(ip_string, default_ip.to_string()); assert_eq!(ip_string, default_ip.to_string());
} }
// 无论如何,返回的都应该是有效的IP地址字符串 // In any case, should always return a valid IP address string
let parsed: Result<IpAddr, _> = ip_string.parse(); let parsed: Result<IpAddr, _> = ip_string.parse();
assert!(parsed.is_ok(), "Should always return a valid IP string"); assert!(parsed.is_ok(), "Should always return a valid IP string");
} }
#[test] #[test]
fn test_ip_address_types() { fn test_ip_address_types() {
// 测试IP地址类型的识别 // Test IP address type recognition
if let Some(ip) = get_local_ip() { if let Some(ip) = get_local_ip() {
match ip { match ip {
IpAddr::V4(ipv4) => { IpAddr::V4(ipv4) => {
// 测试IPv4地址的属性 // Test IPv4 address properties
println!("IPv4 address: {}", ipv4); println!("IPv4 address: {}", ipv4);
assert!(!ipv4.is_multicast(), "Local IP should not be multicast"); assert!(!ipv4.is_multicast(), "Local IP should not be multicast");
assert!(!ipv4.is_broadcast(), "Local IP should not be broadcast"); assert!(!ipv4.is_broadcast(), "Local IP should not be broadcast");
// 检查是否为私有地址(通常本地IP是私有的) // Check if it's a private address (usually local IP is private)
let is_private = ipv4.is_private(); let is_private = ipv4.is_private();
let is_loopback = ipv4.is_loopback(); let is_loopback = ipv4.is_loopback();
println!("IPv4 is private: {}, is loopback: {}", is_private, is_loopback); println!("IPv4 is private: {}, is loopback: {}", is_private, is_loopback);
} }
IpAddr::V6(ipv6) => { IpAddr::V6(ipv6) => {
// 测试IPv6地址的属性 // Test IPv6 address properties
println!("IPv6 address: {}", ipv6); println!("IPv6 address: {}", ipv6);
assert!(!ipv6.is_multicast(), "Local IP should not be multicast"); assert!(!ipv6.is_multicast(), "Local IP should not be multicast");
@@ -114,28 +114,28 @@ mod tests {
#[test] #[test]
fn test_ip_string_format() { fn test_ip_string_format() {
// 测试IP地址字符串格式 // Test IP address string format
let ip_string = get_local_ip_with_default(); let ip_string = get_local_ip_with_default();
// 验证字符串格式 // Verify string format
assert!(!ip_string.contains(' '), "IP string should not contain spaces"); assert!(!ip_string.contains(' '), "IP string should not contain spaces");
assert!(!ip_string.is_empty(), "IP string should not be empty"); assert!(!ip_string.is_empty(), "IP string should not be empty");
// 验证可以往返转换 // Verify round-trip conversion
let parsed_ip: IpAddr = ip_string.parse().expect("Should parse as valid IP"); let parsed_ip: IpAddr = ip_string.parse().expect("Should parse as valid IP");
let back_to_string = parsed_ip.to_string(); let back_to_string = parsed_ip.to_string();
// 对于标准IP地址,往返转换应该保持一致 // For standard IP addresses, round-trip conversion should be consistent
println!("Original: {}, Parsed back: {}", ip_string, back_to_string); println!("Original: {}, Parsed back: {}", ip_string, back_to_string);
} }
#[test] #[test]
fn test_default_fallback_value() { fn test_default_fallback_value() {
// 测试默认回退值的正确性 // Test correctness of default fallback value
let default_ip = IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)); let default_ip = IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1));
assert_eq!(default_ip.to_string(), "127.0.0.1"); assert_eq!(default_ip.to_string(), "127.0.0.1");
// 验证默认IP的属性 // Verify default IP properties
if let IpAddr::V4(ipv4) = default_ip { if let IpAddr::V4(ipv4) = default_ip {
assert!(ipv4.is_loopback(), "Default IP should be loopback"); assert!(ipv4.is_loopback(), "Default IP should be loopback");
assert!(!ipv4.is_unspecified(), "Default IP should not be unspecified"); assert!(!ipv4.is_unspecified(), "Default IP should not be unspecified");
@@ -145,18 +145,18 @@ mod tests {
#[test] #[test]
fn test_consistency_between_functions() { fn test_consistency_between_functions() {
// 测试两个函数之间的一致性 // Test consistency between the two functions
let ip_option = get_local_ip(); let ip_option = get_local_ip();
let ip_string = get_local_ip_with_default(); let ip_string = get_local_ip_with_default();
match ip_option { match ip_option {
Some(ip) => { Some(ip) => {
// 如果get_local_ip返回Some,那么get_local_ip_with_default应该返回相同的IP // If get_local_ip returns Some, then get_local_ip_with_default should return the same IP
assert_eq!(ip.to_string(), ip_string, assert_eq!(ip.to_string(), ip_string,
"Both functions should return the same IP when available"); "Both functions should return the same IP when available");
} }
None => { None => {
// 如果get_local_ip返回None,那么get_local_ip_with_default应该返回默认值 // If get_local_ip returns None, then get_local_ip_with_default should return default value
assert_eq!(ip_string, "127.0.0.1", assert_eq!(ip_string, "127.0.0.1",
"Should return default value when no IP is available"); "Should return default value when no IP is available");
} }
@@ -165,13 +165,13 @@ mod tests {
#[test] #[test]
fn test_multiple_calls_consistency() { fn test_multiple_calls_consistency() {
// 测试多次调用的一致性 // Test consistency of multiple calls
let ip1 = get_local_ip(); let ip1 = get_local_ip();
let ip2 = get_local_ip(); let ip2 = get_local_ip();
let ip_str1 = get_local_ip_with_default(); let ip_str1 = get_local_ip_with_default();
let ip_str2 = get_local_ip_with_default(); let ip_str2 = get_local_ip_with_default();
// 多次调用应该返回相同的结果 // Multiple calls should return the same result
assert_eq!(ip1, ip2, "Multiple calls to get_local_ip should return same result"); assert_eq!(ip1, ip2, "Multiple calls to get_local_ip should return same result");
assert_eq!(ip_str1, ip_str2, "Multiple calls to get_local_ip_with_default should return same result"); assert_eq!(ip_str1, ip_str2, "Multiple calls to get_local_ip_with_default should return same result");
} }
@@ -179,20 +179,20 @@ mod tests {
#[cfg(feature = "integration")] #[cfg(feature = "integration")]
#[test] #[test]
fn test_network_connectivity() { fn test_network_connectivity() {
// 集成测试:验证获取的IP地址是否可用于网络连接 // Integration test: verify that the obtained IP address can be used for network connections
if let Some(ip) = get_local_ip() { if let Some(ip) = get_local_ip() {
match ip { match ip {
IpAddr::V4(ipv4) => { IpAddr::V4(ipv4) => {
// 对于IPv4,检查是否为有效的网络地址 // For IPv4, check if it's a valid network address
assert!(!ipv4.is_unspecified(), "Should not be 0.0.0.0"); assert!(!ipv4.is_unspecified(), "Should not be 0.0.0.0");
// 如果不是回环地址,应该是可路由的 // If it's not a loopback address, it should be routable
if !ipv4.is_loopback() { if !ipv4.is_loopback() {
println!("Got routable IPv4: {}", ipv4); println!("Got routable IPv4: {}", ipv4);
} }
} }
IpAddr::V6(ipv6) => { IpAddr::V6(ipv6) => {
// 对于IPv6,检查是否为有效的网络地址 // For IPv6, check if it's a valid network address
assert!(!ipv6.is_unspecified(), "Should not be ::"); assert!(!ipv6.is_unspecified(), "Should not be ::");
if !ipv6.is_loopback() { if !ipv6.is_loopback() {
+3 -3
View File
@@ -315,7 +315,7 @@ mod tests {
#[test] #[test]
fn test_compression_format_from_extension() { fn test_compression_format_from_extension() {
// 测试支持的压缩格式识别 // Test supported compression format recognition
assert_eq!(CompressionFormat::from_extension("gz"), CompressionFormat::Gzip); assert_eq!(CompressionFormat::from_extension("gz"), CompressionFormat::Gzip);
assert_eq!(CompressionFormat::from_extension("gzip"), CompressionFormat::Gzip); assert_eq!(CompressionFormat::from_extension("gzip"), CompressionFormat::Gzip);
assert_eq!(CompressionFormat::from_extension("bz2"), CompressionFormat::Bzip2); assert_eq!(CompressionFormat::from_extension("bz2"), CompressionFormat::Bzip2);
@@ -327,11 +327,11 @@ mod tests {
assert_eq!(CompressionFormat::from_extension("zstd"), CompressionFormat::Zstd); assert_eq!(CompressionFormat::from_extension("zstd"), CompressionFormat::Zstd);
assert_eq!(CompressionFormat::from_extension("tar"), CompressionFormat::Tar); assert_eq!(CompressionFormat::from_extension("tar"), CompressionFormat::Tar);
// 测试大小写不敏感 // Test case insensitivity
assert_eq!(CompressionFormat::from_extension("GZ"), CompressionFormat::Gzip); assert_eq!(CompressionFormat::from_extension("GZ"), CompressionFormat::Gzip);
assert_eq!(CompressionFormat::from_extension("ZIP"), CompressionFormat::Zip); assert_eq!(CompressionFormat::from_extension("ZIP"), CompressionFormat::Zip);
// 测试未知格式 // Test unknown formats
assert_eq!(CompressionFormat::from_extension("unknown"), CompressionFormat::Unknown); assert_eq!(CompressionFormat::from_extension("unknown"), CompressionFormat::Unknown);
assert_eq!(CompressionFormat::from_extension("txt"), CompressionFormat::Unknown); assert_eq!(CompressionFormat::from_extension("txt"), CompressionFormat::Unknown);
assert_eq!(CompressionFormat::from_extension(""), CompressionFormat::Unknown); assert_eq!(CompressionFormat::from_extension(""), CompressionFormat::Unknown);
+211 -1
View File
@@ -30,7 +30,6 @@ impl ID {
_ => { _ => {
let params = Params::new(64 * 1024, 1, 4, Some(32))?; let params = Params::new(64 * 1024, 1, 4, Some(32))?;
let argon_2id = Argon2::new(Algorithm::Argon2id, Version::V0x13, params); let argon_2id = Argon2::new(Algorithm::Argon2id, Version::V0x13, params);
let mut key = vec![0u8; 32];
argon_2id.hash_password_into(password, salt, &mut key)?; argon_2id.hash_password_into(password, salt, &mut key)?;
} }
} }
@@ -38,3 +37,214 @@ impl ID {
Ok(key) Ok(key)
} }
} }
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_id_enum_values() {
// Test enum discriminant values
assert_eq!(ID::Argon2idAESGCM as u8, 0x00);
assert_eq!(ID::Argon2idChaCHa20Poly1305 as u8, 0x01);
assert_eq!(ID::Pbkdf2AESGCM as u8, 0x02);
}
#[test]
fn test_id_try_from_valid_values() {
// Test valid conversions from u8 to ID
assert!(matches!(ID::try_from(0x00), Ok(ID::Argon2idAESGCM)));
assert!(matches!(ID::try_from(0x01), Ok(ID::Argon2idChaCHa20Poly1305)));
assert!(matches!(ID::try_from(0x02), Ok(ID::Pbkdf2AESGCM)));
}
#[test]
fn test_id_try_from_invalid_values() {
// Test invalid conversions from u8 to ID
assert!(ID::try_from(0x03).is_err());
assert!(ID::try_from(0xFF).is_err());
assert!(ID::try_from(100).is_err());
// Verify error type
if let Err(crate::Error::ErrInvalidAlgID(value)) = ID::try_from(0x03) {
assert_eq!(value, 0x03);
} else {
panic!("Expected ErrInvalidAlgID error");
}
}
#[test]
fn test_id_debug_format() {
// Test Debug trait implementation
let argon2_aes = ID::Argon2idAESGCM;
let argon2_chacha = ID::Argon2idChaCHa20Poly1305;
let pbkdf2 = ID::Pbkdf2AESGCM;
assert_eq!(format!("{:?}", argon2_aes), "Argon2idAESGCM");
assert_eq!(format!("{:?}", argon2_chacha), "Argon2idChaCHa20Poly1305");
assert_eq!(format!("{:?}", pbkdf2), "Pbkdf2AESGCM");
}
#[test]
fn test_id_clone_and_copy() {
// Test Clone and Copy traits
let original = ID::Argon2idAESGCM;
let cloned = original.clone();
let copied = original;
assert!(matches!(cloned, ID::Argon2idAESGCM));
assert!(matches!(copied, ID::Argon2idAESGCM));
}
#[test]
fn test_pbkdf2_key_generation() {
// Test PBKDF2 key generation
let id = ID::Pbkdf2AESGCM;
let password = b"test_password";
let salt = b"test_salt_16bytes";
let result = id.get_key(password, salt);
assert!(result.is_ok());
let key = result.unwrap();
assert_eq!(key.len(), 32);
// Verify deterministic behavior - same inputs should produce same output
let result2 = id.get_key(password, salt);
assert!(result2.is_ok());
assert_eq!(key, result2.unwrap());
}
#[test]
fn test_argon2_key_generation() {
// Test Argon2id key generation
let id = ID::Argon2idAESGCM;
let password = b"test_password";
let salt = b"test_salt_16bytes";
let result = id.get_key(password, salt);
assert!(result.is_ok());
let key = result.unwrap();
assert_eq!(key.len(), 32);
// Verify deterministic behavior
let result2 = id.get_key(password, salt);
assert!(result2.is_ok());
assert_eq!(key, result2.unwrap());
}
#[test]
fn test_argon2_chacha_key_generation() {
// Test Argon2id ChaCha20Poly1305 key generation
let id = ID::Argon2idChaCHa20Poly1305;
let password = b"test_password";
let salt = b"test_salt_16bytes";
let result = id.get_key(password, salt);
assert!(result.is_ok());
let key = result.unwrap();
assert_eq!(key.len(), 32);
}
#[test]
fn test_key_generation_with_different_passwords() {
// Test that different passwords produce different keys
let id = ID::Pbkdf2AESGCM;
let salt = b"same_salt_for_all";
let key1 = id.get_key(b"password1", salt).unwrap();
let key2 = id.get_key(b"password2", salt).unwrap();
assert_ne!(key1, key2);
}
#[test]
fn test_key_generation_with_different_salts() {
// Test that different salts produce different keys
let id = ID::Pbkdf2AESGCM;
let password = b"same_password";
let key1 = id.get_key(password, b"salt1_16_bytes__").unwrap();
let key2 = id.get_key(password, b"salt2_16_bytes__").unwrap();
assert_ne!(key1, key2);
}
#[test]
fn test_key_generation_with_empty_inputs() {
// Test key generation with empty password and salt
let id = ID::Pbkdf2AESGCM;
let result1 = id.get_key(b"", b"salt");
assert!(result1.is_ok());
let result2 = id.get_key(b"password", b"");
assert!(result2.is_ok());
let result3 = id.get_key(b"", b"");
assert!(result3.is_ok());
}
#[test]
fn test_all_algorithms_produce_valid_keys() {
// Test that all algorithm variants can generate valid keys
let algorithms = [
ID::Argon2idAESGCM,
ID::Argon2idChaCHa20Poly1305,
ID::Pbkdf2AESGCM,
];
let password = b"test_password_123";
let salt = b"test_salt_16bytes";
for algorithm in &algorithms {
let result = algorithm.get_key(password, salt);
assert!(result.is_ok(), "Algorithm {:?} should generate valid key", algorithm);
let key = result.unwrap();
assert_eq!(key.len(), 32, "Key length should be 32 bytes for {:?}", algorithm);
// Verify key is not all zeros (very unlikely with proper implementation)
assert_ne!(key, [0u8; 32], "Key should not be all zeros for {:?}", algorithm);
}
}
#[test]
fn test_round_trip_conversion() {
// Test round-trip conversion: ID -> u8 -> ID
let original_ids = [
ID::Argon2idAESGCM,
ID::Argon2idChaCHa20Poly1305,
ID::Pbkdf2AESGCM,
];
for original in &original_ids {
let as_u8 = *original as u8;
let converted_back = ID::try_from(as_u8).unwrap();
assert!(matches!((original, converted_back),
(ID::Argon2idAESGCM, ID::Argon2idAESGCM) |
(ID::Argon2idChaCHa20Poly1305, ID::Argon2idChaCHa20Poly1305) |
(ID::Pbkdf2AESGCM, ID::Pbkdf2AESGCM)
));
}
}
#[test]
fn test_key_generation_consistency_across_algorithms() {
// Test that different algorithms produce different keys for same input
let password = b"consistent_password";
let salt = b"consistent_salt_";
let key_argon2_aes = ID::Argon2idAESGCM.get_key(password, salt).unwrap();
let key_argon2_chacha = ID::Argon2idChaCHa20Poly1305.get_key(password, salt).unwrap();
let key_pbkdf2 = ID::Pbkdf2AESGCM.get_key(password, salt).unwrap();
// Different algorithms should produce different keys
assert_ne!(key_argon2_aes, key_pbkdf2);
assert_ne!(key_argon2_chacha, key_pbkdf2);
// Note: Argon2 variants might produce same key since they use same algorithm
}
}