use serde_json::{Value, json};
use std::time::Instant;
fn generate_synthetic_mnist_data() -> Vec<f32> {
use rand::RngExt;
let mut rng = rand::rng();
let mut data = vec![0.0; 784];
for y in 0..28 {
for x in 0..28 {
let idx = y * 28 + x;
let center_x = 14.0;
let center_y = 14.0;
let distance = ((x as f32 - center_x).powi(2) + (y as f32 - center_y).powi(2)).sqrt();
if distance < 8.0 {
data[idx] = rng.random_range(0.7..1.0);
} else if distance < 10.0 {
data[idx] = rng.random_range(0.3..0.7);
} else {
data[idx] = rng.random_range(0.0..0.2);
}
}
}
data
}
fn generate_edge_cases() -> Vec<(String, Vec<f32>)> {
vec![
("all_zeros".to_string(), vec![0.0; 784]),
("all_ones".to_string(), vec![1.0; 784]),
(
"alternating".to_string(),
(0..784)
.map(|i| if i % 2 == 0 { 1.0 } else { 0.0 })
.collect(),
),
(
"gradient".to_string(),
(0..784).map(|i| i as f32 / 784.0).collect(),
),
("random_sparse".to_string(), {
let mut data = vec![0.0; 784];
for i in (0..784).step_by(10) {
data[i] = rand::random::<f32>();
}
data
}),
]
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("๐งช Advanced Furnace inference testing with custom data...");
let base_url = "http://127.0.0.1:3000";
let client = reqwest::Client::new();
println!("\n1๏ธโฃ Verifying server connection...");
match client.get(format!("{base_url}/healthz")).send().await {
Ok(response) if response.status().is_success() => {
let health: Value = response.json().await?;
println!("โ
Server is running");
println!(
" Model loaded: {}",
health["model_loaded"].as_bool().unwrap_or(false)
);
}
Ok(response) => {
println!("โ Server responded with error: {}", response.status());
return Ok(());
}
Err(_) => {
println!("โ Cannot connect to server. Make sure it's running:");
println!(" cargo run --bin furnace -- --model-path examples/basic_mnist/model");
return Ok(());
}
}
println!("\n2๏ธโฃ Testing with synthetic MNIST-like data...");
for i in 1..=3 {
let synthetic_data = generate_synthetic_mnist_data();
let request_body = json!({ "input": synthetic_data });
let start = Instant::now();
let response = client
.post(format!("{base_url}/predict"))
.header("Content-Type", "application/json")
.json(&request_body)
.send()
.await?;
let duration = start.elapsed();
if response.status().is_success() {
let result: Value = response.json().await?;
if let Some(output) = result["output"].as_array() {
let max_idx = output
.iter()
.enumerate()
.max_by(|(_, a), (_, b)| a.as_f64().partial_cmp(&b.as_f64()).unwrap())
.map(|(i, _)| i)
.unwrap_or(0);
println!(
" Sample {}: โ
Predicted class {} ({:.2}ms)",
i,
max_idx,
duration.as_secs_f64() * 1000.0
);
}
} else {
println!(" Sample {}: โ {}", i, response.status());
}
}
println!("\n3๏ธโฃ Testing edge cases...");
let edge_cases = generate_edge_cases();
for (name, data) in edge_cases {
let request_body = json!({ "input": data });
let start = Instant::now();
let response = client
.post(format!("{base_url}/predict"))
.header("Content-Type", "application/json")
.json(&request_body)
.send()
.await?;
let duration = start.elapsed();
if response.status().is_success() {
let result: Value = response.json().await?;
if let Some(output) = result["output"].as_array() {
let max_idx = output
.iter()
.enumerate()
.max_by(|(_, a), (_, b)| a.as_f64().partial_cmp(&b.as_f64()).unwrap())
.map(|(i, _)| i)
.unwrap_or(0);
println!(
" {}: โ
Class {} ({:.2}ms)",
name,
max_idx,
duration.as_secs_f64() * 1000.0
);
}
} else {
println!(" {}: โ {}", name, response.status());
}
}
println!("\n4๏ธโฃ Simulating batch processing...");
let batch_size = 10;
let mut total_time = 0.0;
let mut successful_requests = 0;
let start_batch = Instant::now();
for i in 1..=batch_size {
let data = generate_synthetic_mnist_data();
let request_body = json!({ "input": data });
let start = Instant::now();
let response = client
.post(format!("{base_url}/predict"))
.header("Content-Type", "application/json")
.json(&request_body)
.send()
.await?;
let duration = start.elapsed().as_secs_f64() * 1000.0;
total_time += duration;
if response.status().is_success() {
successful_requests += 1;
if i % 3 == 0 {
println!(" Processed {i}/{batch_size} requests...");
}
}
}
let batch_duration = start_batch.elapsed().as_secs_f64() * 1000.0;
println!(" โ
Batch completed: {successful_requests}/{batch_size} successful");
println!(
" Average request time: {:.2}ms",
total_time / batch_size as f64
);
println!(" Total batch time: {batch_duration:.2}ms");
println!(
" Throughput: {:.1} requests/second",
batch_size as f64 / (batch_duration / 1000.0)
);
println!("\n5๏ธโฃ Testing error handling...");
let error_cases = vec![
("empty_input", json!({ "input": [] })),
("wrong_size", json!({ "input": vec![1.0; 100] })),
("missing_input", json!({ "data": vec![1.0; 784] })),
("invalid_type", json!({ "input": "not_an_array" })),
("null_input", json!({ "input": serde_json::Value::Null })),
];
for (name, request_body) in error_cases {
let response = client
.post(format!("{base_url}/predict"))
.header("Content-Type", "application/json")
.json(&request_body)
.send()
.await?;
if response.status().is_client_error() {
println!(" {}: โ
Correctly rejected ({})", name, response.status());
} else {
println!(
" {}: โ Unexpected response ({})",
name,
response.status()
);
}
}
println!("\n๐ Advanced testing completed!");
println!("\n๐ Summary:");
println!(" - Synthetic data generation and testing");
println!(" - Edge case validation");
println!(" - Batch processing simulation");
println!(" - Comprehensive error handling");
println!(" - Performance measurement");
Ok(())
}