#[cfg(all(test, feature = "gpu"))]
mod potemkin_village {
use axum::{
body::Body,
http::{Request, StatusCode},
};
use tower::ServiceExt;
use crate::api::gpu_handlers::{
BatchConfig, GpuBatchRequest, GpuBatchResponse, GpuStatusResponse, GpuWarmupResponse,
};
use crate::api::{create_router, AppState};
use crate::gguf::{GGUFConfig, OwnedQuantizedModelCachedSync};
fn create_potemkin_app() -> axum::Router {
let config = GGUFConfig {
architecture: "llama".to_string(),
constraints: crate::gguf::ArchConstraints::from_architecture("llama"),
hidden_dim: 32,
num_layers: 1,
num_heads: 4,
num_kv_heads: 4,
vocab_size: 32,
intermediate_dim: 64,
context_length: 512,
rope_theta: 10000.0,
eps: 1e-5,
rope_type: 0, explicit_head_dim: None,
query_pre_attn_scalar: None,
bos_token_id: None,
eos_token_id: None,
};
let mock_model = crate::api::test_helpers::create_test_quantized_model(&config);
let cached_model = OwnedQuantizedModelCachedSync::new(mock_model);
let state =
AppState::with_cached_model(cached_model).expect("Failed to create Potemkin AppState");
create_router(state)
}
#[tokio::test]
async fn test_gpu_warmup_handler_with_mock() {
let app = create_potemkin_app();
let response = app
.oneshot(
Request::builder()
.method("POST")
.uri("/v1/gpu/warmup")
.body(Body::empty())
.expect("test value should be present"),
)
.await
.expect("test value should be present");
let status = response.status();
assert!(
status == StatusCode::OK || status == StatusCode::INTERNAL_SERVER_ERROR,
"Expected OK or error, got: {}",
status
);
}
#[tokio::test]
async fn test_gpu_status_handler_with_mock() {
let app = create_potemkin_app();
let response = app
.oneshot(
Request::builder()
.method("GET")
.uri("/v1/gpu/status")
.body(Body::empty())
.expect("test value should be present"),
)
.await
.expect("test value should be present");
let status = response.status();
assert!(
status == StatusCode::OK || status == StatusCode::SERVICE_UNAVAILABLE,
"Expected OK or unavailable, got: {}",
status
);
if status == StatusCode::OK {
let body = axum::body::to_bytes(response.into_body(), usize::MAX)
.await
.expect("test value should be present");
let gpu_status: GpuStatusResponse = serde_json::from_slice(&body).expect("JSON from_slice failed");
assert!(gpu_status.batch_threshold > 0);
}
}
#[tokio::test]
async fn test_gpu_batch_completions_with_mock() {
let app = create_potemkin_app();
let request = GpuBatchRequest {
prompts: vec!["Hello".to_string(), "World".to_string()],
max_tokens: 5,
temperature: 0.0,
top_k: 1,
stop: vec![],
};
let response = app
.oneshot(
Request::builder()
.method("POST")
.uri("/v1/batch/completions")
.header("Content-Type", "application/json")
.body(Body::from(serde_json::to_string(&request).expect("JSON serialization failed")))
.expect("test value should be present"),
)
.await
.expect("test value should be present");
let status = response.status();
assert!(
status == StatusCode::OK
|| status == StatusCode::INTERNAL_SERVER_ERROR
|| status == StatusCode::SERVICE_UNAVAILABLE,
"Unexpected status: {}",
status
);
}
#[tokio::test]
async fn test_gpu_batch_completions_single_prompt() {
let app = create_potemkin_app();
let request = GpuBatchRequest {
prompts: vec!["Test".to_string()],
max_tokens: 3,
temperature: 0.7,
top_k: 10,
stop: vec!["<|end|>".to_string()],
};
let response = app
.oneshot(
Request::builder()
.method("POST")
.uri("/v1/batch/completions")
.header("Content-Type", "application/json")
.body(Body::from(serde_json::to_string(&request).expect("JSON serialization failed")))
.expect("test value should be present"),
)
.await
.expect("test value should be present");
let _status = response.status();
}
#[tokio::test]
async fn test_gpu_batch_completions_empty_prompts() {
let app = create_potemkin_app();
let request = GpuBatchRequest {
prompts: vec![],
max_tokens: 5,
temperature: 0.0,
top_k: 1,
stop: vec![],
};
let response = app
.oneshot(
Request::builder()
.method("POST")
.uri("/v1/batch/completions")
.header("Content-Type", "application/json")
.body(Body::from(serde_json::to_string(&request).expect("JSON serialization failed")))
.expect("test value should be present"),
)
.await
.expect("test value should be present");
let _status = response.status();
}
#[test]
fn test_batch_config_default() {
let config = BatchConfig::default();
assert_eq!(config.window_ms, 50);
assert_eq!(config.min_batch, 4);
assert_eq!(config.optimal_batch, 32);
assert_eq!(config.max_batch, 64);
assert_eq!(config.gpu_threshold, 32);
}
#[test]
fn test_batch_config_low_latency() {
let config = BatchConfig::low_latency();
assert_eq!(config.window_ms, 5);
assert_eq!(config.min_batch, 2);
assert_eq!(config.optimal_batch, 8);
assert!(config.gpu_threshold > config.max_batch); }
#[test]
fn test_batch_config_high_throughput() {
let config = BatchConfig::high_throughput();
assert_eq!(config.window_ms, 100);
assert_eq!(config.min_batch, 8);
assert_eq!(config.optimal_batch, 32);
assert_eq!(config.max_batch, 128);
}
#[test]
fn test_batch_config_should_process() {
let config = BatchConfig::default();
assert!(!config.should_process(31)); assert!(config.should_process(32)); assert!(config.should_process(64)); }
#[test]
fn test_batch_config_meets_minimum() {
let config = BatchConfig::default();
assert!(!config.meets_minimum(3)); assert!(config.meets_minimum(4)); assert!(config.meets_minimum(10)); }
#[test]
fn test_gpu_warmup_response_serde() {
let response = GpuWarmupResponse {
success: true,
memory_bytes: 1024 * 1024,
num_layers: 12,
message: "Warmed up".to_string(),
};
let json = serde_json::to_string(&response).expect("JSON serialization failed");
let parsed: GpuWarmupResponse = serde_json::from_str(&json).expect("JSON deserialization failed");
assert!(parsed.success);
assert_eq!(parsed.memory_bytes, 1024 * 1024);
assert_eq!(parsed.num_layers, 12);
}
#[test]
fn test_gpu_status_response_serde() {
let response = GpuStatusResponse {
cache_ready: true,
cache_memory_bytes: 2048,
batch_threshold: 32,
recommended_min_batch: 8,
};
let json = serde_json::to_string(&response).expect("JSON serialization failed");
let parsed: GpuStatusResponse = serde_json::from_str(&json).expect("JSON deserialization failed");
assert!(parsed.cache_ready);
assert_eq!(parsed.batch_threshold, 32);
}
#[test]
fn test_gpu_batch_request_serde() {
let request = GpuBatchRequest {
prompts: vec!["a".to_string(), "b".to_string()],
max_tokens: 100,
temperature: 0.8,
top_k: 50,
stop: vec!["</s>".to_string()],
};
let json = serde_json::to_string(&request).expect("JSON serialization failed");
let parsed: GpuBatchRequest = serde_json::from_str(&json).expect("JSON deserialization failed");
assert_eq!(parsed.prompts.len(), 2);
assert_eq!(parsed.max_tokens, 100);
assert_eq!(parsed.temperature, 0.8);
}
#[test]
fn test_gpu_batch_response_serde() {
use crate::api::gpu_handlers::{GpuBatchResult, GpuBatchStats};
let response = GpuBatchResponse {
results: vec![GpuBatchResult {
index: 0,
token_ids: vec![1, 2, 3],
text: "hello".to_string(),
num_generated: 3,
}],
stats: GpuBatchStats {
batch_size: 1,
gpu_used: true,
total_tokens: 3,
processing_time_ms: 10.5,
throughput_tps: 285.7,
},
};
let json = serde_json::to_string(&response).expect("JSON serialization failed");
let parsed: GpuBatchResponse = serde_json::from_str(&json).expect("JSON deserialization failed");
assert_eq!(parsed.results.len(), 1);
assert!(parsed.stats.gpu_used);
}
#[tokio::test]
async fn test_generate_handler_with_mock_gpu() {
let app = create_potemkin_app();
let request_json = r#"{"prompt": "Hello", "max_tokens": 5}"#;
let response = app
.oneshot(
Request::builder()
.method("POST")
.uri("/v1/generate")
.header("Content-Type", "application/json")
.body(Body::from(request_json))
.expect("test value should be present"),
)
.await
.expect("test value should be present");
let _status = response.status();
}
#[tokio::test]
async fn test_tokenize_handler_with_mock_gpu() {
let app = create_potemkin_app();
let request_json = r#"{"text": "Hello world"}"#;
let response = app
.oneshot(
Request::builder()
.method("POST")
.uri("/v1/tokenize")
.header("Content-Type", "application/json")
.body(Body::from(request_json))
.expect("test value should be present"),
)
.await
.expect("test value should be present");
let status = response.status();
assert!(
status == StatusCode::OK || status == StatusCode::NOT_FOUND,
"Unexpected: {}",
status
);
}
#[tokio::test]
async fn test_batch_tokenize_handler_with_mock_gpu() {
let app = create_potemkin_app();
let request_json = r#"{"texts": ["Hello", "World"]}"#;
let response = app
.oneshot(
Request::builder()
.method("POST")
.uri("/v1/batch/tokenize")
.header("Content-Type", "application/json")
.body(Body::from(request_json))
.expect("test value should be present"),
)
.await
.expect("test value should be present");
let _status = response.status();
}
#[tokio::test]
async fn test_batch_generate_handler_with_mock_gpu() {
let app = create_potemkin_app();
let request_json = r#"{"prompts": ["Hello"], "max_tokens": 5}"#;
let response = app
.oneshot(
Request::builder()
.method("POST")
.uri("/v1/batch/generate")
.header("Content-Type", "application/json")
.body(Body::from(request_json))
.expect("test value should be present"),
)
.await
.expect("test value should be present");
let _status = response.status();
}
#[tokio::test]
async fn test_models_handler_with_mock_gpu() {
let app = create_potemkin_app();
let response = app
.oneshot(
Request::builder()
.method("GET")
.uri("/v1/models")
.body(Body::empty())
.expect("test value should be present"),
)
.await
.expect("test value should be present");
let status = response.status();
assert!(status == StatusCode::OK || status == StatusCode::NOT_FOUND);
}
}
#[cfg(all(test, not(feature = "gpu")))]
mod potemkin_fallback {
#[test]
fn test_potemkin_requires_gpu_feature() {
assert!(true, "GPU feature not enabled - Potemkin tests skipped");
}
}