use std::collections::HashMap;
use std::time::Duration;
use blueprint_testing_utils::setup_log;
use rust_decimal::Decimal;
use rust_decimal::prelude::FromPrimitive;
use crate::benchmark::{BenchmarkProfile, BenchmarkRunConfig, run_benchmark_suite};
use crate::pricing::{ResourcePricing, calculate_price};
use crate::types::ResourceUnit;
fn create_test_benchmark_profile(avg_cpu_cores: f32) -> BenchmarkProfile {
BenchmarkProfile {
job_id: "test-job".to_string(),
execution_mode: "native".to_string(),
duration_secs: 60,
timestamp: 1643723400, success: true,
cpu_details: Some(crate::benchmark::CpuBenchmarkResult {
num_cores_detected: 4,
avg_cores_used: avg_cpu_cores,
avg_usage_percent: avg_cpu_cores * 25.0, peak_cores_used: avg_cpu_cores * 1.5,
peak_usage_percent: avg_cpu_cores * 1.5 * 25.0,
benchmark_duration_ms: 10000,
primes_found: 1000,
max_prime: 10000,
primes_per_second: 100.0,
cpu_model: "Test CPU".to_string(),
cpu_frequency_mhz: 3000.0,
}),
memory_details: Some(crate::benchmark::MemoryBenchmarkResult {
avg_memory_mb: 100.0,
peak_memory_mb: 150.0,
block_size_kb: 1024,
total_size_mb: 1024,
operations_per_second: 10000.0,
transfer_rate_mb_s: 2048.0,
access_mode: crate::benchmark::MemoryAccessMode::Sequential,
operation_type: crate::benchmark::MemoryOperationType::Write,
latency_ns: 250.0,
duration_ms: 5000,
}),
io_details: Some(crate::benchmark::IoBenchmarkResult {
read_mb: 10.0,
write_mb: 5.0,
read_iops: 100.0,
write_iops: 50.0,
avg_read_latency_ms: 0.1,
avg_write_latency_ms: 0.2,
max_read_latency_ms: 1.0,
max_write_latency_ms: 2.0,
test_mode: crate::benchmark::IoTestMode::RndRw,
block_size: 4096,
total_file_size: 1024 * 1024 * 100, num_files: 2,
duration_ms: 5000,
}),
network_details: Some(crate::benchmark::NetworkBenchmarkResult {
network_rx_mb: 20.0,
network_tx_mb: 10.0,
download_speed_mbps: 100.0,
upload_speed_mbps: 50.0,
latency_ms: 15.0,
duration_ms: 5000,
packet_loss_percent: 0.0,
jitter_ms: 2.5,
}),
gpu_details: Some(crate::benchmark::GpuBenchmarkResult {
gpu_available: false,
gpu_memory_mb: 0.0,
gpu_model: "Test GPU".to_string(),
gpu_frequency_mhz: 0.0,
}),
storage_details: Some(crate::benchmark::StorageBenchmarkResult {
storage_available_gb: 100.0,
}),
}
}
#[test]
fn test_benchmark_suite() {
setup_log();
let result = run_benchmark_suite(
"test-suite".to_string(),
"test".to_string(),
Duration::from_secs(30),
Duration::from_millis(500),
true, true, true, true, true, );
assert!(result.is_ok());
let profile = result.unwrap();
println!("Profile: {profile:#?}");
assert!(profile.success)
}
#[test]
fn test_calculate_price_basic() {
setup_log();
let profile = create_test_benchmark_profile(1.0);
let mut pricing_config = HashMap::new();
let default_resources = vec![
ResourcePricing {
kind: ResourceUnit::CPU,
count: 1,
price_per_unit_rate: Decimal::from_f64(0.000001).unwrap(),
},
ResourcePricing {
kind: ResourceUnit::MemoryMB,
count: 1024,
price_per_unit_rate: Decimal::from_f64(0.00000005).unwrap(),
},
ResourcePricing {
kind: ResourceUnit::StorageMB,
count: 1024,
price_per_unit_rate: Decimal::from_f64(0.00000002).unwrap(),
},
ResourcePricing {
kind: ResourceUnit::NetworkIngressMB,
count: 1024,
price_per_unit_rate: Decimal::from_f64(0.00000001).unwrap(),
},
ResourcePricing {
kind: ResourceUnit::NetworkEgressMB,
count: 1024,
price_per_unit_rate: Decimal::from_f64(0.00000003).unwrap(),
},
];
pricing_config.insert(None, default_resources);
let ttl_blocks = 600u64;
let price_model =
calculate_price(profile.clone(), &pricing_config, None, ttl_blocks, None).unwrap();
println!("Price Model: {price_model:#?}");
assert!(
!price_model.resources.is_empty(),
"Resources should not be empty"
);
assert!(
price_model.total_cost > Decimal::from_f64(0.0).unwrap(),
"Expected total cost to be positive, got {}",
price_model.total_cost
);
assert!(
price_model.benchmark_profile.is_some(),
"Benchmark profile should be included"
);
if let Some(cpu_resource) = price_model
.resources
.iter()
.find(|r| matches!(r.kind, ResourceUnit::CPU))
{
assert!(
cpu_resource.count > 0,
"Expected CPU count to be positive, got {}",
cpu_resource.count
);
} else {
panic!("CPU resource not found in price model");
}
assert!(
price_model
.resources
.iter()
.any(|r| matches!(r.kind, ResourceUnit::MemoryMB)),
"Memory resource not found in price model"
);
assert!(
price_model
.resources
.iter()
.any(|r| matches!(r.kind, ResourceUnit::StorageMB)),
"Storage resource not found in price model"
);
assert!(
price_model
.resources
.iter()
.any(|r| matches!(r.kind, ResourceUnit::NetworkIngressMB)),
"Network ingress resource not found in price model"
);
assert!(
price_model
.resources
.iter()
.any(|r| matches!(r.kind, ResourceUnit::NetworkEgressMB)),
"Network egress resource not found in price model"
);
}
#[test]
fn test_calculate_price_high_cpu() {
setup_log();
let profile = create_test_benchmark_profile(4.0);
let mut pricing_config = HashMap::new();
let default_resources = vec![
ResourcePricing {
kind: ResourceUnit::CPU,
count: 1,
price_per_unit_rate: Decimal::from_f64(0.000001).unwrap(),
},
ResourcePricing {
kind: ResourceUnit::MemoryMB,
count: 1024,
price_per_unit_rate: Decimal::from_f64(0.00000005).unwrap(),
},
];
pricing_config.insert(None, default_resources);
let _ttl_blocks = 600u64;
let price_model = calculate_price(profile.clone(), &pricing_config, None, 600, None).unwrap();
println!("Price Model (High CPU): {price_model:#?}");
if let Some(cpu_resource) = price_model
.resources
.iter()
.find(|r| matches!(r.kind, ResourceUnit::CPU))
{
assert_eq!(
cpu_resource.count, 4,
"Expected 4 CPU cores, got {}",
cpu_resource.count
);
} else {
panic!("CPU resource not found in price model");
}
assert!(
price_model.total_cost > Decimal::from_f64(0.004).unwrap(), "Expected total cost to be higher than 0.004, got {}",
price_model.total_cost
);
}
#[test]
fn test_calculate_price_different_scaling_factors() {
setup_log();
let profile = create_test_benchmark_profile(2.0);
let mut pricing_config = HashMap::new();
let default_resources = vec![
ResourcePricing {
kind: ResourceUnit::CPU,
count: 1,
price_per_unit_rate: Decimal::from_f64(0.000001).unwrap(),
},
ResourcePricing {
kind: ResourceUnit::MemoryMB,
count: 1024,
price_per_unit_rate: Decimal::from_f64(0.00000005).unwrap(),
},
];
pricing_config.insert(None, default_resources);
let _ttl_blocks = 600u64;
let ttl_values = [300u64, 600u64, 1200u64];
for &ttl in &ttl_values {
let price_model =
calculate_price(profile.clone(), &pricing_config, None, ttl, None).unwrap();
println!("Price Model (TTL = {ttl} blocks): {price_model:#?}");
let base_price = price_model.total_cost;
let expected_total_cost = base_price * (Decimal::from_u64(ttl).unwrap());
println!("Base price per block: ${base_price:.6}");
println!("Total cost for {ttl} blocks: ${expected_total_cost:.6}");
if let Some(cpu_resource) = price_model
.resources
.iter()
.find(|r| matches!(r.kind, ResourceUnit::CPU))
{
assert_eq!(
cpu_resource.count, 2,
"Expected 2 CPU cores, got {}",
cpu_resource.count
);
} else {
panic!("CPU resource not found in price model");
}
}
}
#[test]
fn test_calculate_price_negative_scaling_factor() {
setup_log();
let profile = create_test_benchmark_profile(1.0);
let mut pricing_config = HashMap::new();
let default_resources = vec![
ResourcePricing {
kind: ResourceUnit::CPU,
count: 1,
price_per_unit_rate: Decimal::from_f64(-0.000001).unwrap(),
},
ResourcePricing {
kind: ResourceUnit::MemoryMB,
count: 1024,
price_per_unit_rate: Decimal::from_f64(0.00000005).unwrap(),
},
];
pricing_config.insert(None, default_resources);
let _ttl_blocks = 600u64;
let result = calculate_price(profile.clone(), &pricing_config, None, 600, None);
match result {
Ok(price_model) => {
println!(
"Price calculation succeeded with negative price: ${:.6}",
price_model.total_cost
);
assert!(
price_model.total_cost >= Decimal::from_f64(0.0).unwrap(),
"Expected total cost to be non-negative, got {}",
price_model.total_cost
);
}
Err(e) => {
println!("Price calculation failed as expected: {e:?}");
}
}
}
#[test]
fn test_io_benchmark() {
setup_log();
let config = BenchmarkRunConfig {
job_id: "io-test".to_string(),
mode: "test".to_string(),
command: "echo".to_string(),
args: vec!["benchmark".to_string()],
max_duration: Duration::from_secs(30),
sample_interval: Duration::from_millis(100),
run_cpu_test: false,
run_memory_test: false,
run_io_test: true,
run_network_test: false,
run_gpu_test: false,
};
let result = crate::benchmark::io::run_io_benchmark(&config).unwrap();
println!("IO Benchmark Results:");
println!(" Read: {:.2} MB", result.read_mb);
println!(" Write: {:.2} MB", result.write_mb);
println!(" Read IOPS: {:.2}", result.read_iops);
println!(" Write IOPS: {:.2}", result.write_iops);
println!(" Read Latency: {:.2} ms", result.avg_read_latency_ms);
println!(" Write Latency: {:.2} ms", result.avg_write_latency_ms);
println!(" Duration: {} ms", result.duration_ms);
assert!(result.read_mb > 0.0);
assert!(result.write_mb > 0.0);
assert!(result.read_iops > 0.0);
assert!(result.write_iops > 0.0);
}
#[test]
fn test_memory_benchmark() {
setup_log();
let config = BenchmarkRunConfig {
job_id: "memory-test".to_string(),
mode: "test".to_string(),
command: "echo".to_string(),
args: vec!["benchmark".to_string()],
max_duration: Duration::from_secs(30),
sample_interval: Duration::from_millis(100),
run_cpu_test: false,
run_memory_test: true,
run_io_test: false,
run_network_test: false,
run_gpu_test: false,
};
let result = crate::benchmark::memory::run_memory_benchmark(&config).unwrap();
println!("Memory Benchmark Results:");
println!(" Average Memory: {:.2} MB", result.avg_memory_mb);
println!(" Peak Memory: {:.2} MB", result.peak_memory_mb);
println!(" Block Size: {} KB", result.block_size_kb);
println!(" Total Size: {} MB", result.total_size_mb);
println!(" Operations/sec: {:.2}", result.operations_per_second);
println!(" Transfer Rate: {:.2} MB/s", result.transfer_rate_mb_s);
println!(" Latency: {:.2} ns", result.latency_ns);
println!(" Duration: {} ms", result.duration_ms);
assert!(result.avg_memory_mb > 0.0);
assert!(result.peak_memory_mb > 0.0);
assert!(result.operations_per_second > 0.0);
assert!(result.transfer_rate_mb_s > 0.0);
}
#[test]
fn test_network_benchmark() {
setup_log();
let config = BenchmarkRunConfig {
job_id: "network-test".to_string(),
mode: "test".to_string(),
command: "echo".to_string(),
args: vec!["benchmark".to_string()],
max_duration: Duration::from_secs(30),
sample_interval: Duration::from_millis(100),
run_cpu_test: false,
run_memory_test: false,
run_io_test: false,
run_network_test: true,
run_gpu_test: false,
};
let result = crate::benchmark::network::run_network_benchmark(&config).unwrap();
println!("Network Benchmark Results:");
println!(" Data Received: {:.2} MB", result.network_rx_mb);
println!(" Data Transmitted: {:.2} MB", result.network_tx_mb);
println!(" Download Speed: {:.2} Mbps", result.download_speed_mbps);
println!(" Upload Speed: {:.2} Mbps", result.upload_speed_mbps);
println!(" Latency: {:.2} ms", result.latency_ms);
println!(" Jitter: {:.2} ms", result.jitter_ms);
println!(" Packet Loss: {:.2}%", result.packet_loss_percent);
println!(" Duration: {} ms", result.duration_ms);
assert!(result.network_rx_mb > 0.0);
assert!(result.network_tx_mb > 0.0);
assert!(result.download_speed_mbps > 0.0);
assert!(result.upload_speed_mbps > 0.0);
}
#[test]
fn test_resource_pricing() {
let price_model = crate::pricing::PriceModel {
resources: vec![
crate::pricing::ResourcePricing {
kind: ResourceUnit::CPU,
count: 2,
price_per_unit_rate: Decimal::from_f64(0.000001).unwrap(), },
crate::pricing::ResourcePricing {
kind: ResourceUnit::MemoryMB,
count: 1024,
price_per_unit_rate: Decimal::from_f64(0.00000005).unwrap(), },
],
total_cost: Decimal::from_f64(0.0000532).unwrap(), benchmark_profile: None,
};
assert!(
(price_model.total_cost - Decimal::from_f64(0.0000532).unwrap()).abs()
< Decimal::from_f64(1e-6).unwrap(),
"Expected total cost to be 0.0000532, got {}",
price_model.total_cost
);
}
#[test]
fn test_pow_challenge_generation() {
use crate::pow::generate_challenge;
let blueprint_id_1 = 12345;
let timestamp_1 = 1643723400;
let challenge_1 = generate_challenge(blueprint_id_1, timestamp_1);
let blueprint_id_2 = 12345;
let timestamp_2 = 1643723401; let challenge_2 = generate_challenge(blueprint_id_2, timestamp_2);
let blueprint_id_3 = 54321; let timestamp_3 = 1643723400;
let challenge_3 = generate_challenge(blueprint_id_3, timestamp_3);
assert!(!challenge_1.is_empty());
assert!(!challenge_2.is_empty());
assert!(!challenge_3.is_empty());
assert_ne!(
challenge_1, challenge_2,
"Different timestamps should produce different challenges"
);
assert_ne!(
challenge_1, challenge_3,
"Different blueprint IDs should produce different challenges"
);
let challenge_1_repeat = generate_challenge(blueprint_id_1, timestamp_1);
assert_eq!(
challenge_1, challenge_1_repeat,
"Same inputs should produce the same challenge"
);
}