use super::*;
use std::time::{Duration, Instant};
fn create_test_scheduler() -> PredictiveScheduler {
let config = PredictiveSchedulerConfig::default();
let mut scheduler = PredictiveScheduler::new(config);
let mut on_demand = HostProfile::new("host-1", InstanceType::OnDemand);
on_demand.compute_capacity = 1000.0;
on_demand.current_load = 0.2;
on_demand.hourly_cost = 1.0;
scheduler.register_host(on_demand);
let mut spot = HostProfile::new("host-2", InstanceType::Spot);
spot.compute_capacity = 1000.0;
spot.current_load = 0.1;
spot.hourly_cost = 1.0;
scheduler.register_host(spot);
let mut reserved = HostProfile::new("host-3", InstanceType::Reserved);
reserved.compute_capacity = 2000.0;
reserved.current_load = 0.5;
reserved.hourly_cost = 1.5;
scheduler.register_host(reserved);
scheduler
}
#[test]
fn test_host_cost_per_op() {
let host = HostProfile::new("test", InstanceType::OnDemand);
let cost = host.cost_per_op();
assert!(cost > 0.0);
assert!(cost < f64::MAX);
}
#[test]
fn test_spot_instance_discount() {
let on_demand = HostProfile::new("od", InstanceType::OnDemand);
let spot = HostProfile::new("spot", InstanceType::Spot);
let od_cost = on_demand.cost_per_op();
let spot_cost = spot.cost_per_op();
assert!(spot_cost < od_cost);
assert!((spot_cost / od_cost - 0.3).abs() < 0.01);
}
#[test]
fn test_workload_execution_time_estimate() {
let mut host = HostProfile::new("test", InstanceType::OnDemand);
host.compute_capacity = 1000.0;
host.current_load = 0.0;
let mut workload = WorkloadSpec::new("w1", 1000);
workload.operation_count = 1000;
let time = workload.estimated_execution_time(&host);
assert!((time.as_secs_f64() - 1.0).abs() < 0.01);
}
#[test]
fn test_scheduling_prefers_available_capacity() {
let mut scheduler = create_test_scheduler();
scheduler.update_host_load("host-2", 0.7);
let mut workload = WorkloadSpec::new("w1", 100);
workload.slo_deadline = Duration::from_secs(1);
let decision = scheduler.schedule(&workload).unwrap();
assert!(decision.slo_compliance_prob > 0.9);
}
#[test]
fn test_scheduling_respects_preemption_buffer() {
let mut scheduler = create_test_scheduler();
scheduler.update_preemption_deadline(
"host-2",
Some(Instant::now() + Duration::from_secs(60)), );
let workload = WorkloadSpec::new("w1", 100);
let decision = scheduler.schedule(&workload).unwrap();
assert_ne!(decision.host_id, "host-2");
}
#[test]
fn test_slo_compliance_prediction() {
let scheduler = create_test_scheduler();
let host = scheduler.get_host("host-1").unwrap();
let mut easy = WorkloadSpec::new("easy", 100);
easy.slo_deadline = Duration::from_secs(10);
let mut hard = WorkloadSpec::new("hard", 10000);
hard.slo_deadline = Duration::from_millis(100);
let easy_time = easy.estimated_execution_time(host);
let hard_time = hard.estimated_execution_time(host);
let easy_prob = scheduler.predict_slo_compliance(host, &easy, easy_time);
let hard_prob = scheduler.predict_slo_compliance(host, &hard, hard_time);
assert!(easy_prob > hard_prob);
assert!(easy_prob > 0.9);
}
#[test]
fn test_result_recording_updates_history() {
let mut scheduler = create_test_scheduler();
for i in 0..5 {
scheduler.record_result("host-1", Duration::from_millis(100 + i * 10), false, 0.01);
}
scheduler.record_result("host-1", Duration::from_millis(500), true, 0.05);
assert_eq!(scheduler.metrics().total_decisions, 0); assert_eq!(scheduler.metrics().slo_violations, 1);
assert!(scheduler.metrics().total_cost > 0.0);
}
#[test]
fn test_rebalancing_suggestions() {
let mut scheduler = create_test_scheduler();
scheduler.update_host_load("host-1", 0.9);
scheduler.update_host_load("host-2", 0.1);
let suggestions = scheduler.suggest_rebalancing();
assert!(!suggestions.is_empty());
assert_eq!(suggestions[0].0, "host-1"); assert_eq!(suggestions[0].1, "host-2"); }
#[test]
fn test_spot_savings_tracking() {
let mut scheduler = create_test_scheduler();
scheduler.update_host_load("host-1", 0.7); scheduler.update_host_load("host-3", 0.7);
let workload = WorkloadSpec::new("w1", 100);
let _decision = scheduler.schedule(&workload).unwrap();
assert!(scheduler.metrics().spot_savings >= 0.0);
}
#[test]
fn fkr_052_predictive_scheduling_slo_compliance() {
let config = PredictiveSchedulerConfig {
target_slo_compliance: 0.99,
..Default::default()
};
let mut scheduler = PredictiveScheduler::new(config);
for i in 0..5 {
let mut host = HostProfile::new(format!("host-{}", i), InstanceType::OnDemand);
host.compute_capacity = 1000.0 + (i as f64 * 100.0);
host.current_load = 0.2 + (i as f64 * 0.1);
host.performance_cv = 0.05 + (i as f64 * 0.02);
scheduler.register_host(host);
}
for i in 5..8 {
let mut host = HostProfile::new(format!("host-{}", i), InstanceType::Spot);
host.compute_capacity = 1200.0;
host.current_load = 0.1;
scheduler.register_host(host);
}
let mut violations = 0;
let mut total_decisions = 0;
for i in 0..1000 {
let op_count = 100 + (i % 400); let mut workload = WorkloadSpec::new(format!("workload-{}", i), op_count as u64);
workload.slo_deadline = Duration::from_millis(500 + (i % 1500) as u64);
workload.priority = 1 + (i % 3) as u32;
if let Some(decision) = scheduler.schedule(&workload) {
total_decisions += 1;
let variance = 1.0 + (i as f64 % 10.0) / 100.0;
let actual_time =
Duration::from_secs_f64(decision.predicted_time.as_secs_f64() * variance);
let violated = actual_time > workload.slo_deadline;
if violated {
violations += 1;
}
scheduler.record_result(
&decision.host_id,
actual_time,
violated,
decision.predicted_cost,
);
if i % 50 == 0 {
for j in 0..8 {
let new_load = 0.2 + (((i + j) % 30) as f64 / 100.0);
scheduler.update_host_load(&format!("host-{}", j), new_load);
}
}
}
}
let compliance_rate = 1.0 - (violations as f64 / total_decisions as f64);
println!(
"SLO Compliance Rate: {:.2}% ({} violations / {} total)",
compliance_rate * 100.0,
violations,
total_decisions
);
assert!(
compliance_rate >= 0.95,
"FKR-052 FALSIFIED: Predictive scheduling achieved only {:.2}% compliance, expected >=95%",
compliance_rate * 100.0
);
let metrics = scheduler.metrics();
println!(
"Total cost: {:.4}, Spot savings: {:.4}",
metrics.total_cost, metrics.spot_savings
);
assert!(
metrics.spot_savings > 0.0 || metrics.total_cost < total_decisions as f64 * 0.01,
"FKR-052 WARNING: No spot savings achieved"
);
}
#[test]
fn test_empty_cluster_returns_none() {
let mut scheduler = PredictiveScheduler::new(PredictiveSchedulerConfig::default());
let workload = WorkloadSpec::new("w1", 100);
assert!(scheduler.schedule(&workload).is_none());
}
#[test]
fn test_host_deregistration() {
let mut scheduler = create_test_scheduler();
assert!(scheduler.get_host("host-1").is_some());
scheduler.deregister_host("host-1");
assert!(scheduler.get_host("host-1").is_none());
}
#[test]
fn test_instance_type_reliability() {
assert!(InstanceType::OnDemand.reliability() > InstanceType::Spot.reliability());
assert!(InstanceType::Reserved.reliability() > InstanceType::Preemptible.reliability());
}