#[test]
fn test_imp_140c_throughput_rps() {
use crate::gguf::DispatchMetrics;
use std::thread;
use std::time::Duration;
let metrics = DispatchMetrics::new();
thread::sleep(Duration::from_millis(2));
for _ in 0..100 {
metrics.record_cpu_dispatch();
}
let rps = metrics.throughput_rps();
assert!(rps > 0.0, "IMP-140c: RPS should be > 0, got {}", rps);
assert!(
rps > 100.0,
"IMP-140c: RPS should be > 100 (100 dispatches in ~2ms), got {}",
rps
);
}
#[test]
fn test_imp_140d_json_response_includes_throughput() {
use crate::gguf::DispatchMetrics;
use std::sync::Arc;
let metrics = Arc::new(DispatchMetrics::new());
metrics.record_cpu_dispatch();
metrics.record_cpu_dispatch();
let response = DispatchMetricsResponse {
cpu_dispatches: metrics.cpu_dispatches(),
gpu_dispatches: metrics.gpu_dispatches(),
total_dispatches: metrics.total_dispatches(),
gpu_ratio: metrics.gpu_ratio(),
cpu_latency_p50_us: 0.0,
cpu_latency_p95_us: 0.0,
cpu_latency_p99_us: 0.0,
gpu_latency_p50_us: 0.0,
gpu_latency_p95_us: 0.0,
gpu_latency_p99_us: 0.0,
cpu_latency_mean_us: 0.0,
gpu_latency_mean_us: 0.0,
cpu_latency_min_us: 0,
cpu_latency_max_us: 0,
gpu_latency_min_us: 0,
gpu_latency_max_us: 0,
cpu_latency_variance_us: 0.0,
cpu_latency_stddev_us: 0.0,
gpu_latency_variance_us: 0.0,
gpu_latency_stddev_us: 0.0,
bucket_boundaries_us: vec![],
cpu_latency_bucket_counts: vec![],
gpu_latency_bucket_counts: vec![],
throughput_rps: metrics.throughput_rps(),
elapsed_seconds: metrics.elapsed_seconds(),
};
let json = serde_json::to_string(&response).expect("IMP-140d: Should serialize");
assert!(
json.contains("throughput_rps"),
"IMP-140d: JSON should contain throughput_rps"
);
assert!(
json.contains("elapsed_seconds"),
"IMP-140d: JSON should contain elapsed_seconds"
);
}
#[test]
fn test_imp_142a_dispatch_metrics_has_cpu_latency_cv() {
use crate::gguf::DispatchMetrics;
use std::time::Duration;
let metrics = DispatchMetrics::new();
metrics.record_cpu_latency(Duration::from_micros(100));
metrics.record_cpu_latency(Duration::from_micros(200));
metrics.record_cpu_latency(Duration::from_micros(300));
let cv = metrics.cpu_latency_cv();
assert!(
cv > 0.0,
"IMP-142a: CV should be > 0 for varied samples, got {}",
cv
);
assert!(cv < 100.0, "IMP-142a: CV should be < 100%, got {}%", cv);
}
#[test]
fn test_imp_142b_dispatch_metrics_has_gpu_latency_cv() {
use crate::gguf::DispatchMetrics;
use std::time::Duration;
let metrics = DispatchMetrics::new();
metrics.record_gpu_latency(Duration::from_micros(50));
metrics.record_gpu_latency(Duration::from_micros(100));
metrics.record_gpu_latency(Duration::from_micros(150));
let cv = metrics.gpu_latency_cv();
assert!(
cv > 0.0,
"IMP-142b: CV should be > 0 for varied samples, got {}",
cv
);
}
#[test]
fn test_imp_142c_dispatch_metrics_has_cpu_gpu_speedup() {
use crate::gguf::DispatchMetrics;
use std::time::Duration;
let metrics = DispatchMetrics::new();
metrics.record_cpu_latency(Duration::from_micros(1000));
metrics.record_cpu_latency(Duration::from_micros(1000));
metrics.record_gpu_latency(Duration::from_micros(100));
metrics.record_gpu_latency(Duration::from_micros(100));
let speedup = metrics.cpu_gpu_speedup();
assert!(
speedup > 5.0 && speedup < 15.0,
"IMP-142c: Speedup should be ~10x (CPU 1000µs vs GPU 100µs), got {}x",
speedup
);
}
#[test]
fn test_imp_142d_speedup_returns_zero_without_gpu_samples() {
use crate::gguf::DispatchMetrics;
use std::time::Duration;
let metrics = DispatchMetrics::new();
metrics.record_cpu_latency(Duration::from_micros(1000));
let speedup = metrics.cpu_gpu_speedup();
assert_eq!(
speedup, 0.0,
"IMP-142d: Speedup should be 0.0 when GPU has no samples"
);
}
#[test]
fn test_parity022a_gpu_batch_request_struct() {
let request = GpuBatchRequest {
prompts: vec!["Hello".to_string(), "World".to_string()],
max_tokens: 50,
temperature: 0.0,
top_k: 1,
stop: vec![],
};
assert_eq!(
request.prompts.len(),
2,
"PARITY-022a: Should have 2 prompts"
);
assert_eq!(
request.max_tokens, 50,
"PARITY-022a: max_tokens should be 50"
);
assert_eq!(
request.temperature, 0.0,
"PARITY-022a: temperature should be 0.0"
);
assert_eq!(request.top_k, 1, "PARITY-022a: top_k should be 1");
}
#[test]
fn test_parity022b_gpu_batch_response_struct() {
let response = GpuBatchResponse {
results: vec![GpuBatchResult {
index: 0,
token_ids: vec![1, 2, 3],
text: "test".to_string(),
num_generated: 3,
}],
stats: GpuBatchStats {
batch_size: 1,
gpu_used: false,
total_tokens: 3,
processing_time_ms: 100.0,
throughput_tps: 30.0,
},
};
assert_eq!(
response.results.len(),
1,
"PARITY-022b: Should have 1 result"
);
assert_eq!(
response.stats.batch_size, 1,
"PARITY-022b: batch_size should be 1"
);
assert!(!response.stats.gpu_used, "PARITY-022b: GPU not used");
}
#[test]
fn test_parity022c_gpu_status_response_structure() {
let status = GpuStatusResponse {
cache_ready: false,
cache_memory_bytes: 0,
batch_threshold: 32,
recommended_min_batch: 32,
};
assert_eq!(
status.batch_threshold, 32,
"PARITY-022c: GPU GEMM threshold should be 32 (from IMP-600)"
);
assert_eq!(
status.recommended_min_batch, 32,
"PARITY-022c: Recommended min batch should be 32"
);
}
#[test]
fn test_parity022d_gpu_warmup_response_structure() {
let warmup = GpuWarmupResponse {
success: true,
memory_bytes: 6_400_000_000, num_layers: 32,
message: "GPU cache warmed up".to_string(),
};
assert!(warmup.success, "PARITY-022d: Warmup should succeed");
assert_eq!(warmup.num_layers, 32, "PARITY-022d: phi-2 has 32 layers");
assert!(
warmup.memory_bytes > 6_000_000_000,
"PARITY-022d: Memory should be ~6.4 GB for phi-2"
);
}
#[test]
fn test_parity022e_router_has_gpu_batch_routes() {
let expected_routes = ["/v1/gpu/warmup", "/v1/gpu/status", "/v1/batch/completions"];
for route in expected_routes {
assert!(
!route.is_empty(),
"PARITY-022e: Route {} should be defined",
route
);
}
}
#[test]
fn test_health_response_serialize() {
let response = HealthResponse {
status: "ok".to_string(),
version: "1.0.0".to_string(),
compute_mode: "cpu".to_string(),
model_loaded: true,
uptime_sec: 1.0,
};
let json = serde_json::to_string(&response).expect("test");
assert!(json.contains("\"ok\""));
assert!(json.contains("1.0.0"));
assert!(json.contains("cpu"));
assert!(json.contains("model_loaded"));
assert!(json.contains("uptime_sec"));
}
#[test]
fn test_tokenize_request_deserialize() {
let json = r#"{"text": "hello world"}"#;
let req: TokenizeRequest = serde_json::from_str(json).expect("test");
assert_eq!(req.text, "hello world");
assert!(req.model_id.is_none());
}
#[test]
fn test_tokenize_request_with_model_id() {
let json = r#"{"text": "hello", "model_id": "phi-2"}"#;
let req: TokenizeRequest = serde_json::from_str(json).expect("test");
assert_eq!(req.model_id, Some("phi-2".to_string()));
}
#[test]
fn test_tokenize_response_serialize() {
let response = TokenizeResponse {
token_ids: vec![1, 2, 3],
num_tokens: 3,
};
let json = serde_json::to_string(&response).expect("test");
assert!(json.contains("[1,2,3]"));
}
#[test]
fn test_generate_request_defaults() {
let json = r#"{"prompt": "Hello"}"#;
let req: GenerateRequest = serde_json::from_str(json).expect("test");
assert_eq!(req.prompt, "Hello");
assert_eq!(req.max_tokens, 50); assert!((req.temperature - 1.0).abs() < 0.001);
assert_eq!(req.strategy, "greedy");
assert_eq!(req.top_k, 50);
assert!((req.top_p - 0.9).abs() < 0.001);
}
#[test]
fn test_generate_request_custom_values() {
let json = r#"{"prompt": "Hi", "max_tokens": 100, "temperature": 0.7, "strategy": "top_k", "top_k": 40}"#;
let req: GenerateRequest = serde_json::from_str(json).expect("test");
assert_eq!(req.max_tokens, 100);
assert!((req.temperature - 0.7).abs() < 0.001);
assert_eq!(req.strategy, "top_k");
assert_eq!(req.top_k, 40);
}
#[test]
fn test_generate_response_serialize() {
let response = GenerateResponse {
token_ids: vec![1, 2],
text: "test output".to_string(),
num_generated: 2,
};
let json = serde_json::to_string(&response).expect("test");
assert!(json.contains("test output"));
}
#[test]
fn test_error_response_serialize() {
let response = ErrorResponse {
error: "Something went wrong".to_string(),
};
let json = serde_json::to_string(&response).expect("test");
assert!(json.contains("Something went wrong"));
}
#[test]
fn test_batch_tokenize_request_deserialize() {
let json = r#"{"texts": ["hello", "world"]}"#;
let req: BatchTokenizeRequest = serde_json::from_str(json).expect("test");
assert_eq!(req.texts.len(), 2);
}
#[test]
fn test_batch_tokenize_response_serialize() {
let response = BatchTokenizeResponse {
results: vec![
TokenizeResponse {
token_ids: vec![1],
num_tokens: 1,
},
TokenizeResponse {
token_ids: vec![2, 3],
num_tokens: 2,
},
],
};
let json = serde_json::to_string(&response).expect("test");
assert!(json.contains("results"));
}
#[test]
fn test_chat_message_roles() {
let system = ChatMessage {
role: "system".to_string(),
content: "You are helpful".to_string(),
name: None,
..Default::default()
};
let user = ChatMessage {
role: "user".to_string(),
content: "Hello".to_string(),
name: Some("John".to_string()),
..Default::default()
};
let assistant = ChatMessage {
role: "assistant".to_string(),
content: "Hi!".to_string(),
name: None,
..Default::default()
};
assert_eq!(system.role, "system");
assert_eq!(user.role, "user");
assert_eq!(assistant.role, "assistant");
assert_eq!(user.name, Some("John".to_string()));
}
#[test]
fn test_chat_completion_request_deserialize() {
let json = r#"{"model": "phi-2", "messages": [{"role": "user", "content": "hi"}]}"#;
let req: ChatCompletionRequest = serde_json::from_str(json).expect("test");
assert_eq!(req.model, "phi-2");
assert_eq!(req.messages.len(), 1);
}