#[tokio::test]
async fn test_imp_127d_dispatch_metrics_no_gpu_model() {
let state = AppState::demo().expect("Should create demo AppState");
let app = create_router(state);
let response = app
.oneshot(
Request::builder()
.uri("/metrics/dispatch")
.body(Body::empty())
.expect("test"),
)
.await
.expect("test");
assert_eq!(
response.status(),
StatusCode::SERVICE_UNAVAILABLE,
"IMP-127d: /metrics/dispatch should return 503 when no GPU model configured"
);
}
#[tokio::test]
#[cfg(feature = "gpu")]
async fn test_imp_128a_prometheus_format_endpoint() {
use crate::gguf::{GGUFConfig, OwnedQuantizedModelCachedSync};
let config = GGUFConfig {
architecture: "test".to_string(),
constraints: crate::gguf::ArchConstraints::from_architecture("test"),
hidden_dim: 64,
intermediate_dim: 128,
num_layers: 1,
num_heads: 4,
num_kv_heads: 4,
vocab_size: 100,
context_length: 128,
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 model = create_test_quantized_model(&config);
let cached_model = OwnedQuantizedModelCachedSync::new(model);
let state =
AppState::with_cached_model(cached_model).expect("IMP-128a: Should create AppState");
let app = create_router(state);
let response = app
.oneshot(
Request::builder()
.uri("/metrics/dispatch?format=prometheus")
.body(Body::empty())
.expect("test"),
)
.await
.expect("test");
assert_eq!(
response.status(),
StatusCode::OK,
"IMP-128a: Prometheus format should return 200 OK"
);
let content_type = response
.headers()
.get("content-type")
.and_then(|v| v.to_str().ok());
assert!(
content_type.is_some_and(|s| s.contains("text/plain")),
"IMP-128a: Prometheus response should be text/plain"
);
}
#[tokio::test]
#[cfg(feature = "gpu")]
async fn test_imp_128b_prometheus_format_structure() {
use crate::gguf::{GGUFConfig, OwnedQuantizedModelCachedSync};
let config = GGUFConfig {
architecture: "test".to_string(),
constraints: crate::gguf::ArchConstraints::from_architecture("test"),
hidden_dim: 64,
intermediate_dim: 128,
num_layers: 1,
num_heads: 4,
num_kv_heads: 4,
vocab_size: 100,
context_length: 128,
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 model = create_test_quantized_model(&config);
let cached_model = OwnedQuantizedModelCachedSync::new(model);
let state =
AppState::with_cached_model(cached_model).expect("IMP-128b: Should create AppState");
let app = create_router(state);
let response = app
.oneshot(
Request::builder()
.uri("/metrics/dispatch?format=prometheus")
.body(Body::empty())
.expect("test"),
)
.await
.expect("test");
let body = axum::body::to_bytes(response.into_body(), usize::MAX)
.await
.expect("test");
let text = String::from_utf8_lossy(&body);
assert!(
text.contains("realizar_dispatch_cpu_total"),
"IMP-128b: Should have CPU dispatch counter"
);
assert!(
text.contains("realizar_dispatch_gpu_total"),
"IMP-128b: Should have GPU dispatch counter"
);
assert!(
text.contains("realizar_dispatch_gpu_ratio"),
"IMP-128b: Should have GPU ratio gauge"
);
}
#[tokio::test]
#[cfg(feature = "gpu")]
async fn test_imp_128c_default_format_is_json() {
use crate::gguf::{GGUFConfig, OwnedQuantizedModelCachedSync};
let config = GGUFConfig {
architecture: "test".to_string(),
constraints: crate::gguf::ArchConstraints::from_architecture("test"),
hidden_dim: 64,
intermediate_dim: 128,
num_layers: 1,
num_heads: 4,
num_kv_heads: 4,
vocab_size: 100,
context_length: 128,
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 model = create_test_quantized_model(&config);
let cached_model = OwnedQuantizedModelCachedSync::new(model);
let state =
AppState::with_cached_model(cached_model).expect("IMP-128c: Should create AppState");
let app = create_router(state);
let response = app
.oneshot(
Request::builder()
.uri("/metrics/dispatch")
.body(Body::empty())
.expect("test"),
)
.await
.expect("test");
let content_type = response
.headers()
.get("content-type")
.and_then(|v| v.to_str().ok());
assert!(
content_type.is_some_and(|s| s.contains("application/json")),
"IMP-128c: Default format should be JSON"
);
}
#[tokio::test]
#[cfg(feature = "gpu")]
async fn test_imp_128d_explicit_json_format() {
use crate::gguf::{GGUFConfig, OwnedQuantizedModelCachedSync};
let config = GGUFConfig {
architecture: "test".to_string(),
constraints: crate::gguf::ArchConstraints::from_architecture("test"),
hidden_dim: 64,
intermediate_dim: 128,
num_layers: 1,
num_heads: 4,
num_kv_heads: 4,
vocab_size: 100,
context_length: 128,
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 model = create_test_quantized_model(&config);
let cached_model = OwnedQuantizedModelCachedSync::new(model);
let state =
AppState::with_cached_model(cached_model).expect("IMP-128d: Should create AppState");
let app = create_router(state);
let response = app
.oneshot(
Request::builder()
.uri("/metrics/dispatch?format=json")
.body(Body::empty())
.expect("test"),
)
.await
.expect("test");
let content_type = response
.headers()
.get("content-type")
.and_then(|v| v.to_str().ok());
assert!(
content_type.is_some_and(|s| s.contains("application/json")),
"IMP-128d: format=json should return JSON"
);
}
#[tokio::test]
#[cfg(feature = "gpu")]
async fn test_imp_130a_prometheus_includes_cpu_latency_histogram() {
use crate::gguf::{GGUFConfig, OwnedQuantizedModelCachedSync};
let config = GGUFConfig {
architecture: "test".to_string(),
constraints: crate::gguf::ArchConstraints::from_architecture("test"),
hidden_dim: 64,
intermediate_dim: 128,
num_layers: 1,
num_heads: 4,
num_kv_heads: 4,
vocab_size: 100,
context_length: 128,
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 model = create_test_quantized_model(&config);
let cached_model = OwnedQuantizedModelCachedSync::new(model);
let state =
AppState::with_cached_model(cached_model).expect("IMP-130a: Should create AppState");
if let Some(metrics) = state.dispatch_metrics() {
metrics.record_cpu_latency(std::time::Duration::from_micros(50));
metrics.record_cpu_latency(std::time::Duration::from_micros(200));
metrics.record_cpu_latency(std::time::Duration::from_micros(800));
}
let app = create_router(state);
let response = app
.oneshot(
Request::builder()
.uri("/metrics/dispatch?format=prometheus")
.body(Body::empty())
.expect("test"),
)
.await
.expect("test");
let body = axum::body::to_bytes(response.into_body(), usize::MAX)
.await
.expect("test");
let body_str = String::from_utf8_lossy(&body);
assert!(
body_str.contains("realizar_dispatch_cpu_latency_bucket"),
"IMP-130a: Prometheus should include CPU latency histogram buckets. Got: {}",
body_str
);
assert!(
body_str.contains("realizar_dispatch_cpu_latency_sum"),
"IMP-130a: Prometheus should include CPU latency sum"
);
assert!(
body_str.contains("realizar_dispatch_cpu_latency_count"),
"IMP-130a: Prometheus should include CPU latency count"
);
}
#[tokio::test]
#[cfg(feature = "gpu")]
async fn test_imp_130b_prometheus_includes_gpu_latency_histogram() {
use crate::gguf::{GGUFConfig, OwnedQuantizedModelCachedSync};
let config = GGUFConfig {
architecture: "test".to_string(),
constraints: crate::gguf::ArchConstraints::from_architecture("test"),
hidden_dim: 64,
intermediate_dim: 128,
num_layers: 1,
num_heads: 4,
num_kv_heads: 4,
vocab_size: 100,
context_length: 128,
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 model = create_test_quantized_model(&config);
let cached_model = OwnedQuantizedModelCachedSync::new(model);
let state =
AppState::with_cached_model(cached_model).expect("IMP-130b: Should create AppState");
if let Some(metrics) = state.dispatch_metrics() {
metrics.record_gpu_latency(std::time::Duration::from_micros(150));
metrics.record_gpu_latency(std::time::Duration::from_micros(600));
}
let app = create_router(state);
let response = app
.oneshot(
Request::builder()
.uri("/metrics/dispatch?format=prometheus")
.body(Body::empty())
.expect("test"),
)
.await
.expect("test");
let body = axum::body::to_bytes(response.into_body(), usize::MAX)
.await
.expect("test");
let body_str = String::from_utf8_lossy(&body);
assert!(
body_str.contains("realizar_dispatch_gpu_latency_bucket"),
"IMP-130b: Prometheus should include GPU latency histogram buckets. Got: {}",
body_str
);
assert!(
body_str.contains("realizar_dispatch_gpu_latency_sum"),
"IMP-130b: Prometheus should include GPU latency sum"
);
assert!(
body_str.contains("realizar_dispatch_gpu_latency_count"),
"IMP-130b: Prometheus should include GPU latency count"
);
}
#[tokio::test]
#[cfg(feature = "gpu")]
async fn test_imp_130c_prometheus_latency_buckets_have_correct_labels() {
use crate::gguf::{GGUFConfig, OwnedQuantizedModelCachedSync};
let config = GGUFConfig {
architecture: "test".to_string(),
constraints: crate::gguf::ArchConstraints::from_architecture("test"),
hidden_dim: 64,
intermediate_dim: 128,
num_layers: 1,
num_heads: 4,
num_kv_heads: 4,
vocab_size: 100,
context_length: 128,
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 model = create_test_quantized_model(&config);
let cached_model = OwnedQuantizedModelCachedSync::new(model);
let state =
AppState::with_cached_model(cached_model).expect("IMP-130c: Should create AppState");
let app = create_router(state);
let response = app
.oneshot(
Request::builder()
.uri("/metrics/dispatch?format=prometheus")
.body(Body::empty())
.expect("test"),
)
.await
.expect("test");
let body = axum::body::to_bytes(response.into_body(), usize::MAX)
.await
.expect("test");
let body_str = String::from_utf8_lossy(&body);
assert!(
body_str.contains(r#"le="100""#),
"IMP-130c: Should have 100µs bucket label"
);
assert!(
body_str.contains(r#"le="500""#),
"IMP-130c: Should have 500µs bucket label"
);
assert!(
body_str.contains(r#"le="1000""#),
"IMP-130c: Should have 1000µs bucket label"
);
assert!(
body_str.contains(r#"le="5000""#),
"IMP-130c: Should have 5000µs bucket label"
);
assert!(
body_str.contains(r#"le="+Inf""#),
"IMP-130c: Should have +Inf bucket label"
);
}