mirror of
https://github.com/rustfs/rustfs.git
synced 2026-07-26 16:28:15 +00:00
Merge pull request #407 from rustfs/feat/improve-test-coverage-and-translations
feat: improve test coverage and fix critical crypto bug
This commit is contained in:
+50
-2
@@ -194,6 +194,38 @@ mod tests {
|
||||
- Each test should have a clear purpose and verify specific behavior
|
||||
- Test data should be realistic and representative of actual use cases
|
||||
|
||||
## Cross-Platform Compatibility Guidelines
|
||||
|
||||
### 1. CPU Architecture Compatibility
|
||||
- **Always consider multi-platform and different CPU architecture compatibility** when writing code
|
||||
- Support major architectures: x86_64, aarch64 (ARM64), and other target platforms
|
||||
- Use conditional compilation for architecture-specific code:
|
||||
```rust
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
fn optimized_x86_64_function() { /* x86_64 specific implementation */ }
|
||||
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
fn optimized_aarch64_function() { /* ARM64 specific implementation */ }
|
||||
|
||||
#[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64")))]
|
||||
fn generic_function() { /* Generic fallback implementation */ }
|
||||
```
|
||||
|
||||
### 2. Platform-Specific Dependencies
|
||||
- Use feature flags for platform-specific dependencies
|
||||
- Provide fallback implementations for unsupported platforms
|
||||
- Test on multiple architectures in CI/CD pipeline
|
||||
|
||||
### 3. Endianness Considerations
|
||||
- Use explicit byte order conversion when dealing with binary data
|
||||
- Prefer `to_le_bytes()`, `from_le_bytes()` for consistent little-endian format
|
||||
- Use `byteorder` crate for complex binary format handling
|
||||
|
||||
### 4. SIMD and Performance Optimizations
|
||||
- Use portable SIMD libraries like `wide` or `packed_simd`
|
||||
- Provide fallback implementations for non-SIMD architectures
|
||||
- Use runtime feature detection when appropriate
|
||||
|
||||
## Security Guidelines
|
||||
|
||||
### 1. Memory Safety
|
||||
@@ -275,14 +307,22 @@ async fn health_check() -> Result<HealthStatus> {
|
||||
- [ ] Are there appropriate permission checks?
|
||||
- [ ] Is information leakage avoided?
|
||||
|
||||
### 4. Maintainability
|
||||
### 4. Cross-Platform Compatibility
|
||||
- [ ] Does the code work on different CPU architectures (x86_64, aarch64)?
|
||||
- [ ] Are platform-specific features properly gated with conditional compilation?
|
||||
- [ ] Is byte order handling correct for binary data?
|
||||
- [ ] Are there appropriate fallback implementations for unsupported platforms?
|
||||
|
||||
### 5. Maintainability
|
||||
- [ ] Is the code clear and understandable?
|
||||
- [ ] Does it follow the project's architectural patterns?
|
||||
- [ ] Is there appropriate documentation?
|
||||
|
||||
### 5. Code Commits
|
||||
### 6. Code Commits and Documentation
|
||||
- [ ] Does it comply with [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/)?
|
||||
- [ ] Are commit messages concise and under 72 characters for the title line?
|
||||
- [ ] Commit titles should be concise and in English, avoid Chinese
|
||||
- [ ] Is PR description provided in copyable markdown format for easy copying?
|
||||
|
||||
## Common Patterns and Best Practices
|
||||
|
||||
@@ -363,6 +403,14 @@ These rules should serve as guiding principles when developing the RustFS projec
|
||||
- Do not arbitrarily modify numbers and constants in test cases, carefully analyze their meaning to ensure test case correctness
|
||||
- When writing or modifying tests, check existing test cases to ensure they have scientific naming and rigorous logic testing, if not compliant, modify test cases to ensure scientific and rigorous testing
|
||||
- After each development completion, first git add . then git commit -m "feat: feature description" or "fix: issue description", ensure compliance with [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/)
|
||||
- **Keep commit messages concise and under 72 characters** for the title line, use body for detailed explanations if needed
|
||||
- After each development completion, first git push to remote repository
|
||||
- After each change completion, summarize the changes, do not create summary files, provide a brief change description, ensure compliance with [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/)
|
||||
- Provide change descriptions needed for PR in the conversation, ensure compliance with [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/)
|
||||
- **Always provide PR descriptions in English** after completing any changes, including:
|
||||
- Clear and concise title following Conventional Commits format
|
||||
- Detailed description of what was changed and why
|
||||
- List of key changes and improvements
|
||||
- Any breaking changes or migration notes if applicable
|
||||
- Testing information and verification steps
|
||||
- **Provide PR descriptions in copyable markdown format** enclosed in code blocks for easy one-click copying
|
||||
|
||||
@@ -96,7 +96,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_app_basic_constants() {
|
||||
// 测试应用基本常量
|
||||
// Test application basic constants
|
||||
assert_eq!(APP_NAME, "RustFs");
|
||||
assert!(!APP_NAME.is_empty(), "App name should not be empty");
|
||||
assert!(!APP_NAME.contains(' '), "App name should not contain spaces");
|
||||
@@ -110,7 +110,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_logging_constants() {
|
||||
// 测试日志相关常量
|
||||
// Test logging related constants
|
||||
assert_eq!(DEFAULT_LOG_LEVEL, "info");
|
||||
assert!(["trace", "debug", "info", "warn", "error"].contains(&DEFAULT_LOG_LEVEL),
|
||||
"Log level should be a valid tracing level");
|
||||
@@ -127,7 +127,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_environment_constants() {
|
||||
// 测试环境相关常量
|
||||
// Test environment related constants
|
||||
assert_eq!(ENVIRONMENT, "production");
|
||||
assert!(["development", "staging", "production", "test"].contains(&ENVIRONMENT),
|
||||
"Environment should be a standard environment name");
|
||||
@@ -135,7 +135,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_connection_constants() {
|
||||
// 测试连接相关常量
|
||||
// Test connection related constants
|
||||
assert_eq!(MAX_CONNECTIONS, 100);
|
||||
assert!(MAX_CONNECTIONS > 0, "Max connections should be positive");
|
||||
assert!(MAX_CONNECTIONS <= 10000, "Max connections should be reasonable");
|
||||
@@ -147,7 +147,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_security_constants() {
|
||||
// 测试安全相关常量
|
||||
// Test security related constants
|
||||
assert_eq!(DEFAULT_ACCESS_KEY, "rustfsadmin");
|
||||
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");
|
||||
@@ -156,14 +156,14 @@ mod tests {
|
||||
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");
|
||||
|
||||
// 在生产环境中,访问密钥和秘密密钥应该不同
|
||||
// 这里是默认值,所以相同是可以接受的,但应该在文档中警告
|
||||
// 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!");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_file_path_constants() {
|
||||
// 测试文件路径相关常量
|
||||
// Test file path related constants
|
||||
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.is_empty(), "Config path should not be empty");
|
||||
@@ -177,14 +177,14 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_port_constants() {
|
||||
// 测试端口相关常量
|
||||
// Test port related constants
|
||||
assert_eq!(DEFAULT_PORT, 9000);
|
||||
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!(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,
|
||||
"Main port and console port should be different");
|
||||
@@ -192,7 +192,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_address_constants() {
|
||||
// 测试地址相关常量
|
||||
// Test address related constants
|
||||
assert_eq!(DEFAULT_ADDRESS, ":9000");
|
||||
assert!(DEFAULT_ADDRESS.starts_with(':'), "Address should start with colon");
|
||||
assert!(DEFAULT_ADDRESS.contains(&DEFAULT_PORT.to_string()),
|
||||
@@ -209,7 +209,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_const_str_concat_functionality() {
|
||||
// 测试const_str::concat宏的功能
|
||||
// Test const_str::concat macro functionality
|
||||
let expected_address = format!(":{}", DEFAULT_PORT);
|
||||
assert_eq!(DEFAULT_ADDRESS, expected_address);
|
||||
|
||||
@@ -219,7 +219,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_string_constants_validity() {
|
||||
// 测试字符串常量的有效性
|
||||
// Test validity of string constants
|
||||
let string_constants = [
|
||||
APP_NAME,
|
||||
VERSION,
|
||||
@@ -244,7 +244,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_numeric_constants_validity() {
|
||||
// 测试数值常量的有效性
|
||||
// Test validity of numeric constants
|
||||
assert!(SAMPLE_RATIO.is_finite(), "Sample ratio should be finite");
|
||||
assert!(!SAMPLE_RATIO.is_nan(), "Sample ratio should not be NaN");
|
||||
|
||||
@@ -258,42 +258,42 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
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!("Access Key: {}", DEFAULT_ACCESS_KEY);
|
||||
println!("Secret Key: {}", DEFAULT_SECRET_KEY);
|
||||
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_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 _access_key_lower = DEFAULT_ACCESS_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]
|
||||
fn test_configuration_consistency() {
|
||||
// 测试配置的一致性
|
||||
// Test configuration consistency
|
||||
|
||||
// 版本一致性
|
||||
// Version consistency
|
||||
assert_eq!(VERSION, SERVICE_VERSION, "Application version should match service version");
|
||||
|
||||
// 端口不冲突
|
||||
// Port conflict check
|
||||
let ports = [DEFAULT_PORT, DEFAULT_CONSOLE_PORT];
|
||||
let mut unique_ports = std::collections::HashSet::new();
|
||||
for port in &ports {
|
||||
assert!(unique_ports.insert(port), "Port {} is duplicated", port);
|
||||
}
|
||||
|
||||
// 地址格式一致性
|
||||
// Address format consistency
|
||||
assert_eq!(DEFAULT_ADDRESS, format!(":{}", DEFAULT_PORT));
|
||||
assert_eq!(DEFAULT_CONSOLE_ADDRESS, format!(":{}", DEFAULT_CONSOLE_PORT));
|
||||
}
|
||||
|
||||
+26
-26
@@ -35,13 +35,13 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_get_local_ip_returns_some_ip() {
|
||||
// 测试获取本地IP地址,应该返回Some值
|
||||
// Test getting local IP address, should return Some value
|
||||
let ip = get_local_ip();
|
||||
assert!(ip.is_some(), "Should be able to get local IP address");
|
||||
|
||||
if let Some(ip_addr) = ip {
|
||||
println!("Local IP address: {}", ip_addr);
|
||||
// 验证返回的是有效的IP地址
|
||||
// Verify that the returned IP address is valid
|
||||
match ip_addr {
|
||||
IpAddr::V4(ipv4) => {
|
||||
assert!(!ipv4.is_unspecified(), "IPv4 should not be unspecified (0.0.0.0)");
|
||||
@@ -57,11 +57,11 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
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();
|
||||
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();
|
||||
assert!(parsed_ip.is_ok(), "Returned string should be a valid IP address: {}", ip_string);
|
||||
|
||||
@@ -70,38 +70,38 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
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 ip_string = get_local_ip_with_default();
|
||||
|
||||
// 如果无法获取真实IP,应该返回默认值
|
||||
// If unable to get real IP, should return default value
|
||||
if get_local_ip().is_none() {
|
||||
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();
|
||||
assert!(parsed.is_ok(), "Should always return a valid IP string");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ip_address_types() {
|
||||
// 测试IP地址类型的识别
|
||||
// Test IP address type recognition
|
||||
if let Some(ip) = get_local_ip() {
|
||||
match ip {
|
||||
IpAddr::V4(ipv4) => {
|
||||
// 测试IPv4地址的属性
|
||||
// Test IPv4 address properties
|
||||
println!("IPv4 address: {}", ipv4);
|
||||
assert!(!ipv4.is_multicast(), "Local IP should not be multicast");
|
||||
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_loopback = ipv4.is_loopback();
|
||||
println!("IPv4 is private: {}, is loopback: {}", is_private, is_loopback);
|
||||
}
|
||||
IpAddr::V6(ipv6) => {
|
||||
// 测试IPv6地址的属性
|
||||
// Test IPv6 address properties
|
||||
println!("IPv6 address: {}", ipv6);
|
||||
assert!(!ipv6.is_multicast(), "Local IP should not be multicast");
|
||||
|
||||
@@ -114,28 +114,28 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_ip_string_format() {
|
||||
// 测试IP地址字符串格式
|
||||
// Test IP address string format
|
||||
let ip_string = get_local_ip_with_default();
|
||||
|
||||
// 验证字符串格式
|
||||
// Verify string format
|
||||
assert!(!ip_string.contains(' '), "IP string should not contain spaces");
|
||||
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 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);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_default_fallback_value() {
|
||||
// 测试默认回退值的正确性
|
||||
// Test correctness of default fallback value
|
||||
let default_ip = IpAddr::V4(Ipv4Addr::new(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 {
|
||||
assert!(ipv4.is_loopback(), "Default IP should be loopback");
|
||||
assert!(!ipv4.is_unspecified(), "Default IP should not be unspecified");
|
||||
@@ -145,18 +145,18 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_consistency_between_functions() {
|
||||
// 测试两个函数之间的一致性
|
||||
// Test consistency between the two functions
|
||||
let ip_option = get_local_ip();
|
||||
let ip_string = get_local_ip_with_default();
|
||||
|
||||
match ip_option {
|
||||
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,
|
||||
"Both functions should return the same IP when available");
|
||||
}
|
||||
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",
|
||||
"Should return default value when no IP is available");
|
||||
}
|
||||
@@ -165,13 +165,13 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_multiple_calls_consistency() {
|
||||
// 测试多次调用的一致性
|
||||
// Test consistency of multiple calls
|
||||
let ip1 = get_local_ip();
|
||||
let ip2 = get_local_ip();
|
||||
let ip_str1 = 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!(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")]
|
||||
#[test]
|
||||
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() {
|
||||
match ip {
|
||||
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");
|
||||
|
||||
// 如果不是回环地址,应该是可路由的
|
||||
// If it's not a loopback address, it should be routable
|
||||
if !ipv4.is_loopback() {
|
||||
println!("Got routable IPv4: {}", ipv4);
|
||||
}
|
||||
}
|
||||
IpAddr::V6(ipv6) => {
|
||||
// 对于IPv6,检查是否为有效的网络地址
|
||||
// For IPv6, check if it's a valid network address
|
||||
assert!(!ipv6.is_unspecified(), "Should not be ::");
|
||||
|
||||
if !ipv6.is_loopback() {
|
||||
|
||||
@@ -315,7 +315,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_compression_format_from_extension() {
|
||||
// 测试支持的压缩格式识别
|
||||
// Test supported compression format recognition
|
||||
assert_eq!(CompressionFormat::from_extension("gz"), CompressionFormat::Gzip);
|
||||
assert_eq!(CompressionFormat::from_extension("gzip"), CompressionFormat::Gzip);
|
||||
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("tar"), CompressionFormat::Tar);
|
||||
|
||||
// 测试大小写不敏感
|
||||
// Test case insensitivity
|
||||
assert_eq!(CompressionFormat::from_extension("GZ"), CompressionFormat::Gzip);
|
||||
assert_eq!(CompressionFormat::from_extension("ZIP"), CompressionFormat::Zip);
|
||||
|
||||
// 测试未知格式
|
||||
// Test unknown formats
|
||||
assert_eq!(CompressionFormat::from_extension("unknown"), CompressionFormat::Unknown);
|
||||
assert_eq!(CompressionFormat::from_extension("txt"), CompressionFormat::Unknown);
|
||||
assert_eq!(CompressionFormat::from_extension(""), CompressionFormat::Unknown);
|
||||
|
||||
+211
-1
@@ -30,7 +30,6 @@ impl ID {
|
||||
_ => {
|
||||
let params = Params::new(64 * 1024, 1, 4, Some(32))?;
|
||||
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)?;
|
||||
}
|
||||
}
|
||||
@@ -38,3 +37,214 @@ impl ID {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user