#[cfg(test)]
mod tests {
use super::super::{
AtpDemandPriority, AtpFairnessCoordinator, AtpFairnessPolicy, AtpResourceBudget,
AtpResourceDemand, AtpResourceGovernor, AtpTransferId,
config::{AtpCustomLimits, AtpGovernanceConfig},
};
use crate::atp::profiles::{AtpPowerProfile, AtpResourceProfile};
#[test]
fn e2e_governance_bandwidth_enforcement_across_profiles() {
let test_cases = vec![
(AtpPowerProfile::MaxSpeed, None), (AtpPowerProfile::Balanced, Some(128 * 1_048_576)),
(AtpPowerProfile::Background, Some(32 * 1_048_576)),
(AtpPowerProfile::BatterySaver, Some(16 * 1_048_576)),
(AtpPowerProfile::CiDeterministic, Some(4 * 1_048_576)),
];
for (profile, expected_bandwidth) in test_cases {
let governor =
AtpResourceGovernor::from_profile(AtpResourceProfile::for_power_profile(profile));
let high_demand = AtpResourceDemand {
bandwidth_bytes_per_second: 256 * 1_048_576, ..AtpResourceDemand::default()
};
let decision = governor.evaluate(high_demand);
match expected_bandwidth {
None => {
assert!(
decision.allowed,
"Profile {profile:?} should allow high bandwidth"
);
}
Some(limit) => {
if high_demand.bandwidth_bytes_per_second > limit {
assert!(
decision.rejected(),
"Profile {profile:?} should reject bandwidth over {limit}"
);
assert!(!decision.violations.is_empty());
} else {
assert!(
decision.allowed,
"Profile {profile:?} should allow bandwidth under {limit}"
);
}
}
}
}
}
#[test]
fn e2e_fairness_equal_share_multiple_transfers() {
let budget = AtpResourceBudget::from_profile(AtpResourceProfile::for_power_profile(
AtpPowerProfile::Balanced,
));
let mut coordinator = AtpFairnessCoordinator::new(budget, AtpFairnessPolicy::EqualShare);
coordinator.register_transfer(
"transfer1".into(),
AtpResourceDemand {
bandwidth_bytes_per_second: 100 * 1_048_576,
in_flight_bytes: 50 * 1_048_576,
repair_symbols_per_second: 1_000,
disk_write_concurrency: 2,
relay_cost_micros_per_mib: None,
priority: AtpDemandPriority::Foreground,
},
);
coordinator.register_transfer(
"transfer2".into(),
AtpResourceDemand {
bandwidth_bytes_per_second: 200 * 1_048_576,
in_flight_bytes: 100 * 1_048_576,
repair_symbols_per_second: 2_000,
disk_write_concurrency: 4,
relay_cost_micros_per_mib: Some(500_000),
priority: AtpDemandPriority::Foreground,
},
);
coordinator.register_transfer(
"transfer3".into(),
AtpResourceDemand {
bandwidth_bytes_per_second: 50 * 1_048_576,
in_flight_bytes: 25 * 1_048_576,
repair_symbols_per_second: 500,
disk_write_concurrency: 1,
relay_cost_micros_per_mib: Some(1_000_000),
priority: AtpDemandPriority::Foreground,
},
);
let allocations = coordinator.calculate_allocations();
assert_eq!(allocations.len(), 3);
for allocation in &allocations {
assert!((allocation.share_ratio - (1.0 / 3.0)).abs() < 0.01);
assert_eq!(allocation.bandwidth_bytes_per_second, 128 * 1_048_576 / 3);
assert_eq!(allocation.in_flight_bytes, 128 * 1_048_576 / 3);
assert_eq!(allocation.repair_symbols_per_second, 4_096 / 3);
assert_eq!(allocation.disk_write_concurrency, 1);
}
}
#[test]
fn e2e_fairness_fcfs_prioritizes_first_transfer() {
let budget = AtpResourceBudget::from_profile(AtpResourceProfile::for_power_profile(
AtpPowerProfile::Balanced,
));
let mut coordinator =
AtpFairnessCoordinator::new(budget, AtpFairnessPolicy::FirstComeFirstServed);
coordinator.register_transfer("first".into(), AtpResourceDemand::default());
coordinator.register_transfer("second".into(), AtpResourceDemand::default());
coordinator.register_transfer("third".into(), AtpResourceDemand::default());
let allocations = coordinator.calculate_allocations();
assert_eq!(allocations.len(), 3);
let mut allocations = allocations;
allocations.sort_by(|a, b| a.transfer_id.0.cmp(&b.transfer_id.0));
let first_alloc = &allocations[0]; assert!((first_alloc.share_ratio - 0.7).abs() < 0.01);
for allocation in &allocations[1..] {
assert!((allocation.share_ratio - 0.15).abs() < 0.01);
}
}
#[test]
fn e2e_fairness_size_proportional_allocation() {
let budget = AtpResourceBudget::from_profile(AtpResourceProfile::for_power_profile(
AtpPowerProfile::Balanced,
));
let mut coordinator =
AtpFairnessCoordinator::new(budget, AtpFairnessPolicy::SizeProportional);
coordinator.register_transfer(
"small".into(),
AtpResourceDemand {
in_flight_bytes: 10 * 1_048_576,
..AtpResourceDemand::default()
},
);
coordinator.register_transfer(
"medium".into(),
AtpResourceDemand {
in_flight_bytes: 30 * 1_048_576,
..AtpResourceDemand::default()
},
);
coordinator.register_transfer(
"large".into(),
AtpResourceDemand {
in_flight_bytes: 60 * 1_048_576,
..AtpResourceDemand::default()
},
);
let allocations = coordinator.calculate_allocations();
assert_eq!(allocations.len(), 3);
let small_alloc = allocations
.iter()
.find(|a| a.transfer_id.0 == "small")
.unwrap();
let medium_alloc = allocations
.iter()
.find(|a| a.transfer_id.0 == "medium")
.unwrap();
let large_alloc = allocations
.iter()
.find(|a| a.transfer_id.0 == "large")
.unwrap();
assert!((small_alloc.share_ratio - 0.1).abs() < 0.01);
assert!((medium_alloc.share_ratio - 0.3).abs() < 0.01);
assert!((large_alloc.share_ratio - 0.6).abs() < 0.01);
}
#[test]
fn e2e_governance_config_custom_limit_override() {
let config = AtpGovernanceConfig::from_power_profile(AtpPowerProfile::Balanced)
.with_custom_limits(AtpCustomLimits {
max_bandwidth_bytes_per_second: Some(64 * 1_048_576), background_priority: Some(true), ..AtpCustomLimits::default()
});
let resolved_budget = config.resolve_budget();
assert_eq!(
resolved_budget.max_bandwidth_bytes_per_second,
Some(64 * 1_048_576)
);
assert!(resolved_budget.background_priority);
assert_eq!(resolved_budget.max_in_flight_bytes, Some(128 * 1_048_576));
assert_eq!(resolved_budget.max_repair_symbols_per_second, Some(4_096));
assert!(!resolved_budget.metered_network); }
#[test]
fn e2e_governance_dry_run_mode() {
let config = AtpGovernanceConfig::from_power_profile(AtpPowerProfile::BatterySaver)
.with_dry_run(true);
let governor = AtpResourceGovernor::from_profile(config.resolve_profile());
let high_demand = AtpResourceDemand {
bandwidth_bytes_per_second: 100 * 1_048_576, repair_symbols_per_second: 2_048, ..AtpResourceDemand::default()
};
let decision = governor.evaluate(high_demand);
assert!(decision.rejected());
assert!(!decision.violations.is_empty());
assert_eq!(decision.reason_code, "resource_budget_exceeded");
}
#[test]
fn e2e_fairness_coordinator_transfer_lifecycle() {
let budget = AtpResourceBudget::from_profile(AtpResourceProfile::for_power_profile(
AtpPowerProfile::Balanced,
));
let mut coordinator = AtpFairnessCoordinator::new(budget, AtpFairnessPolicy::EqualShare);
assert_eq!(coordinator.active_transfer_count(), 0);
assert!(coordinator.calculate_allocations().is_empty());
let transfer1_id: AtpTransferId = "transfer1".into();
coordinator.register_transfer(transfer1_id.clone(), AtpResourceDemand::default());
assert_eq!(coordinator.active_transfer_count(), 1);
let allocations = coordinator.calculate_allocations();
assert_eq!(allocations.len(), 1);
assert!((allocations[0].share_ratio - 1.0).abs() < 0.01);
let transfer2_id: AtpTransferId = "transfer2".into();
coordinator.register_transfer(transfer2_id.clone(), AtpResourceDemand::default());
assert_eq!(coordinator.active_transfer_count(), 2);
let allocations = coordinator.calculate_allocations();
assert_eq!(allocations.len(), 2);
for allocation in &allocations {
assert!((allocation.share_ratio - 0.5).abs() < 0.01); }
coordinator.unregister_transfer(&transfer1_id);
assert_eq!(coordinator.active_transfer_count(), 1);
let allocations = coordinator.calculate_allocations();
assert_eq!(allocations.len(), 1);
assert_eq!(allocations[0].transfer_id, transfer2_id);
assert!((allocations[0].share_ratio - 1.0).abs() < 0.01);
coordinator.unregister_transfer(&transfer2_id);
assert_eq!(coordinator.active_transfer_count(), 0);
assert!(coordinator.calculate_allocations().is_empty());
}
#[test]
fn e2e_governance_performance_benchmark() {
let governor = AtpResourceGovernor::from_profile(AtpResourceProfile::for_power_profile(
AtpPowerProfile::Balanced,
));
let demand = AtpResourceDemand {
bandwidth_bytes_per_second: 64 * 1_048_576,
in_flight_bytes: 32 * 1_048_576,
repair_symbols_per_second: 1_024,
disk_write_concurrency: 2,
relay_cost_micros_per_mib: Some(500_000),
priority: AtpDemandPriority::Foreground,
};
let start = std::time::Instant::now();
for _ in 0..10_000 {
let _decision = governor.evaluate(demand);
}
let duration = start.elapsed();
assert!(
duration.as_millis() < 1000,
"Governance evaluation too slow: {duration:?}"
);
}
}