#[cfg(test)]
mod proxy_service_tests {
use crate::proxy::types::{BufferSize, RequestSizeLimit, ResponseSizeLimit, SlotSize};
use crate::proxy::{ProxyConfig, ProxyService};
use std::time::Duration;
#[test]
fn test_proxy_service_creation() {
let config = ProxyConfig::default();
let service = ProxyService::new(config);
let ring_buffer = service.ring_buffer();
assert_eq!(ring_buffer.overflow_count(), 0);
}
#[test]
fn test_proxy_config_defaults() {
let config = ProxyConfig::default();
assert_eq!(*config.max_request_size.as_ref(), 10 * 1024 * 1024);
assert_eq!(*config.max_response_size.as_ref(), 10 * 1024 * 1024);
assert_eq!(config.request_timeout, Duration::from_secs(30));
assert_eq!(*config.ring_buffer.buffer_size.as_ref(), 1024 * 1024 * 1024);
assert_eq!(*config.ring_buffer.slot_size.as_ref(), 64 * 1024);
}
#[test]
fn test_custom_proxy_config() {
let config = ProxyConfig {
max_request_size: RequestSizeLimit::try_new(1024 * 1024).expect("valid size"), max_response_size: ResponseSizeLimit::try_new(2 * 1024 * 1024).expect("valid size"), request_timeout: Duration::from_secs(60),
ring_buffer: crate::proxy::types::RingBufferConfig {
buffer_size: BufferSize::try_new(512 * 1024 * 1024).expect("valid size"), slot_size: SlotSize::try_new(32 * 1024).expect("valid size"), },
bedrock_region: None,
};
let _service = ProxyService::new(config.clone());
assert_eq!(*config.max_request_size.as_ref(), 1024 * 1024);
assert_eq!(*config.ring_buffer.buffer_size.as_ref(), 512 * 1024 * 1024);
}
#[tokio::test]
async fn test_proxy_service_router_creation() {
let config = ProxyConfig::default();
let service = ProxyService::new(config);
let auth_config = crate::proxy::AuthConfig::default();
let router = service.into_router(auth_config);
let _ = router;
}
}
#[cfg(test)]
mod type_tests {
use crate::proxy::types::*;
use uuid::Uuid;
#[test]
fn test_request_id_creation() {
let id = RequestId::new();
let uuid: &Uuid = id.as_ref();
assert_eq!(uuid.get_version_num(), 7);
}
#[test]
fn test_session_id_creation() {
let id = SessionId::new();
let uuid: &Uuid = id.as_ref();
assert_eq!(uuid.get_version_num(), 7);
}
#[test]
fn test_target_url_validation() {
assert!(TargetUrl::try_new("https://api.example.com").is_ok());
assert!(TargetUrl::try_new("http://localhost:8080").is_ok());
assert!(TargetUrl::try_new("not-a-url").is_err());
assert!(TargetUrl::try_new("ftp://example.com").is_err());
assert!(TargetUrl::try_new("").is_err());
}
#[test]
fn test_api_key_validation() {
assert!(ApiKey::try_new("sk-1234567890").is_ok());
assert!(ApiKey::try_new("test-key").is_ok());
assert!(ApiKey::try_new("").is_err());
}
#[test]
fn test_audit_event_serialization() {
let event = AuditEvent {
request_id: RequestId::new(),
session_id: SessionId::new(),
timestamp: chrono::Utc::now(),
event_type: AuditEventType::RequestReceived {
method: HttpMethod::try_new(METHOD_POST.to_string()).unwrap(),
uri: RequestUri::try_new("/v1/chat/completions".to_string()).unwrap(),
headers: Headers::from_vec(vec![(
"content-type".to_string(),
"application/json".to_string(),
)])
.unwrap_or_default(),
body_size: BodySize::from(1024),
},
};
let json = serde_json::to_string(&event).unwrap();
assert!(json.contains("RequestReceived"));
let deserialized: AuditEvent = serde_json::from_str(&json).unwrap();
assert_eq!(deserialized.request_id.as_ref(), event.request_id.as_ref());
}
}
#[cfg(test)]
mod streaming_tests {
use crate::proxy::types::{BufferSize, ResponseSizeLimit, SlotSize};
use crate::proxy::{ProxyConfig, ProxyService};
use axum::{body::Body, http::StatusCode};
use bytes::Bytes;
use futures_util::stream;
use std::time::Duration;
use tower::ServiceExt;
#[tokio::test]
async fn test_streaming_request_handling() {
let chunks = vec![
Ok::<_, std::io::Error>(Bytes::from("chunk1")),
Ok(Bytes::from("chunk2")),
Ok(Bytes::from("chunk3")),
];
let stream = stream::iter(chunks);
let body = Body::from_stream(stream);
let request = http::Request::builder()
.method("POST")
.uri("/stream")
.header("content-type", "application/octet-stream")
.body(body)
.unwrap();
let config = ProxyConfig::default();
let service = ProxyService::new(config);
let auth_config = crate::proxy::AuthConfig::default();
let app = service.into_router(auth_config);
let _ = app.oneshot(request).await;
}
#[tokio::test]
async fn test_zero_copy_streaming() {
let config = ProxyConfig {
max_response_size: ResponseSizeLimit::try_new(10 * 1024 * 1024).expect("valid size"), ..Default::default()
};
let service = ProxyService::new(config);
let auth_config = crate::proxy::AuthConfig::default();
let _app = service.into_router(auth_config);
}
#[tokio::test]
async fn test_streaming_with_ring_buffer_capture() {
let config = ProxyConfig::default();
let service = ProxyService::new(config);
let ring_buffer = service.ring_buffer();
assert_eq!(ring_buffer.overflow_count(), 0);
}
#[tokio::test]
async fn test_large_streaming_response() {
let config = ProxyConfig {
ring_buffer: crate::proxy::types::RingBufferConfig {
buffer_size: BufferSize::try_new(1024 * 1024).expect("valid size"), slot_size: SlotSize::try_new(64 * 1024).expect("valid size"), },
..Default::default()
};
let _service = ProxyService::new(config);
}
#[tokio::test]
async fn test_streaming_timeout_handling() {
let config = ProxyConfig {
request_timeout: Duration::from_millis(100),
..Default::default()
};
let _service = ProxyService::new(config);
}
#[tokio::test]
async fn test_streaming_error_handling() {
let error_stream = stream::once(async {
Err::<Bytes, std::io::Error>(std::io::Error::new(
std::io::ErrorKind::ConnectionAborted,
"Connection lost",
))
});
let body = Body::from_stream(error_stream);
let request = http::Request::builder()
.method("POST")
.uri("/error-stream")
.body(body)
.unwrap();
let config = ProxyConfig::default();
let service = ProxyService::new(config);
let auth_config = crate::proxy::AuthConfig::default();
let app = service.into_router(auth_config);
let response = app.oneshot(request).await.unwrap();
assert_eq!(response.status(), StatusCode::UNAUTHORIZED);
}
}