use super::*;
#[test]
fn tier_boundaries() {
assert_eq!(MemoryTier::from_total_ram_mb(16 * 1024), MemoryTier::Medium);
assert_eq!(MemoryTier::from_total_ram_mb(24 * 1024), MemoryTier::Medium);
assert_eq!(MemoryTier::from_total_ram_mb(31 * 1024), MemoryTier::Medium);
assert_eq!(MemoryTier::from_total_ram_mb(32 * 1024), MemoryTier::Large);
assert_eq!(MemoryTier::from_total_ram_mb(63 * 1024), MemoryTier::Large);
assert_eq!(MemoryTier::from_total_ram_mb(64 * 1024), MemoryTier::XLarge);
assert_eq!(
MemoryTier::from_total_ram_mb(192 * 1024),
MemoryTier::XLarge
);
}
#[test]
fn degrade_tier_below_minimum() {
for mb in [0, 4 * 1024, 8 * 1024, 12 * 1024, 15 * 1024] {
let tier = MemoryTier::from_total_ram_mb(mb);
assert_eq!(tier, MemoryTier::Degraded, "{mb} MB must be Degraded");
assert!(tier.is_degraded());
}
assert_eq!(
MemoryTier::from_total_ram_mb(MINIMUM_SUPPORTED_RAM_MB - 1),
MemoryTier::Degraded
);
}
#[test]
fn medium_tier_at_the_minimum() {
let tier = MemoryTier::from_total_ram_mb(MINIMUM_SUPPORTED_RAM_MB);
assert_eq!(tier, MemoryTier::Medium);
assert!(!tier.is_degraded());
for t in [MemoryTier::Medium, MemoryTier::Large, MemoryTier::XLarge] {
assert!(!t.is_degraded(), "{t} must not report degraded");
}
}
#[test]
fn tier_display_strings() {
assert_eq!(MemoryTier::Degraded.to_string(), "Degraded");
assert_eq!(MemoryTier::Medium.to_string(), "Medium");
assert_eq!(MemoryTier::Large.to_string(), "Large");
assert_eq!(MemoryTier::XLarge.to_string(), "XLarge");
}
#[test]
fn batch_size_hard_cap_table() {
assert_eq!(MemoryTier::Degraded.batch_size_hard_cap(), 64);
assert_eq!(MemoryTier::Medium.batch_size_hard_cap(), 128);
assert_eq!(MemoryTier::Large.batch_size_hard_cap(), 256);
assert_eq!(MemoryTier::XLarge.batch_size_hard_cap(), 512);
}
#[test]
fn supported_target_resolves_to_medium_with_pinned_budget() {
let b = MachineBudget::from_total_ram_mb(SUPPORTED_TARGET_RAM_MB);
assert_eq!(b.total_ram_mb, 24 * 1024);
assert_eq!(b.tier, MemoryTier::Medium);
assert_eq!(b.memory_limit_mb, 6_144);
assert_eq!(b.index_memory_limit_mb, 18_432);
assert!(!b.is_below_minimum());
assert_eq!(b.minimum_advisory(), None);
}
#[test]
fn budget_is_continuous_across_tier_boundaries() {
const GB: u64 = 1024;
for gb in 15u64..=33 {
let lo = MachineBudget::from_total_ram_mb(gb * GB);
let hi = MachineBudget::from_total_ram_mb((gb + 1) * GB);
assert_eq!(
hi.memory_limit_mb - lo.memory_limit_mb,
256,
"memory_limit_mb must rise by 25% of 1 GB from {gb} GB to {} GB",
gb + 1
);
assert_eq!(
hi.index_memory_limit_mb - lo.index_memory_limit_mb,
768,
"index_memory_limit_mb must rise by 75% of 1 GB from {gb} GB to {} GB",
gb + 1
);
}
let min = MachineBudget::from_total_ram_mb(16 * GB);
assert_eq!(
(min.memory_limit_mb, min.index_memory_limit_mb),
(4_096, 12_288)
);
let large = MachineBudget::from_total_ram_mb(32 * GB);
assert_eq!(
(large.memory_limit_mb, large.index_memory_limit_mb),
(8_192, 24_576)
);
}
#[test]
fn degrade_budget_is_reduced_not_zero() {
let degraded = MachineBudget::from_total_ram_mb(12 * 1024);
assert_eq!(degraded.tier, MemoryTier::Degraded);
assert!(degraded.is_below_minimum());
assert_eq!(degraded.memory_limit_mb, 3_072);
assert_eq!(degraded.index_memory_limit_mb, 9_216);
let minimum = MachineBudget::from_total_ram_mb(MINIMUM_SUPPORTED_RAM_MB);
assert!(degraded.memory_limit_mb < minimum.memory_limit_mb);
assert!(degraded.index_memory_limit_mb < minimum.index_memory_limit_mb);
assert!(degraded.memory_limit_mb >= MEMORY_LIMIT_FLOOR_MB as usize);
assert!(degraded.index_memory_limit_mb >= degraded.memory_limit_mb);
}
#[test]
fn advisory_present_below_minimum_absent_at_or_above() {
let msg = MachineBudget::from_total_ram_mb(12 * 1024)
.minimum_advisory()
.expect("a 12 GB host must earn an advisory");
assert!(
msg.contains("12288 MB"),
"advisory must name the reading: {msg}"
);
assert!(
msg.contains("16 GB minimum"),
"advisory must name the bar: {msg}"
);
assert!(
msg.contains("Degraded"),
"advisory must name the tier: {msg}"
);
assert!(
msg.contains("24 GB"),
"advisory must name the target: {msg}"
);
for mb in [MINIMUM_SUPPORTED_RAM_MB, 24 * 1024, 128 * 1024] {
assert_eq!(
MachineBudget::from_total_ram_mb(mb).minimum_advisory(),
None,
"{mb} MB is at or above the minimum and must earn no advisory"
);
}
}
#[test]
fn memory_limit_table() {
assert_eq!(compute_memory_limit_mb(12 * 1024), 3 * 1024); assert_eq!(compute_memory_limit_mb(16 * 1024), 4 * 1024); assert_eq!(compute_memory_limit_mb(24 * 1024), 6 * 1024); assert_eq!(compute_memory_limit_mb(32 * 1024), 8 * 1024); assert_eq!(compute_memory_limit_mb(64 * 1024), 16 * 1024); assert_eq!(compute_memory_limit_mb(128 * 1024), 32 * 1024); assert_eq!(compute_memory_limit_mb(192 * 1024), 48 * 1024);
assert_eq!(
compute_memory_limit_mb(256 * 1024),
MEMORY_LIMIT_CEIL_MB as usize
);
assert_eq!(
compute_memory_limit_mb(1024 * 1024),
MEMORY_LIMIT_CEIL_MB as usize
);
assert_eq!(compute_memory_limit_mb(0), MEMORY_LIMIT_FLOOR_MB as usize);
assert_eq!(
compute_memory_limit_mb(2 * 1024),
MEMORY_LIMIT_FLOOR_MB as usize
);
assert_eq!(
compute_memory_limit_mb(4 * 1024),
MEMORY_LIMIT_FLOOR_MB as usize
);
}
#[test]
fn index_memory_limit_table() {
assert_eq!(compute_index_memory_limit_mb(12 * 1024), 9_216); assert_eq!(compute_index_memory_limit_mb(16 * 1024), 12_288); assert_eq!(compute_index_memory_limit_mb(24 * 1024), 18_432); assert_eq!(compute_index_memory_limit_mb(32 * 1024), 24_576); assert_eq!(compute_index_memory_limit_mb(64 * 1024), 49_152);
for ram_gb in [128u64, 256, 1024] {
assert_eq!(
compute_index_memory_limit_mb(ram_gb * 1024),
INDEX_MEMORY_LIMIT_CEIL_MB as usize
);
}
assert_eq!(
compute_index_memory_limit_mb(0),
INDEX_MEMORY_LIMIT_FLOOR_MB as usize
);
assert_eq!(
compute_index_memory_limit_mb(2 * 1024),
INDEX_MEMORY_LIMIT_FLOOR_MB as usize
);
for ram_gb in [8u64, 12, 16, 24, 32, 64, 128, 192, 256] {
let ram = ram_gb * 1024;
assert!(
compute_index_memory_limit_mb(ram) >= compute_memory_limit_mb(ram),
"index limit must be >= daemon limit at {ram_gb} GB"
);
}
}
#[test]
fn max_chunks_table() {
assert_eq!(compute_max_chunks(3_072), 153_600); assert_eq!(compute_max_chunks(4_096), 204_800);
assert_eq!(compute_max_chunks(6_144), 307_200); assert_eq!(compute_max_chunks(8_192), 409_600);
assert_eq!(compute_max_chunks(16_384), MAX_CHUNKS_CEIL);
assert_eq!(compute_max_chunks(65_536), MAX_CHUNKS_CEIL);
assert_eq!(compute_max_chunks(0), MAX_CHUNKS_FLOOR);
assert_eq!(compute_max_chunks(500), MAX_CHUNKS_FLOOR);
}
#[test]
fn max_batch_size_table() {
assert_eq!(compute_max_batch_size(4_096), 96);
assert_eq!(compute_max_batch_size(8_192), 192);
assert_eq!(compute_max_batch_size(16_384), 384);
assert_eq!(compute_max_batch_size(0), MIN_COMPUTED_BATCH_SIZE);
assert_eq!(compute_max_batch_size(1_024), MIN_COMPUTED_BATCH_SIZE);
assert_eq!(compute_max_batch_size(22_000), MAX_COMPUTED_BATCH_SIZE);
assert_eq!(compute_max_batch_size(64_000), MAX_COMPUTED_BATCH_SIZE);
assert_eq!(compute_max_batch_size(1_000_000), MAX_COMPUTED_BATCH_SIZE);
}
#[test]
fn ram_detection_returns_nonzero() {
let mb = detect_total_ram_mb();
#[cfg(any(target_os = "macos", target_os = "linux"))]
{
let mb = mb.expect("macOS and Linux both have a detection path");
assert!(mb > 0, "detected RAM must be > 0 MB, got {mb}");
}
#[cfg(not(any(target_os = "macos", target_os = "linux")))]
{
assert!(mb.is_none(), "unsupported platforms detect nothing");
}
}
#[test]
fn detect_returns_a_plausible_budget() {
let b = MachineBudget::detect();
assert!(b.total_ram_mb > 0);
assert!(b.memory_limit_mb >= MEMORY_LIMIT_FLOOR_MB as usize);
assert!(b.index_memory_limit_mb >= b.memory_limit_mb);
assert_eq!(b.tier, MemoryTier::from_total_ram_mb(b.total_ram_mb));
assert_eq!(b, MachineBudget::from_total_ram_mb(b.total_ram_mb));
}
#[test]
fn fallback_ram_resolves_to_a_degraded_budget() {
let b = MachineBudget::from_total_ram_mb(FALLBACK_RAM_MB);
assert_eq!(b.tier, MemoryTier::Degraded);
assert!(b.minimum_advisory().is_some());
assert!(b.memory_limit_mb >= MEMORY_LIMIT_FLOOR_MB as usize);
}