Merge pull request #430 from rustfs/feat/improve-madmin-module-tests

feat: improve madmin module test coverage - Add comprehensive test ca…
This commit is contained in:
安正超
2025-05-27 23:44:17 +08:00
committed by GitHub
3 changed files with 1784 additions and 4 deletions

View File

@@ -186,3 +186,501 @@ pub struct MemInfo {
pub fn get_mem_info(_addr: &str) -> MemInfo {
MemInfo::default()
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json;
#[test]
fn test_node_common_creation() {
let node = NodeCommon::default();
assert!(node.addr.is_empty(), "Default addr should be empty");
assert!(node.error.is_none(), "Default error should be None");
}
#[test]
fn test_node_common_with_values() {
let node = NodeCommon {
addr: "127.0.0.1:9000".to_string(),
error: Some("Connection failed".to_string()),
};
assert_eq!(node.addr, "127.0.0.1:9000");
assert_eq!(node.error.unwrap(), "Connection failed");
}
#[test]
fn test_node_common_serialization() {
let node = NodeCommon {
addr: "localhost:8080".to_string(),
error: None,
};
let json = serde_json::to_string(&node).unwrap();
assert!(json.contains("localhost:8080"));
assert!(!json.contains("error"), "None error should be skipped in serialization");
}
#[test]
fn test_node_common_deserialization() {
let json = r#"{"addr":"test.example.com:9000","error":"Test error"}"#;
let node: NodeCommon = serde_json::from_str(json).unwrap();
assert_eq!(node.addr, "test.example.com:9000");
assert_eq!(node.error.unwrap(), "Test error");
}
#[test]
fn test_cpu_default() {
let cpu = Cpu::default();
assert!(cpu.vendor_id.is_empty());
assert!(cpu.family.is_empty());
assert!(cpu.model.is_empty());
assert_eq!(cpu.stepping, 0);
assert_eq!(cpu.mhz, 0.0);
assert_eq!(cpu.cache_size, 0);
assert!(cpu.flags.is_empty());
assert_eq!(cpu.cores, 0);
}
#[test]
fn test_cpu_with_values() {
let cpu = Cpu {
vendor_id: "GenuineIntel".to_string(),
family: "6".to_string(),
model: "142".to_string(),
stepping: 12,
physical_id: "0".to_string(),
model_name: "Intel(R) Core(TM) i7-8565U CPU @ 1.80GHz".to_string(),
mhz: 1800.0,
cache_size: 8192,
flags: vec!["fpu".to_string(), "vme".to_string(), "de".to_string()],
microcode: "0xf0".to_string(),
cores: 4,
};
assert_eq!(cpu.vendor_id, "GenuineIntel");
assert_eq!(cpu.cores, 4);
assert_eq!(cpu.flags.len(), 3);
assert!(cpu.flags.contains(&"fpu".to_string()));
}
#[test]
fn test_cpu_serialization() {
let cpu = Cpu {
vendor_id: "AMD".to_string(),
model_name: "AMD Ryzen 7".to_string(),
cores: 8,
..Default::default()
};
let json = serde_json::to_string(&cpu).unwrap();
assert!(json.contains("AMD"));
assert!(json.contains("AMD Ryzen 7"));
assert!(json.contains("8"));
}
#[test]
fn test_cpu_freq_stats_default() {
let stats = CpuFreqStats::default();
assert!(stats.name.is_empty());
assert!(stats.cpuinfo_current_frequency.is_none());
assert!(stats.available_governors.is_empty());
assert!(stats.driver.is_empty());
}
#[test]
fn test_cpus_structure() {
let cpus = Cpus {
node_common: NodeCommon {
addr: "node1".to_string(),
error: None,
},
cpus: vec![Cpu {
vendor_id: "Intel".to_string(),
cores: 4,
..Default::default()
}],
cpu_freq_stats: vec![CpuFreqStats {
name: "cpu0".to_string(),
cpuinfo_current_frequency: Some(2400),
..Default::default()
}],
};
assert_eq!(cpus.node_common.addr, "node1");
assert_eq!(cpus.cpus.len(), 1);
assert_eq!(cpus.cpu_freq_stats.len(), 1);
assert_eq!(cpus.cpus[0].cores, 4);
}
#[test]
fn test_get_cpus_function() {
let cpus = get_cpus();
assert!(cpus.node_common.addr.is_empty());
assert!(cpus.cpus.is_empty());
assert!(cpus.cpu_freq_stats.is_empty());
}
#[test]
fn test_partition_default() {
let partition = Partition::default();
assert!(partition.error.is_empty());
assert!(partition.device.is_empty());
assert_eq!(partition.space_total, 0);
assert_eq!(partition.space_free, 0);
assert_eq!(partition.inode_total, 0);
assert_eq!(partition.inode_free, 0);
}
#[test]
fn test_partition_with_values() {
let partition = Partition {
error: "".to_string(),
device: "/dev/sda1".to_string(),
model: "Samsung SSD".to_string(),
revision: "1.0".to_string(),
mountpoint: "/".to_string(),
fs_type: "ext4".to_string(),
mount_options: "rw,relatime".to_string(),
space_total: 1000000000,
space_free: 500000000,
inode_total: 1000000,
inode_free: 800000,
};
assert_eq!(partition.device, "/dev/sda1");
assert_eq!(partition.fs_type, "ext4");
assert_eq!(partition.space_total, 1000000000);
assert_eq!(partition.space_free, 500000000);
}
#[test]
fn test_partitions_structure() {
let partitions = Partitions {
node_common: NodeCommon {
addr: "storage-node".to_string(),
error: None,
},
partitions: vec![
Partition {
device: "/dev/sda1".to_string(),
mountpoint: "/".to_string(),
space_total: 1000000,
space_free: 500000,
..Default::default()
},
Partition {
device: "/dev/sdb1".to_string(),
mountpoint: "/data".to_string(),
space_total: 2000000,
space_free: 1500000,
..Default::default()
},
],
};
assert_eq!(partitions.partitions.len(), 2);
assert_eq!(partitions.partitions[0].device, "/dev/sda1");
assert_eq!(partitions.partitions[1].mountpoint, "/data");
}
#[test]
fn test_get_partitions_function() {
let partitions = get_partitions();
assert!(partitions.node_common.addr.is_empty());
assert!(partitions.partitions.is_empty());
}
#[test]
fn test_os_info_default() {
let os_info = OsInfo::default();
assert!(os_info.node_common.addr.is_empty());
assert!(os_info.node_common.error.is_none());
}
#[test]
fn test_get_os_info_function() {
let os_info = get_os_info();
assert!(os_info.node_common.addr.is_empty());
}
#[test]
fn test_proc_info_default() {
let proc_info = ProcInfo::default();
assert_eq!(proc_info.pid, 0);
assert!(!proc_info.is_background);
assert_eq!(proc_info.cpu_percent, 0.0);
assert!(proc_info.children_pids.is_empty());
assert!(proc_info.cmd_line.is_empty());
assert_eq!(proc_info.num_connections, 0);
assert!(!proc_info.is_running);
assert_eq!(proc_info.mem_percent, 0.0);
assert!(proc_info.name.is_empty());
assert_eq!(proc_info.nice, 0);
assert_eq!(proc_info.num_fds, 0);
assert_eq!(proc_info.num_threads, 0);
assert_eq!(proc_info.ppid, 0);
assert!(proc_info.status.is_empty());
assert_eq!(proc_info.tgid, 0);
assert!(proc_info.uids.is_empty());
assert!(proc_info.username.is_empty());
}
#[test]
fn test_proc_info_with_values() {
let proc_info = ProcInfo {
node_common: NodeCommon {
addr: "worker-node".to_string(),
error: None,
},
pid: 1234,
is_background: true,
cpu_percent: 15.5,
children_pids: vec![1235, 1236],
cmd_line: "rustfs --config /etc/rustfs.conf".to_string(),
num_connections: 10,
create_time: 1640995200,
cwd: "/opt/rustfs".to_string(),
exec_path: "/usr/bin/rustfs".to_string(),
gids: vec![1000, 1001],
is_running: true,
mem_percent: 8.2,
name: "rustfs".to_string(),
nice: 0,
num_fds: 25,
num_threads: 4,
ppid: 1,
status: "running".to_string(),
tgid: 1234,
uids: vec![1000],
username: "rustfs".to_string(),
};
assert_eq!(proc_info.pid, 1234);
assert!(proc_info.is_background);
assert_eq!(proc_info.cpu_percent, 15.5);
assert_eq!(proc_info.children_pids.len(), 2);
assert_eq!(proc_info.name, "rustfs");
assert!(proc_info.is_running);
}
#[test]
fn test_get_proc_info_function() {
let proc_info = get_proc_info("127.0.0.1:9000");
assert_eq!(proc_info.pid, 0);
assert!(!proc_info.is_running);
}
#[test]
fn test_sys_service_default() {
let service = SysService::default();
assert!(service.name.is_empty());
assert!(service.status.is_empty());
}
#[test]
fn test_sys_service_with_values() {
let service = SysService {
name: "rustfs".to_string(),
status: "active".to_string(),
};
assert_eq!(service.name, "rustfs");
assert_eq!(service.status, "active");
}
#[test]
fn test_sys_services_structure() {
let services = SysServices {
node_common: NodeCommon {
addr: "service-node".to_string(),
error: None,
},
services: vec![
SysService {
name: "rustfs".to_string(),
status: "active".to_string(),
},
SysService {
name: "nginx".to_string(),
status: "inactive".to_string(),
},
],
};
assert_eq!(services.services.len(), 2);
assert_eq!(services.services[0].name, "rustfs");
assert_eq!(services.services[1].status, "inactive");
}
#[test]
fn test_get_sys_services_function() {
let services = get_sys_services("localhost");
assert!(services.node_common.addr.is_empty());
assert!(services.services.is_empty());
}
#[test]
fn test_sys_config_default() {
let config = SysConfig::default();
assert!(config.node_common.addr.is_empty());
assert!(config.config.is_empty());
}
#[test]
fn test_sys_config_with_values() {
let mut config_map = HashMap::new();
config_map.insert("max_connections".to_string(), "1000".to_string());
config_map.insert("timeout".to_string(), "30".to_string());
let config = SysConfig {
node_common: NodeCommon {
addr: "config-node".to_string(),
error: None,
},
config: config_map,
};
assert_eq!(config.config.len(), 2);
assert_eq!(config.config.get("max_connections").unwrap(), "1000");
assert_eq!(config.config.get("timeout").unwrap(), "30");
}
#[test]
fn test_get_sys_config_function() {
let config = get_sys_config("192.168.1.100");
assert!(config.node_common.addr.is_empty());
assert!(config.config.is_empty());
}
#[test]
fn test_sys_errors_default() {
let errors = SysErrors::default();
assert!(errors.node_common.addr.is_empty());
assert!(errors.errors.is_empty());
}
#[test]
fn test_sys_errors_with_values() {
let errors = SysErrors {
node_common: NodeCommon {
addr: "error-node".to_string(),
error: None,
},
errors: vec![
"Connection timeout".to_string(),
"Memory allocation failed".to_string(),
"Disk full".to_string(),
],
};
assert_eq!(errors.errors.len(), 3);
assert!(errors.errors.contains(&"Connection timeout".to_string()));
assert!(errors.errors.contains(&"Disk full".to_string()));
}
#[test]
fn test_get_sys_errors_function() {
let errors = get_sys_errors("test-node");
assert!(errors.node_common.addr.is_empty());
assert!(errors.errors.is_empty());
}
#[test]
fn test_mem_info_default() {
let mem_info = MemInfo::default();
assert!(mem_info.node_common.addr.is_empty());
assert!(mem_info.total.is_none());
assert!(mem_info.used.is_none());
assert!(mem_info.free.is_none());
assert!(mem_info.available.is_none());
assert!(mem_info.shared.is_none());
assert!(mem_info.cache.is_none());
assert!(mem_info.buffers.is_none());
assert!(mem_info.swap_space_total.is_none());
assert!(mem_info.swap_space_free.is_none());
assert!(mem_info.limit.is_none());
}
#[test]
fn test_mem_info_with_values() {
let mem_info = MemInfo {
node_common: NodeCommon {
addr: "memory-node".to_string(),
error: None,
},
total: Some(16777216000),
used: Some(8388608000),
free: Some(4194304000),
available: Some(12582912000),
shared: Some(1048576000),
cache: Some(2097152000),
buffers: Some(524288000),
swap_space_total: Some(4294967296),
swap_space_free: Some(2147483648),
limit: Some(16777216000),
};
assert_eq!(mem_info.total.unwrap(), 16777216000);
assert_eq!(mem_info.used.unwrap(), 8388608000);
assert_eq!(mem_info.free.unwrap(), 4194304000);
assert_eq!(mem_info.swap_space_total.unwrap(), 4294967296);
}
#[test]
fn test_mem_info_serialization() {
let mem_info = MemInfo {
node_common: NodeCommon {
addr: "test-node".to_string(),
error: None,
},
total: Some(8000000000),
used: Some(4000000000),
free: None,
available: Some(6000000000),
..Default::default()
};
let json = serde_json::to_string(&mem_info).unwrap();
assert!(json.contains("8000000000"));
assert!(json.contains("4000000000"));
assert!(json.contains("6000000000"));
assert!(!json.contains("free"), "None values should be skipped");
}
#[test]
fn test_get_mem_info_function() {
let mem_info = get_mem_info("memory-server");
assert!(mem_info.node_common.addr.is_empty());
assert!(mem_info.total.is_none());
assert!(mem_info.used.is_none());
}
#[test]
fn test_all_structures_debug_format() {
let node = NodeCommon::default();
let cpu = Cpu::default();
let partition = Partition::default();
let proc_info = ProcInfo::default();
let service = SysService::default();
let mem_info = MemInfo::default();
// Test that all structures can be formatted with Debug
assert!(!format!("{:?}", node).is_empty());
assert!(!format!("{:?}", cpu).is_empty());
assert!(!format!("{:?}", partition).is_empty());
assert!(!format!("{:?}", proc_info).is_empty());
assert!(!format!("{:?}", service).is_empty());
assert!(!format!("{:?}", mem_info).is_empty());
}
#[test]
fn test_memory_efficiency() {
// Test that structures don't use excessive memory
assert!(std::mem::size_of::<NodeCommon>() < 1000);
assert!(std::mem::size_of::<Cpu>() < 2000);
assert!(std::mem::size_of::<Partition>() < 2000);
assert!(std::mem::size_of::<MemInfo>() < 1000);
}
}

View File

@@ -331,3 +331,759 @@ pub struct InfoMessage {
pub servers: Option<Vec<ServerProperties>>,
pub pools: Option<std::collections::HashMap<i32, std::collections::HashMap<i32, ErasureSetInfo>>>,
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json;
use std::collections::HashMap;
use time::OffsetDateTime;
#[test]
fn test_item_state_to_string() {
assert_eq!(ItemState::Offline.to_string(), ITEM_OFFLINE);
assert_eq!(ItemState::Initializing.to_string(), ITEM_INITIALIZING);
assert_eq!(ItemState::Online.to_string(), ITEM_ONLINE);
}
#[test]
fn test_item_state_from_string_valid() {
assert_eq!(ItemState::from_string(ITEM_OFFLINE), Some(ItemState::Offline));
assert_eq!(ItemState::from_string(ITEM_INITIALIZING), Some(ItemState::Initializing));
assert_eq!(ItemState::from_string(ITEM_ONLINE), Some(ItemState::Online));
}
#[test]
fn test_item_state_from_string_invalid() {
assert_eq!(ItemState::from_string("invalid"), None);
assert_eq!(ItemState::from_string(""), None);
assert_eq!(ItemState::from_string("OFFLINE"), None); // Case sensitive
}
#[test]
fn test_disk_metrics_default() {
let metrics = DiskMetrics::default();
assert!(metrics.last_minute.is_empty());
assert!(metrics.api_calls.is_empty());
assert_eq!(metrics.total_waiting, 0);
assert_eq!(metrics.total_errors_availability, 0);
assert_eq!(metrics.total_errors_timeout, 0);
assert_eq!(metrics.total_writes, 0);
assert_eq!(metrics.total_deletes, 0);
}
#[test]
fn test_disk_metrics_with_values() {
let mut last_minute = HashMap::new();
last_minute.insert("read".to_string(), TimedAction::default());
let mut api_calls = HashMap::new();
api_calls.insert("GET".to_string(), 100);
api_calls.insert("PUT".to_string(), 50);
let metrics = DiskMetrics {
last_minute,
api_calls,
total_waiting: 5,
total_errors_availability: 2,
total_errors_timeout: 1,
total_writes: 1000,
total_deletes: 50,
};
assert_eq!(metrics.last_minute.len(), 1);
assert_eq!(metrics.api_calls.len(), 2);
assert_eq!(metrics.total_waiting, 5);
assert_eq!(metrics.total_writes, 1000);
assert_eq!(metrics.total_deletes, 50);
}
#[test]
fn test_disk_default() {
let disk = Disk::default();
assert!(disk.endpoint.is_empty());
assert!(!disk.root_disk);
assert!(disk.drive_path.is_empty());
assert!(!disk.healing);
assert!(!disk.scanning);
assert!(disk.state.is_empty());
assert!(disk.uuid.is_empty());
assert_eq!(disk.major, 0);
assert_eq!(disk.minor, 0);
assert!(disk.model.is_none());
assert_eq!(disk.total_space, 0);
assert_eq!(disk.used_space, 0);
assert_eq!(disk.available_space, 0);
assert_eq!(disk.read_throughput, 0.0);
assert_eq!(disk.write_throughput, 0.0);
assert_eq!(disk.read_latency, 0.0);
assert_eq!(disk.write_latency, 0.0);
assert_eq!(disk.utilization, 0.0);
assert!(disk.metrics.is_none());
assert!(disk.heal_info.is_none());
assert_eq!(disk.used_inodes, 0);
assert_eq!(disk.free_inodes, 0);
assert!(!disk.local);
assert_eq!(disk.pool_index, 0);
assert_eq!(disk.set_index, 0);
assert_eq!(disk.disk_index, 0);
}
#[test]
fn test_disk_with_values() {
let disk = Disk {
endpoint: "http://localhost:9000".to_string(),
root_disk: true,
drive_path: "/data/disk1".to_string(),
healing: false,
scanning: true,
state: "online".to_string(),
uuid: "12345678-1234-1234-1234-123456789abc".to_string(),
major: 8,
minor: 1,
model: Some("Samsung SSD 980".to_string()),
total_space: 1000000000000,
used_space: 500000000000,
available_space: 500000000000,
read_throughput: 100.5,
write_throughput: 80.3,
read_latency: 5.2,
write_latency: 7.8,
utilization: 50.0,
metrics: Some(DiskMetrics::default()),
heal_info: None,
used_inodes: 1000000,
free_inodes: 9000000,
local: true,
pool_index: 0,
set_index: 1,
disk_index: 2,
};
assert_eq!(disk.endpoint, "http://localhost:9000");
assert!(disk.root_disk);
assert_eq!(disk.drive_path, "/data/disk1");
assert!(disk.scanning);
assert_eq!(disk.state, "online");
assert_eq!(disk.major, 8);
assert_eq!(disk.minor, 1);
assert_eq!(disk.model.unwrap(), "Samsung SSD 980");
assert_eq!(disk.total_space, 1000000000000);
assert_eq!(disk.utilization, 50.0);
assert!(disk.metrics.is_some());
assert!(disk.local);
}
#[test]
fn test_healing_disk_default() {
let healing_disk = HealingDisk::default();
assert!(healing_disk.id.is_empty());
assert!(healing_disk.heal_id.is_empty());
assert!(healing_disk.pool_index.is_none());
assert!(healing_disk.set_index.is_none());
assert!(healing_disk.disk_index.is_none());
assert!(healing_disk.endpoint.is_empty());
assert!(healing_disk.path.is_empty());
assert!(healing_disk.started.is_none());
assert!(healing_disk.last_update.is_none());
assert_eq!(healing_disk.retry_attempts, 0);
assert_eq!(healing_disk.objects_total_count, 0);
assert_eq!(healing_disk.objects_total_size, 0);
assert_eq!(healing_disk.items_healed, 0);
assert_eq!(healing_disk.items_failed, 0);
assert_eq!(healing_disk.item_skipped, 0);
assert_eq!(healing_disk.bytes_done, 0);
assert_eq!(healing_disk.bytes_failed, 0);
assert_eq!(healing_disk.bytes_skipped, 0);
assert_eq!(healing_disk.objects_healed, 0);
assert_eq!(healing_disk.objects_failed, 0);
assert!(healing_disk.bucket.is_empty());
assert!(healing_disk.object.is_empty());
assert!(healing_disk.queue_buckets.is_empty());
assert!(healing_disk.healed_buckets.is_empty());
assert!(!healing_disk.finished);
}
#[test]
fn test_healing_disk_with_values() {
let now = OffsetDateTime::now_utc();
let system_time = std::time::SystemTime::now();
let healing_disk = HealingDisk {
id: "heal-001".to_string(),
heal_id: "heal-session-123".to_string(),
pool_index: Some(0),
set_index: Some(1),
disk_index: Some(2),
endpoint: "http://node1:9000".to_string(),
path: "/data/disk1".to_string(),
started: Some(now),
last_update: Some(system_time),
retry_attempts: 3,
objects_total_count: 10000,
objects_total_size: 1000000000,
items_healed: 8000,
items_failed: 100,
item_skipped: 50,
bytes_done: 800000000,
bytes_failed: 10000000,
bytes_skipped: 5000000,
objects_healed: 7900,
objects_failed: 100,
bucket: "test-bucket".to_string(),
object: "test-object".to_string(),
queue_buckets: vec!["bucket1".to_string(), "bucket2".to_string()],
healed_buckets: vec!["bucket3".to_string()],
finished: false,
};
assert_eq!(healing_disk.id, "heal-001");
assert_eq!(healing_disk.heal_id, "heal-session-123");
assert_eq!(healing_disk.pool_index.unwrap(), 0);
assert_eq!(healing_disk.set_index.unwrap(), 1);
assert_eq!(healing_disk.disk_index.unwrap(), 2);
assert_eq!(healing_disk.retry_attempts, 3);
assert_eq!(healing_disk.objects_total_count, 10000);
assert_eq!(healing_disk.items_healed, 8000);
assert_eq!(healing_disk.queue_buckets.len(), 2);
assert_eq!(healing_disk.healed_buckets.len(), 1);
assert!(!healing_disk.finished);
}
#[test]
fn test_backend_byte_default() {
let backend = BackendByte::default();
assert!(matches!(backend, BackendByte::Unknown));
}
#[test]
fn test_backend_byte_variants() {
let unknown = BackendByte::Unknown;
let fs = BackendByte::FS;
let erasure = BackendByte::Erasure;
// Test that all variants can be created
assert!(matches!(unknown, BackendByte::Unknown));
assert!(matches!(fs, BackendByte::FS));
assert!(matches!(erasure, BackendByte::Erasure));
}
#[test]
fn test_storage_info_creation() {
let storage_info = StorageInfo {
disks: vec![
Disk {
endpoint: "node1:9000".to_string(),
state: "online".to_string(),
..Default::default()
},
Disk {
endpoint: "node2:9000".to_string(),
state: "offline".to_string(),
..Default::default()
},
],
backend: BackendInfo::default(),
};
assert_eq!(storage_info.disks.len(), 2);
assert_eq!(storage_info.disks[0].endpoint, "node1:9000");
assert_eq!(storage_info.disks[1].state, "offline");
}
#[test]
fn test_backend_disks_new() {
let backend_disks = BackendDisks::new();
assert!(backend_disks.0.is_empty());
}
#[test]
fn test_backend_disks_sum() {
let mut backend_disks = BackendDisks::new();
backend_disks.0.insert("pool1".to_string(), 4);
backend_disks.0.insert("pool2".to_string(), 6);
backend_disks.0.insert("pool3".to_string(), 2);
assert_eq!(backend_disks.sum(), 12);
}
#[test]
fn test_backend_disks_sum_empty() {
let backend_disks = BackendDisks::new();
assert_eq!(backend_disks.sum(), 0);
}
#[test]
fn test_backend_info_default() {
let backend_info = BackendInfo::default();
assert!(matches!(backend_info.backend_type, BackendByte::Unknown));
assert_eq!(backend_info.online_disks.sum(), 0);
assert_eq!(backend_info.offline_disks.sum(), 0);
assert!(backend_info.standard_sc_data.is_empty());
assert!(backend_info.standard_sc_parities.is_empty());
assert!(backend_info.standard_sc_parity.is_none());
assert!(backend_info.rr_sc_data.is_empty());
assert!(backend_info.rr_sc_parities.is_empty());
assert!(backend_info.rr_sc_parity.is_none());
assert!(backend_info.total_sets.is_empty());
assert!(backend_info.drives_per_set.is_empty());
}
#[test]
fn test_backend_info_with_values() {
let mut online_disks = BackendDisks::new();
online_disks.0.insert("set1".to_string(), 4);
online_disks.0.insert("set2".to_string(), 4);
let mut offline_disks = BackendDisks::new();
offline_disks.0.insert("set1".to_string(), 0);
offline_disks.0.insert("set2".to_string(), 1);
let backend_info = BackendInfo {
backend_type: BackendByte::Erasure,
online_disks,
offline_disks,
standard_sc_data: vec![4, 4],
standard_sc_parities: vec![2, 2],
standard_sc_parity: Some(2),
rr_sc_data: vec![2, 2],
rr_sc_parities: vec![1, 1],
rr_sc_parity: Some(1),
total_sets: vec![2],
drives_per_set: vec![6, 6],
};
assert!(matches!(backend_info.backend_type, BackendByte::Erasure));
assert_eq!(backend_info.online_disks.sum(), 8);
assert_eq!(backend_info.offline_disks.sum(), 1);
assert_eq!(backend_info.standard_sc_data.len(), 2);
assert_eq!(backend_info.standard_sc_parity.unwrap(), 2);
assert_eq!(backend_info.total_sets.len(), 1);
assert_eq!(backend_info.drives_per_set.len(), 2);
}
#[test]
fn test_mem_stats_default() {
let mem_stats = MemStats::default();
assert_eq!(mem_stats.alloc, 0);
assert_eq!(mem_stats.total_alloc, 0);
assert_eq!(mem_stats.mallocs, 0);
assert_eq!(mem_stats.frees, 0);
assert_eq!(mem_stats.heap_alloc, 0);
}
#[test]
fn test_mem_stats_with_values() {
let mem_stats = MemStats {
alloc: 1024000,
total_alloc: 5120000,
mallocs: 1000,
frees: 800,
heap_alloc: 2048000,
};
assert_eq!(mem_stats.alloc, 1024000);
assert_eq!(mem_stats.total_alloc, 5120000);
assert_eq!(mem_stats.mallocs, 1000);
assert_eq!(mem_stats.frees, 800);
assert_eq!(mem_stats.heap_alloc, 2048000);
}
#[test]
fn test_server_properties_default() {
let server_props = ServerProperties::default();
assert!(server_props.state.is_empty());
assert!(server_props.endpoint.is_empty());
assert!(server_props.scheme.is_empty());
assert_eq!(server_props.uptime, 0);
assert!(server_props.version.is_empty());
assert!(server_props.commit_id.is_empty());
assert!(server_props.network.is_empty());
assert!(server_props.disks.is_empty());
assert_eq!(server_props.pool_number, 0);
assert!(server_props.pool_numbers.is_empty());
assert_eq!(server_props.mem_stats.alloc, 0);
assert_eq!(server_props.max_procs, 0);
assert_eq!(server_props.num_cpu, 0);
assert!(server_props.runtime_version.is_empty());
assert!(server_props.rustfs_env_vars.is_empty());
}
#[test]
fn test_server_properties_with_values() {
let mut network = HashMap::new();
network.insert("interface".to_string(), "eth0".to_string());
network.insert("ip".to_string(), "192.168.1.100".to_string());
let mut env_vars = HashMap::new();
env_vars.insert("RUSTFS_ROOT_USER".to_string(), "admin".to_string());
env_vars.insert("RUSTFS_ROOT_PASSWORD".to_string(), "password".to_string());
let server_props = ServerProperties {
state: "online".to_string(),
endpoint: "http://localhost:9000".to_string(),
scheme: "http".to_string(),
uptime: 3600,
version: "1.0.0".to_string(),
commit_id: "abc123def456".to_string(),
network,
disks: vec![Disk::default()],
pool_number: 1,
pool_numbers: vec![0, 1],
mem_stats: MemStats {
alloc: 1024000,
total_alloc: 5120000,
mallocs: 1000,
frees: 800,
heap_alloc: 2048000,
},
max_procs: 8,
num_cpu: 4,
runtime_version: "1.70.0".to_string(),
rustfs_env_vars: env_vars,
};
assert_eq!(server_props.state, "online");
assert_eq!(server_props.endpoint, "http://localhost:9000");
assert_eq!(server_props.uptime, 3600);
assert_eq!(server_props.version, "1.0.0");
assert_eq!(server_props.network.len(), 2);
assert_eq!(server_props.disks.len(), 1);
assert_eq!(server_props.pool_number, 1);
assert_eq!(server_props.pool_numbers.len(), 2);
assert_eq!(server_props.mem_stats.alloc, 1024000);
assert_eq!(server_props.max_procs, 8);
assert_eq!(server_props.num_cpu, 4);
assert_eq!(server_props.rustfs_env_vars.len(), 2);
}
#[test]
fn test_kms_default() {
let kms = Kms::default();
assert!(kms.status.is_none());
assert!(kms.encrypt.is_none());
assert!(kms.decrypt.is_none());
assert!(kms.endpoint.is_none());
assert!(kms.version.is_none());
}
#[test]
fn test_kms_with_values() {
let kms = Kms {
status: Some("enabled".to_string()),
encrypt: Some("AES256".to_string()),
decrypt: Some("AES256".to_string()),
endpoint: Some("https://kms.example.com".to_string()),
version: Some("1.0".to_string()),
};
assert_eq!(kms.status.unwrap(), "enabled");
assert_eq!(kms.encrypt.unwrap(), "AES256");
assert_eq!(kms.decrypt.unwrap(), "AES256");
assert_eq!(kms.endpoint.unwrap(), "https://kms.example.com");
assert_eq!(kms.version.unwrap(), "1.0");
}
#[test]
fn test_ldap_default() {
let ldap = Ldap::default();
assert!(ldap.status.is_none());
}
#[test]
fn test_ldap_with_values() {
let ldap = Ldap {
status: Some("enabled".to_string()),
};
assert_eq!(ldap.status.unwrap(), "enabled");
}
#[test]
fn test_status_default() {
let status = Status::default();
assert!(status.status.is_none());
}
#[test]
fn test_status_with_values() {
let status = Status {
status: Some("active".to_string()),
};
assert_eq!(status.status.unwrap(), "active");
}
#[test]
fn test_services_default() {
let services = Services::default();
assert!(services.kms.is_none());
assert!(services.kms_status.is_none());
assert!(services.ldap.is_none());
assert!(services.logger.is_none());
assert!(services.audit.is_none());
assert!(services.notifications.is_none());
}
#[test]
fn test_services_with_values() {
let services = Services {
kms: Some(Kms::default()),
kms_status: Some(vec![Kms::default()]),
ldap: Some(Ldap::default()),
logger: Some(vec![HashMap::new()]),
audit: Some(vec![HashMap::new()]),
notifications: Some(vec![HashMap::new()]),
};
assert!(services.kms.is_some());
assert_eq!(services.kms_status.unwrap().len(), 1);
assert!(services.ldap.is_some());
assert_eq!(services.logger.unwrap().len(), 1);
assert_eq!(services.audit.unwrap().len(), 1);
assert_eq!(services.notifications.unwrap().len(), 1);
}
#[test]
fn test_buckets_default() {
let buckets = Buckets::default();
assert_eq!(buckets.count, 0);
assert!(buckets.error.is_none());
}
#[test]
fn test_buckets_with_values() {
let buckets = Buckets {
count: 10,
error: Some("Access denied".to_string()),
};
assert_eq!(buckets.count, 10);
assert_eq!(buckets.error.unwrap(), "Access denied");
}
#[test]
fn test_objects_default() {
let objects = Objects::default();
assert_eq!(objects.count, 0);
assert!(objects.error.is_none());
}
#[test]
fn test_versions_default() {
let versions = Versions::default();
assert_eq!(versions.count, 0);
assert!(versions.error.is_none());
}
#[test]
fn test_delete_markers_default() {
let delete_markers = DeleteMarkers::default();
assert_eq!(delete_markers.count, 0);
assert!(delete_markers.error.is_none());
}
#[test]
fn test_usage_default() {
let usage = Usage::default();
assert_eq!(usage.size, 0);
assert!(usage.error.is_none());
}
#[test]
fn test_erasure_set_info_default() {
let erasure_set = ErasureSetInfo::default();
assert_eq!(erasure_set.id, 0);
assert_eq!(erasure_set.raw_usage, 0);
assert_eq!(erasure_set.raw_capacity, 0);
assert_eq!(erasure_set.usage, 0);
assert_eq!(erasure_set.objects_count, 0);
assert_eq!(erasure_set.versions_count, 0);
assert_eq!(erasure_set.delete_markers_count, 0);
assert_eq!(erasure_set.heal_disks, 0);
}
#[test]
fn test_erasure_set_info_with_values() {
let erasure_set = ErasureSetInfo {
id: 1,
raw_usage: 1000000000,
raw_capacity: 2000000000,
usage: 800000000,
objects_count: 10000,
versions_count: 15000,
delete_markers_count: 500,
heal_disks: 2,
};
assert_eq!(erasure_set.id, 1);
assert_eq!(erasure_set.raw_usage, 1000000000);
assert_eq!(erasure_set.raw_capacity, 2000000000);
assert_eq!(erasure_set.usage, 800000000);
assert_eq!(erasure_set.objects_count, 10000);
assert_eq!(erasure_set.versions_count, 15000);
assert_eq!(erasure_set.delete_markers_count, 500);
assert_eq!(erasure_set.heal_disks, 2);
}
#[test]
fn test_backend_type_default() {
let backend_type = BackendType::default();
assert!(matches!(backend_type, BackendType::FsType));
}
#[test]
fn test_backend_type_variants() {
let fs_type = BackendType::FsType;
let erasure_type = BackendType::ErasureType;
assert!(matches!(fs_type, BackendType::FsType));
assert!(matches!(erasure_type, BackendType::ErasureType));
}
#[test]
fn test_fs_backend_creation() {
let fs_backend = FSBackend {
backend_type: BackendType::FsType,
};
assert!(matches!(fs_backend.backend_type, BackendType::FsType));
}
#[test]
fn test_erasure_backend_default() {
let erasure_backend = ErasureBackend::default();
assert!(matches!(erasure_backend.backend_type, BackendType::FsType));
assert_eq!(erasure_backend.online_disks, 0);
assert_eq!(erasure_backend.offline_disks, 0);
assert!(erasure_backend.standard_sc_parity.is_none());
assert!(erasure_backend.rr_sc_parity.is_none());
assert!(erasure_backend.total_sets.is_empty());
assert!(erasure_backend.drives_per_set.is_empty());
}
#[test]
fn test_erasure_backend_with_values() {
let erasure_backend = ErasureBackend {
backend_type: BackendType::ErasureType,
online_disks: 8,
offline_disks: 0,
standard_sc_parity: Some(2),
rr_sc_parity: Some(1),
total_sets: vec![2],
drives_per_set: vec![4, 4],
};
assert!(matches!(erasure_backend.backend_type, BackendType::ErasureType));
assert_eq!(erasure_backend.online_disks, 8);
assert_eq!(erasure_backend.offline_disks, 0);
assert_eq!(erasure_backend.standard_sc_parity.unwrap(), 2);
assert_eq!(erasure_backend.rr_sc_parity.unwrap(), 1);
assert_eq!(erasure_backend.total_sets.len(), 1);
assert_eq!(erasure_backend.drives_per_set.len(), 2);
}
#[test]
fn test_info_message_creation() {
let mut pools = HashMap::new();
let mut pool_sets = HashMap::new();
pool_sets.insert(0, ErasureSetInfo::default());
pools.insert(0, pool_sets);
let info_message = InfoMessage {
mode: Some("distributed".to_string()),
domain: Some(vec!["example.com".to_string()]),
region: Some("us-east-1".to_string()),
sqs_arn: Some(vec!["arn:aws:sqs:us-east-1:123456789012:test-queue".to_string()]),
deployment_id: Some("deployment-123".to_string()),
buckets: Some(Buckets { count: 5, error: None }),
objects: Some(Objects { count: 1000, error: None }),
versions: Some(Versions { count: 1200, error: None }),
delete_markers: Some(DeleteMarkers { count: 50, error: None }),
usage: Some(Usage { size: 1000000000, error: None }),
services: Some(Services::default()),
backend: Some(ErasureBackend::default()),
servers: Some(vec![ServerProperties::default()]),
pools: Some(pools),
};
assert_eq!(info_message.mode.unwrap(), "distributed");
assert_eq!(info_message.domain.unwrap().len(), 1);
assert_eq!(info_message.region.unwrap(), "us-east-1");
assert_eq!(info_message.sqs_arn.unwrap().len(), 1);
assert_eq!(info_message.deployment_id.unwrap(), "deployment-123");
assert_eq!(info_message.buckets.unwrap().count, 5);
assert_eq!(info_message.objects.unwrap().count, 1000);
assert_eq!(info_message.versions.unwrap().count, 1200);
assert_eq!(info_message.delete_markers.unwrap().count, 50);
assert_eq!(info_message.usage.unwrap().size, 1000000000);
assert!(info_message.services.is_some());
assert_eq!(info_message.servers.unwrap().len(), 1);
assert_eq!(info_message.pools.unwrap().len(), 1);
}
#[test]
fn test_serialization_deserialization() {
let disk = Disk {
endpoint: "http://localhost:9000".to_string(),
state: "online".to_string(),
total_space: 1000000000,
used_space: 500000000,
..Default::default()
};
let json = serde_json::to_string(&disk).unwrap();
let deserialized: Disk = serde_json::from_str(&json).unwrap();
assert_eq!(deserialized.endpoint, "http://localhost:9000");
assert_eq!(deserialized.state, "online");
assert_eq!(deserialized.total_space, 1000000000);
assert_eq!(deserialized.used_space, 500000000);
}
#[test]
fn test_debug_format_all_structures() {
let item_state = ItemState::Online;
let disk_metrics = DiskMetrics::default();
let disk = Disk::default();
let healing_disk = HealingDisk::default();
let backend_byte = BackendByte::default();
let storage_info = StorageInfo {
disks: vec![],
backend: BackendInfo::default(),
};
let backend_info = BackendInfo::default();
let mem_stats = MemStats::default();
let server_props = ServerProperties::default();
// Test that all structures can be formatted with Debug
assert!(!format!("{:?}", item_state).is_empty());
assert!(!format!("{:?}", disk_metrics).is_empty());
assert!(!format!("{:?}", disk).is_empty());
assert!(!format!("{:?}", healing_disk).is_empty());
assert!(!format!("{:?}", backend_byte).is_empty());
assert!(!format!("{:?}", storage_info).is_empty());
assert!(!format!("{:?}", backend_info).is_empty());
assert!(!format!("{:?}", mem_stats).is_empty());
assert!(!format!("{:?}", server_props).is_empty());
}
#[test]
fn test_memory_efficiency() {
// Test that structures don't use excessive memory
assert!(std::mem::size_of::<ItemState>() < 100);
assert!(std::mem::size_of::<BackendByte>() < 100);
assert!(std::mem::size_of::<BackendType>() < 100);
assert!(std::mem::size_of::<MemStats>() < 1000);
assert!(std::mem::size_of::<Buckets>() < 1000);
assert!(std::mem::size_of::<Objects>() < 1000);
assert!(std::mem::size_of::<Usage>() < 1000);
}
#[test]
fn test_constants() {
assert_eq!(ITEM_OFFLINE, "offline");
assert_eq!(ITEM_INITIALIZING, "initializing");
assert_eq!(ITEM_ONLINE, "online");
}
}

View File

@@ -225,7 +225,7 @@ impl UpdateServiceAccountReq {
}
}
#[derive(Serialize, Deserialize, Debug, Default)]
#[derive(Debug, Serialize, Deserialize)]
pub struct AccountInfo {
pub account_name: String,
pub server: BackendInfo,
@@ -233,7 +233,7 @@ pub struct AccountInfo {
pub buckets: Vec<BucketAccessInfo>,
}
#[derive(Serialize, Deserialize, Debug, Default)]
#[derive(Debug, Serialize, Deserialize)]
pub struct BucketAccessInfo {
pub name: String,
pub size: u64,
@@ -247,7 +247,7 @@ pub struct BucketAccessInfo {
pub access: AccountAccess,
}
#[derive(Serialize, Deserialize, Debug, Default)]
#[derive(Debug, Serialize, Deserialize)]
pub struct BucketDetails {
pub versioning: bool,
pub versioning_suspended: bool,
@@ -256,8 +256,534 @@ pub struct BucketDetails {
// pub tagging: Option<Tagging>,
}
#[derive(Serialize, Deserialize, Debug, Default)]
#[derive(Debug, Serialize, Deserialize)]
pub struct AccountAccess {
pub read: bool,
pub write: bool,
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json;
use time::OffsetDateTime;
#[test]
fn test_account_status_default() {
let status = AccountStatus::default();
assert_eq!(status, AccountStatus::Disabled);
}
#[test]
fn test_account_status_as_ref() {
assert_eq!(AccountStatus::Enabled.as_ref(), "enabled");
assert_eq!(AccountStatus::Disabled.as_ref(), "disabled");
}
#[test]
fn test_account_status_try_from_valid() {
assert_eq!(AccountStatus::try_from("enabled").unwrap(), AccountStatus::Enabled);
assert_eq!(AccountStatus::try_from("disabled").unwrap(), AccountStatus::Disabled);
}
#[test]
fn test_account_status_try_from_invalid() {
let result = AccountStatus::try_from("invalid");
assert!(result.is_err());
assert!(result.unwrap_err().contains("invalid account status"));
}
#[test]
fn test_account_status_serialization() {
let enabled = AccountStatus::Enabled;
let disabled = AccountStatus::Disabled;
let enabled_json = serde_json::to_string(&enabled).unwrap();
let disabled_json = serde_json::to_string(&disabled).unwrap();
assert_eq!(enabled_json, "\"enabled\"");
assert_eq!(disabled_json, "\"disabled\"");
}
#[test]
fn test_account_status_deserialization() {
let enabled: AccountStatus = serde_json::from_str("\"enabled\"").unwrap();
let disabled: AccountStatus = serde_json::from_str("\"disabled\"").unwrap();
assert_eq!(enabled, AccountStatus::Enabled);
assert_eq!(disabled, AccountStatus::Disabled);
}
#[test]
fn test_user_auth_type_serialization() {
let builtin = UserAuthType::Builtin;
let ldap = UserAuthType::Ldap;
let builtin_json = serde_json::to_string(&builtin).unwrap();
let ldap_json = serde_json::to_string(&ldap).unwrap();
assert_eq!(builtin_json, "\"builtin\"");
assert_eq!(ldap_json, "\"ldap\"");
}
#[test]
fn test_user_auth_info_creation() {
let auth_info = UserAuthInfo {
auth_type: UserAuthType::Ldap,
auth_server: Some("ldap.example.com".to_string()),
auth_server_user_id: Some("user123".to_string()),
};
assert!(matches!(auth_info.auth_type, UserAuthType::Ldap));
assert_eq!(auth_info.auth_server.unwrap(), "ldap.example.com");
assert_eq!(auth_info.auth_server_user_id.unwrap(), "user123");
}
#[test]
fn test_user_auth_info_serialization() {
let auth_info = UserAuthInfo {
auth_type: UserAuthType::Builtin,
auth_server: None,
auth_server_user_id: None,
};
let json = serde_json::to_string(&auth_info).unwrap();
assert!(json.contains("builtin"));
assert!(!json.contains("authServer"), "None fields should be skipped");
}
#[test]
fn test_user_info_default() {
let user_info = UserInfo::default();
assert!(user_info.auth_info.is_none());
assert!(user_info.secret_key.is_none());
assert!(user_info.policy_name.is_none());
assert_eq!(user_info.status, AccountStatus::Disabled);
assert!(user_info.member_of.is_none());
assert!(user_info.updated_at.is_none());
}
#[test]
fn test_user_info_with_values() {
let now = OffsetDateTime::now_utc();
let user_info = UserInfo {
auth_info: Some(UserAuthInfo {
auth_type: UserAuthType::Builtin,
auth_server: None,
auth_server_user_id: None,
}),
secret_key: Some("secret123".to_string()),
policy_name: Some("ReadOnlyAccess".to_string()),
status: AccountStatus::Enabled,
member_of: Some(vec!["group1".to_string(), "group2".to_string()]),
updated_at: Some(now),
};
assert!(user_info.auth_info.is_some());
assert_eq!(user_info.secret_key.unwrap(), "secret123");
assert_eq!(user_info.policy_name.unwrap(), "ReadOnlyAccess");
assert_eq!(user_info.status, AccountStatus::Enabled);
assert_eq!(user_info.member_of.unwrap().len(), 2);
assert!(user_info.updated_at.is_some());
}
#[test]
fn test_add_or_update_user_req_creation() {
let req = AddOrUpdateUserReq {
secret_key: "newsecret".to_string(),
policy: Some("FullAccess".to_string()),
status: AccountStatus::Enabled,
};
assert_eq!(req.secret_key, "newsecret");
assert_eq!(req.policy.unwrap(), "FullAccess");
assert_eq!(req.status, AccountStatus::Enabled);
}
#[test]
fn test_service_account_info_creation() {
let now = OffsetDateTime::now_utc();
let service_account = ServiceAccountInfo {
parent_user: "admin".to_string(),
account_status: "enabled".to_string(),
implied_policy: true,
access_key: "AKIAIOSFODNN7EXAMPLE".to_string(),
name: Some("test-service".to_string()),
description: Some("Test service account".to_string()),
expiration: Some(now),
};
assert_eq!(service_account.parent_user, "admin");
assert_eq!(service_account.account_status, "enabled");
assert!(service_account.implied_policy);
assert_eq!(service_account.access_key, "AKIAIOSFODNN7EXAMPLE");
assert_eq!(service_account.name.unwrap(), "test-service");
assert!(service_account.expiration.is_some());
}
#[test]
fn test_list_service_accounts_resp_creation() {
let resp = ListServiceAccountsResp {
accounts: vec![
ServiceAccountInfo {
parent_user: "user1".to_string(),
account_status: "enabled".to_string(),
implied_policy: false,
access_key: "KEY1".to_string(),
name: Some("service1".to_string()),
description: None,
expiration: None,
},
ServiceAccountInfo {
parent_user: "user2".to_string(),
account_status: "disabled".to_string(),
implied_policy: true,
access_key: "KEY2".to_string(),
name: Some("service2".to_string()),
description: Some("Second service".to_string()),
expiration: None,
},
],
};
assert_eq!(resp.accounts.len(), 2);
assert_eq!(resp.accounts[0].parent_user, "user1");
assert_eq!(resp.accounts[1].account_status, "disabled");
}
#[test]
fn test_add_service_account_req_validate_success() {
let req = AddServiceAccountReq {
policy: Some("ReadOnlyAccess".to_string()),
target_user: Some("testuser".to_string()),
access_key: "AKIAIOSFODNN7EXAMPLE".to_string(),
secret_key: "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY".to_string(),
name: Some("test-service".to_string()),
description: Some("Test service account".to_string()),
expiration: None,
};
let result = req.validate();
assert!(result.is_ok());
}
#[test]
fn test_add_service_account_req_validate_empty_access_key() {
let req = AddServiceAccountReq {
policy: None,
target_user: None,
access_key: "".to_string(),
secret_key: "secret".to_string(),
name: Some("test".to_string()),
description: None,
expiration: None,
};
let result = req.validate();
assert!(result.is_err());
assert!(result.unwrap_err().contains("accessKey is empty"));
}
#[test]
fn test_add_service_account_req_validate_empty_secret_key() {
let req = AddServiceAccountReq {
policy: None,
target_user: None,
access_key: "AKIAIOSFODNN7EXAMPLE".to_string(),
secret_key: "".to_string(),
name: Some("test".to_string()),
description: None,
expiration: None,
};
let result = req.validate();
assert!(result.is_err());
assert!(result.unwrap_err().contains("secretKey is empty"));
}
#[test]
fn test_add_service_account_req_validate_empty_name() {
let req = AddServiceAccountReq {
policy: None,
target_user: None,
access_key: "AKIAIOSFODNN7EXAMPLE".to_string(),
secret_key: "secret".to_string(),
name: None,
description: None,
expiration: None,
};
let result = req.validate();
assert!(result.is_err());
assert!(result.unwrap_err().contains("name is empty"));
}
#[test]
fn test_credentials_serialization() {
let now = OffsetDateTime::now_utc();
let credentials = Credentials {
access_key: "AKIAIOSFODNN7EXAMPLE",
secret_key: "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY",
session_token: Some("session123"),
expiration: Some(now),
};
let json = serde_json::to_string(&credentials).unwrap();
assert!(json.contains("AKIAIOSFODNN7EXAMPLE"));
assert!(json.contains("wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"));
assert!(json.contains("session123"));
}
#[test]
fn test_credentials_without_optional_fields() {
let credentials = Credentials {
access_key: "AKIAIOSFODNN7EXAMPLE",
secret_key: "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY",
session_token: None,
expiration: None,
};
let json = serde_json::to_string(&credentials).unwrap();
assert!(json.contains("AKIAIOSFODNN7EXAMPLE"));
assert!(!json.contains("sessionToken"), "None fields should be skipped");
assert!(!json.contains("expiration"), "None fields should be skipped");
}
#[test]
fn test_add_service_account_resp_creation() {
let credentials = Credentials {
access_key: "AKIAIOSFODNN7EXAMPLE",
secret_key: "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY",
session_token: None,
expiration: None,
};
let resp = AddServiceAccountResp { credentials };
assert_eq!(resp.credentials.access_key, "AKIAIOSFODNN7EXAMPLE");
assert_eq!(resp.credentials.secret_key, "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY");
}
#[test]
fn test_info_service_account_resp_creation() {
let now = OffsetDateTime::now_utc();
let resp = InfoServiceAccountResp {
parent_user: "admin".to_string(),
account_status: "enabled".to_string(),
implied_policy: true,
policy: Some("ReadOnlyAccess".to_string()),
name: Some("test-service".to_string()),
description: Some("Test service account".to_string()),
expiration: Some(now),
};
assert_eq!(resp.parent_user, "admin");
assert_eq!(resp.account_status, "enabled");
assert!(resp.implied_policy);
assert_eq!(resp.policy.unwrap(), "ReadOnlyAccess");
assert_eq!(resp.name.unwrap(), "test-service");
assert!(resp.expiration.is_some());
}
#[test]
fn test_update_service_account_req_validate() {
let req = UpdateServiceAccountReq {
new_policy: Some("FullAccess".to_string()),
new_secret_key: Some("newsecret".to_string()),
new_status: Some("enabled".to_string()),
new_name: Some("updated-service".to_string()),
new_description: Some("Updated description".to_string()),
new_expiration: None,
};
let result = req.validate();
assert!(result.is_ok());
}
#[test]
fn test_account_info_creation() {
use crate::BackendInfo;
let account_info = AccountInfo {
account_name: "testuser".to_string(),
server: BackendInfo::default(),
policy: serde_json::json!({"Version": "2012-10-17"}),
buckets: vec![],
};
assert_eq!(account_info.account_name, "testuser");
assert!(account_info.buckets.is_empty());
assert!(account_info.policy.is_object());
}
#[test]
fn test_bucket_access_info_creation() {
let now = OffsetDateTime::now_utc();
let mut sizes_histogram = HashMap::new();
sizes_histogram.insert("small".to_string(), 100);
sizes_histogram.insert("large".to_string(), 50);
let mut versions_histogram = HashMap::new();
versions_histogram.insert("v1".to_string(), 80);
versions_histogram.insert("v2".to_string(), 70);
let mut prefix_usage = HashMap::new();
prefix_usage.insert("logs/".to_string(), 1000000);
prefix_usage.insert("data/".to_string(), 5000000);
let bucket_info = BucketAccessInfo {
name: "test-bucket".to_string(),
size: 6000000,
objects: 150,
object_sizes_histogram: sizes_histogram,
object_versions_histogram: versions_histogram,
details: Some(BucketDetails {
versioning: true,
versioning_suspended: false,
locking: true,
replication: false,
}),
prefix_usage,
created: Some(now),
access: AccountAccess {
read: true,
write: false,
},
};
assert_eq!(bucket_info.name, "test-bucket");
assert_eq!(bucket_info.size, 6000000);
assert_eq!(bucket_info.objects, 150);
assert_eq!(bucket_info.object_sizes_histogram.len(), 2);
assert_eq!(bucket_info.object_versions_histogram.len(), 2);
assert!(bucket_info.details.is_some());
assert_eq!(bucket_info.prefix_usage.len(), 2);
assert!(bucket_info.created.is_some());
assert!(bucket_info.access.read);
assert!(!bucket_info.access.write);
}
#[test]
fn test_bucket_details_creation() {
let details = BucketDetails {
versioning: true,
versioning_suspended: false,
locking: true,
replication: true,
};
assert!(details.versioning);
assert!(!details.versioning_suspended);
assert!(details.locking);
assert!(details.replication);
}
#[test]
fn test_account_access_creation() {
let read_only = AccountAccess {
read: true,
write: false,
};
let full_access = AccountAccess {
read: true,
write: true,
};
let no_access = AccountAccess {
read: false,
write: false,
};
assert!(read_only.read && !read_only.write);
assert!(full_access.read && full_access.write);
assert!(!no_access.read && !no_access.write);
}
#[test]
fn test_serialization_deserialization_roundtrip() {
let user_info = UserInfo {
auth_info: Some(UserAuthInfo {
auth_type: UserAuthType::Ldap,
auth_server: Some("ldap.example.com".to_string()),
auth_server_user_id: Some("user123".to_string()),
}),
secret_key: Some("secret123".to_string()),
policy_name: Some("ReadOnlyAccess".to_string()),
status: AccountStatus::Enabled,
member_of: Some(vec!["group1".to_string()]),
updated_at: None,
};
let json = serde_json::to_string(&user_info).unwrap();
let deserialized: UserInfo = serde_json::from_str(&json).unwrap();
assert_eq!(deserialized.secret_key.unwrap(), "secret123");
assert_eq!(deserialized.policy_name.unwrap(), "ReadOnlyAccess");
assert_eq!(deserialized.status, AccountStatus::Enabled);
assert_eq!(deserialized.member_of.unwrap().len(), 1);
}
#[test]
fn test_debug_format_all_structures() {
let account_status = AccountStatus::Enabled;
let user_auth_type = UserAuthType::Builtin;
let user_info = UserInfo::default();
let service_account = ServiceAccountInfo {
parent_user: "test".to_string(),
account_status: "enabled".to_string(),
implied_policy: false,
access_key: "key".to_string(),
name: None,
description: None,
expiration: None,
};
// Test that all structures can be formatted with Debug
assert!(!format!("{:?}", account_status).is_empty());
assert!(!format!("{:?}", user_auth_type).is_empty());
assert!(!format!("{:?}", user_info).is_empty());
assert!(!format!("{:?}", service_account).is_empty());
}
#[test]
fn test_memory_efficiency() {
// Test that structures don't use excessive memory
assert!(std::mem::size_of::<AccountStatus>() < 100);
assert!(std::mem::size_of::<UserAuthType>() < 100);
assert!(std::mem::size_of::<UserInfo>() < 2000);
assert!(std::mem::size_of::<ServiceAccountInfo>() < 2000);
assert!(std::mem::size_of::<AccountAccess>() < 100);
}
#[test]
fn test_edge_cases() {
// Test empty strings and edge cases
let req = AddServiceAccountReq {
policy: Some("".to_string()),
target_user: Some("".to_string()),
access_key: "valid_key".to_string(),
secret_key: "valid_secret".to_string(),
name: Some("valid_name".to_string()),
description: Some("".to_string()),
expiration: None,
};
// Should still validate successfully with empty optional strings
assert!(req.validate().is_ok());
// Test very long strings
let long_string = "a".repeat(1000);
let long_req = AddServiceAccountReq {
policy: Some(long_string.clone()),
target_user: Some(long_string.clone()),
access_key: long_string.clone(),
secret_key: long_string.clone(),
name: Some(long_string.clone()),
description: Some(long_string),
expiration: None,
};
assert!(long_req.validate().is_ok());
}
}