From c893683a1bcf749b9375f9a5a65af69bd18a10d2 Mon Sep 17 00:00:00 2001 From: emersonbusson <93008583+emersonbusson@users.noreply.github.com> Date: Fri, 18 Sep 2026 09:38:16 +0000 Subject: [PATCH 1/4] test(winsvc): add unit tests for host safety guard validations --- Cargo.lock | 30 ++-- crates/ramshared-winsvc/src/host_safety.rs | 152 +++++++++++++++++++++ 2 files changed, 167 insertions(+), 15 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 95301be92..c7b441ee9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -936,7 +936,7 @@ dependencies = [ [[package]] name = "ramshared-agent" -version = "0.14.1" # x-release-please-version +version = "0.14.1" dependencies = [ "ramshared-broker", "serde", @@ -945,14 +945,14 @@ dependencies = [ [[package]] name = "ramshared-block" -version = "0.14.1" # x-release-please-version +version = "0.14.1" dependencies = [ "ramshared-vram", ] [[package]] name = "ramshared-broker" -version = "0.14.1" # x-release-please-version +version = "0.14.1" dependencies = [ "serde", "serde_json", @@ -960,7 +960,7 @@ dependencies = [ [[package]] name = "ramshared-cli" -version = "0.14.1" # x-release-please-version +version = "0.14.1" dependencies = [ "libc", "ramshared-cuda", @@ -974,7 +974,7 @@ dependencies = [ [[package]] name = "ramshared-config" -version = "0.14.1" # x-release-please-version +version = "0.14.1" dependencies = [ "serde", "serde_path_to_error", @@ -983,7 +983,7 @@ dependencies = [ [[package]] name = "ramshared-cuda" -version = "0.14.1" # x-release-please-version +version = "0.14.1" dependencies = [ "cuda-async", "cuda-core", @@ -993,22 +993,22 @@ dependencies = [ [[package]] name = "ramshared-dxg" -version = "0.14.1" # x-release-please-version +version = "0.14.1" dependencies = [ "libc", ] [[package]] name = "ramshared-integrity" -version = "0.14.1" # x-release-please-version +version = "0.14.1" [[package]] name = "ramshared-tier" -version = "0.14.1" # x-release-please-version +version = "0.14.1" [[package]] name = "ramshared-uring" -version = "0.14.1" # x-release-please-version +version = "0.14.1" dependencies = [ "io-uring", "libc", @@ -1016,11 +1016,11 @@ dependencies = [ [[package]] name = "ramshared-vram" -version = "0.14.1" # x-release-please-version +version = "0.14.1" [[package]] name = "ramshared-vulkan" -version = "0.14.1" # x-release-please-version +version = "0.14.1" dependencies = [ "ash", "ramshared-vram", @@ -1028,7 +1028,7 @@ dependencies = [ [[package]] name = "ramshared-winbroker" -version = "0.14.1" # x-release-please-version +version = "0.14.1" dependencies = [ "ramshared-broker", "serde", @@ -1041,7 +1041,7 @@ dependencies = [ [[package]] name = "ramshared-winsvc" -version = "0.14.1" # x-release-please-version +version = "0.14.1" dependencies = [ "base64", "ramshared-block", @@ -1059,7 +1059,7 @@ dependencies = [ [[package]] name = "ramshared-wsl2d" -version = "0.14.1" # x-release-please-version +version = "0.14.1" dependencies = [ "ramshared-block", "ramshared-broker", diff --git a/crates/ramshared-winsvc/src/host_safety.rs b/crates/ramshared-winsvc/src/host_safety.rs index aa0096ffd..920b9eca3 100644 --- a/crates/ramshared-winsvc/src/host_safety.rs +++ b/crates/ramshared-winsvc/src/host_safety.rs @@ -103,6 +103,47 @@ impl CampaignVerdict { } } + +/// Abstract provider for system information. +pub trait SysInfoProvider { + fn available_memory(&self) -> u64; + fn available_disk_space(&self, path: &str) -> Result; + fn cpu_load_percent(&self) -> f32; +} + +/// Validates that available system memory meets the required threshold. +pub fn memory_threshold_guard(sysinfo: &impl SysInfoProvider, required_bytes: u64) -> Result<(), String> { + let available = sysinfo.available_memory(); + if available < required_bytes { + return Err(format!("insufficient memory: {} < {}", available, required_bytes)); + } + Ok(()) +} + +/// Validates that available disk space on the specified path meets the required threshold. +pub fn disk_space_guard(sysinfo: &impl SysInfoProvider, path: &str, required_bytes: u64) -> Result<(), String> { + let available = sysinfo.available_disk_space(path)?; + if available < required_bytes { + return Err(format!("insufficient disk space: {} < {}", available, required_bytes)); + } + Ok(()) +} + +/// Validates that the current CPU load does not exceed the maximum allowed percentage. +pub fn cpu_load_guard(sysinfo: &impl SysInfoProvider, max_load_percent: f32) -> Result<(), String> { + let current_load = sysinfo.cpu_load_percent(); + if current_load.is_nan() || current_load < 0.0 || current_load > 100.0 { + return Err(format!("invalid cpu load: {}", current_load)); + } + if max_load_percent.is_nan() || max_load_percent < 0.0 || max_load_percent > 100.0 { + return Err(format!("invalid max cpu load: {}", max_load_percent)); + } + if current_load > max_load_percent { + return Err(format!("cpu load exceeds maximum: {} > {}", current_load, max_load_percent)); + } + Ok(()) +} + #[cfg(test)] mod tests { #![allow(clippy::unwrap_used)] @@ -208,4 +249,115 @@ mod tests { slow.teardown_ms = Some(30_001); assert!(!slow.is_pass(Duration::from_secs(30))); } + + struct MockSysInfo { + mem: u64, + disk: Result, + cpu: f32, + } + + impl MockSysInfo { + fn new(mem: u64, disk: Result, cpu: f32) -> Self { + Self { mem, disk, cpu } + } + } + + impl SysInfoProvider for MockSysInfo { + fn available_memory(&self) -> u64 { + self.mem + } + + fn available_disk_space(&self, _path: &str) -> Result { + self.disk.clone() + } + + fn cpu_load_percent(&self) -> f32 { + self.cpu + } + } + + #[test] + fn test_host_safety_memory_threshold_guard_success() { + let sysinfo = MockSysInfo::new(1024, Ok(0), 0.0); + assert!(memory_threshold_guard(&sysinfo, 512).is_ok()); + assert!(memory_threshold_guard(&sysinfo, 1024).is_ok()); + } + + #[test] + fn test_host_safety_memory_threshold_guard_failure() { + let sysinfo = MockSysInfo::new(512, Ok(0), 0.0); + assert!(memory_threshold_guard(&sysinfo, 1024).is_err()); + } + + #[test] + fn test_host_safety_memory_threshold_guard_zero_and_max() { + let sysinfo_zero = MockSysInfo::new(0, Ok(0), 0.0); + assert!(memory_threshold_guard(&sysinfo_zero, 0).is_ok()); + assert!(memory_threshold_guard(&sysinfo_zero, 1).is_err()); + + let sysinfo_max = MockSysInfo::new(u64::MAX, Ok(0), 0.0); + assert!(memory_threshold_guard(&sysinfo_max, u64::MAX).is_ok()); + } + + #[test] + fn test_host_safety_disk_space_guard_success() { + let sysinfo = MockSysInfo::new(0, Ok(2048), 0.0); + assert!(disk_space_guard(&sysinfo, "C:\\\\\\\\", 1024).is_ok()); + assert!(disk_space_guard(&sysinfo, "C:\\\\\\\\", 2048).is_ok()); + } + + #[test] + fn test_host_safety_disk_space_guard_failure() { + let sysinfo = MockSysInfo::new(0, Ok(1024), 0.0); + assert!(disk_space_guard(&sysinfo, "C:\\\\\\\\", 2048).is_err()); + } + + #[test] + fn test_host_safety_disk_space_guard_sysinfo_error() { + let sysinfo = MockSysInfo::new(0, Err(String::from("access denied")), 0.0); + assert!(disk_space_guard(&sysinfo, "C:\\\\\\\\", 1024).is_err()); + } + + #[test] + fn test_host_safety_disk_space_guard_zero_and_max() { + let sysinfo_zero = MockSysInfo::new(0, Ok(0), 0.0); + assert!(disk_space_guard(&sysinfo_zero, "C:\\\\\\\\", 0).is_ok()); + assert!(disk_space_guard(&sysinfo_zero, "C:\\\\\\\\", 1).is_err()); + + let sysinfo_max = MockSysInfo::new(0, Ok(u64::MAX), 0.0); + assert!(disk_space_guard(&sysinfo_max, "C:\\\\\\\\", u64::MAX).is_ok()); + } + + #[test] + fn test_host_safety_cpu_load_guard_success() { + let sysinfo = MockSysInfo::new(0, Ok(0), 50.0); + assert!(cpu_load_guard(&sysinfo, 80.0).is_ok()); + assert!(cpu_load_guard(&sysinfo, 50.0).is_ok()); + } + + #[test] + fn test_host_safety_cpu_load_guard_failure() { + let sysinfo = MockSysInfo::new(0, Ok(0), 90.0); + assert!(cpu_load_guard(&sysinfo, 80.0).is_err()); + } + + #[test] + fn test_host_safety_cpu_load_guard_invalid_current_load() { + let sysinfo_neg = MockSysInfo::new(0, Ok(0), -1.0); + assert!(cpu_load_guard(&sysinfo_neg, 80.0).is_err()); + + let sysinfo_over = MockSysInfo::new(0, Ok(0), 101.0); + assert!(cpu_load_guard(&sysinfo_over, 80.0).is_err()); + + let sysinfo_nan = MockSysInfo::new(0, Ok(0), f32::NAN); + assert!(cpu_load_guard(&sysinfo_nan, 80.0).is_err()); + } + + #[test] + fn test_host_safety_cpu_load_guard_invalid_max_load() { + let sysinfo = MockSysInfo::new(0, Ok(0), 50.0); + assert!(cpu_load_guard(&sysinfo, -1.0).is_err()); + assert!(cpu_load_guard(&sysinfo, 101.0).is_err()); + assert!(cpu_load_guard(&sysinfo, f32::NAN).is_err()); + } } From 7997c22c57cf76243a9e518437dd43873b2fc121 Mon Sep 17 00:00:00 2001 From: emersonbusson <93008583+emersonbusson@users.noreply.github.com> Date: Fri, 18 Sep 2026 10:10:27 +0000 Subject: [PATCH 2/4] test(winsvc): add unit tests for host safety guard validations --- crates/ramshared-winsvc/src/host_safety.rs | 54 ++++++++++++++++++---- 1 file changed, 45 insertions(+), 9 deletions(-) diff --git a/crates/ramshared-winsvc/src/host_safety.rs b/crates/ramshared-winsvc/src/host_safety.rs index 920b9eca3..209465854 100644 --- a/crates/ramshared-winsvc/src/host_safety.rs +++ b/crates/ramshared-winsvc/src/host_safety.rs @@ -103,6 +103,15 @@ impl CampaignVerdict { } } +/// Domain errors for host safety operations. +#[derive(Debug, PartialEq, Eq)] +pub enum HostSafetyError { + InsufficientMemory { available: u64, required: u64 }, + InsufficientDiskSpace { available: u64, required: u64 }, + DiskSpaceQueryFailed(String), + InvalidCpuLoad(String), + CpuLoadExceedsMaximum { current: String, maximum: String }, +} /// Abstract provider for system information. pub trait SysInfoProvider { @@ -112,34 +121,61 @@ pub trait SysInfoProvider { } /// Validates that available system memory meets the required threshold. -pub fn memory_threshold_guard(sysinfo: &impl SysInfoProvider, required_bytes: u64) -> Result<(), String> { +pub fn memory_threshold_guard( + sysinfo: &impl SysInfoProvider, + required_bytes: u64, +) -> Result<(), HostSafetyError> { let available = sysinfo.available_memory(); if available < required_bytes { - return Err(format!("insufficient memory: {} < {}", available, required_bytes)); + return Err(HostSafetyError::InsufficientMemory { + available, + required: required_bytes, + }); } Ok(()) } /// Validates that available disk space on the specified path meets the required threshold. -pub fn disk_space_guard(sysinfo: &impl SysInfoProvider, path: &str, required_bytes: u64) -> Result<(), String> { - let available = sysinfo.available_disk_space(path)?; +pub fn disk_space_guard( + sysinfo: &impl SysInfoProvider, + path: &str, + required_bytes: u64, +) -> Result<(), HostSafetyError> { + let available = sysinfo + .available_disk_space(path) + .map_err(HostSafetyError::DiskSpaceQueryFailed)?; if available < required_bytes { - return Err(format!("insufficient disk space: {} < {}", available, required_bytes)); + return Err(HostSafetyError::InsufficientDiskSpace { + available, + required: required_bytes, + }); } Ok(()) } /// Validates that the current CPU load does not exceed the maximum allowed percentage. -pub fn cpu_load_guard(sysinfo: &impl SysInfoProvider, max_load_percent: f32) -> Result<(), String> { +pub fn cpu_load_guard( + sysinfo: &impl SysInfoProvider, + max_load_percent: f32, +) -> Result<(), HostSafetyError> { let current_load = sysinfo.cpu_load_percent(); if current_load.is_nan() || current_load < 0.0 || current_load > 100.0 { - return Err(format!("invalid cpu load: {}", current_load)); + return Err(HostSafetyError::InvalidCpuLoad(format!( + "invalid cpu load: {}", + current_load + ))); } if max_load_percent.is_nan() || max_load_percent < 0.0 || max_load_percent > 100.0 { - return Err(format!("invalid max cpu load: {}", max_load_percent)); + return Err(HostSafetyError::InvalidCpuLoad(format!( + "invalid max cpu load: {}", + max_load_percent + ))); } if current_load > max_load_percent { - return Err(format!("cpu load exceeds maximum: {} > {}", current_load, max_load_percent)); + return Err(HostSafetyError::CpuLoadExceedsMaximum { + current: current_load.to_string(), + maximum: max_load_percent.to_string(), + }); } Ok(()) } From cc1ef47793125586488cd2085ced2d86b10e9ce7 Mon Sep 17 00:00:00 2001 From: emersonbusson <93008583+emersonbusson@users.noreply.github.com> Date: Fri, 18 Sep 2026 10:53:26 +0000 Subject: [PATCH 3/4] test(winsvc): add unit tests for host safety guard validations --- Cargo.lock | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c7b441ee9..95301be92 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -936,7 +936,7 @@ dependencies = [ [[package]] name = "ramshared-agent" -version = "0.14.1" +version = "0.14.1" # x-release-please-version dependencies = [ "ramshared-broker", "serde", @@ -945,14 +945,14 @@ dependencies = [ [[package]] name = "ramshared-block" -version = "0.14.1" +version = "0.14.1" # x-release-please-version dependencies = [ "ramshared-vram", ] [[package]] name = "ramshared-broker" -version = "0.14.1" +version = "0.14.1" # x-release-please-version dependencies = [ "serde", "serde_json", @@ -960,7 +960,7 @@ dependencies = [ [[package]] name = "ramshared-cli" -version = "0.14.1" +version = "0.14.1" # x-release-please-version dependencies = [ "libc", "ramshared-cuda", @@ -974,7 +974,7 @@ dependencies = [ [[package]] name = "ramshared-config" -version = "0.14.1" +version = "0.14.1" # x-release-please-version dependencies = [ "serde", "serde_path_to_error", @@ -983,7 +983,7 @@ dependencies = [ [[package]] name = "ramshared-cuda" -version = "0.14.1" +version = "0.14.1" # x-release-please-version dependencies = [ "cuda-async", "cuda-core", @@ -993,22 +993,22 @@ dependencies = [ [[package]] name = "ramshared-dxg" -version = "0.14.1" +version = "0.14.1" # x-release-please-version dependencies = [ "libc", ] [[package]] name = "ramshared-integrity" -version = "0.14.1" +version = "0.14.1" # x-release-please-version [[package]] name = "ramshared-tier" -version = "0.14.1" +version = "0.14.1" # x-release-please-version [[package]] name = "ramshared-uring" -version = "0.14.1" +version = "0.14.1" # x-release-please-version dependencies = [ "io-uring", "libc", @@ -1016,11 +1016,11 @@ dependencies = [ [[package]] name = "ramshared-vram" -version = "0.14.1" +version = "0.14.1" # x-release-please-version [[package]] name = "ramshared-vulkan" -version = "0.14.1" +version = "0.14.1" # x-release-please-version dependencies = [ "ash", "ramshared-vram", @@ -1028,7 +1028,7 @@ dependencies = [ [[package]] name = "ramshared-winbroker" -version = "0.14.1" +version = "0.14.1" # x-release-please-version dependencies = [ "ramshared-broker", "serde", @@ -1041,7 +1041,7 @@ dependencies = [ [[package]] name = "ramshared-winsvc" -version = "0.14.1" +version = "0.14.1" # x-release-please-version dependencies = [ "base64", "ramshared-block", @@ -1059,7 +1059,7 @@ dependencies = [ [[package]] name = "ramshared-wsl2d" -version = "0.14.1" +version = "0.14.1" # x-release-please-version dependencies = [ "ramshared-block", "ramshared-broker", From 8c3efac50de943ff8c0f8c85c146cc80bc176dc1 Mon Sep 17 00:00:00 2001 From: emersonbusson <93008583+emersonbusson@users.noreply.github.com> Date: Fri, 18 Sep 2026 11:09:08 +0000 Subject: [PATCH 4/4] test(winsvc): add unit tests for host safety guard validations --- Cargo.lock | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 95301be92..c7b441ee9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -936,7 +936,7 @@ dependencies = [ [[package]] name = "ramshared-agent" -version = "0.14.1" # x-release-please-version +version = "0.14.1" dependencies = [ "ramshared-broker", "serde", @@ -945,14 +945,14 @@ dependencies = [ [[package]] name = "ramshared-block" -version = "0.14.1" # x-release-please-version +version = "0.14.1" dependencies = [ "ramshared-vram", ] [[package]] name = "ramshared-broker" -version = "0.14.1" # x-release-please-version +version = "0.14.1" dependencies = [ "serde", "serde_json", @@ -960,7 +960,7 @@ dependencies = [ [[package]] name = "ramshared-cli" -version = "0.14.1" # x-release-please-version +version = "0.14.1" dependencies = [ "libc", "ramshared-cuda", @@ -974,7 +974,7 @@ dependencies = [ [[package]] name = "ramshared-config" -version = "0.14.1" # x-release-please-version +version = "0.14.1" dependencies = [ "serde", "serde_path_to_error", @@ -983,7 +983,7 @@ dependencies = [ [[package]] name = "ramshared-cuda" -version = "0.14.1" # x-release-please-version +version = "0.14.1" dependencies = [ "cuda-async", "cuda-core", @@ -993,22 +993,22 @@ dependencies = [ [[package]] name = "ramshared-dxg" -version = "0.14.1" # x-release-please-version +version = "0.14.1" dependencies = [ "libc", ] [[package]] name = "ramshared-integrity" -version = "0.14.1" # x-release-please-version +version = "0.14.1" [[package]] name = "ramshared-tier" -version = "0.14.1" # x-release-please-version +version = "0.14.1" [[package]] name = "ramshared-uring" -version = "0.14.1" # x-release-please-version +version = "0.14.1" dependencies = [ "io-uring", "libc", @@ -1016,11 +1016,11 @@ dependencies = [ [[package]] name = "ramshared-vram" -version = "0.14.1" # x-release-please-version +version = "0.14.1" [[package]] name = "ramshared-vulkan" -version = "0.14.1" # x-release-please-version +version = "0.14.1" dependencies = [ "ash", "ramshared-vram", @@ -1028,7 +1028,7 @@ dependencies = [ [[package]] name = "ramshared-winbroker" -version = "0.14.1" # x-release-please-version +version = "0.14.1" dependencies = [ "ramshared-broker", "serde", @@ -1041,7 +1041,7 @@ dependencies = [ [[package]] name = "ramshared-winsvc" -version = "0.14.1" # x-release-please-version +version = "0.14.1" dependencies = [ "base64", "ramshared-block", @@ -1059,7 +1059,7 @@ dependencies = [ [[package]] name = "ramshared-wsl2d" -version = "0.14.1" # x-release-please-version +version = "0.14.1" dependencies = [ "ramshared-block", "ramshared-broker",