use super::*;
use crate::domain::events::DomainEvent;
use crate::proxy::types::{
AuditEvent, AuditEventType, BodySize, DurationMillis, Headers, HttpMethod, HttpStatusCode,
RequestId, RequestUri, SessionId as ProxySessionId, TargetUrl,
};
use chrono::Utc;
use eventcore::{CommandExecutor, EventStore, ExecutionOptions, StreamId};
use eventcore_memory::InMemoryEventStore;
use proptest::prelude::*;
use std::sync::Arc;
mod test_helpers {
use super::*;
pub fn create_test_audit_event(event_type: AuditEventType) -> AuditEvent {
AuditEvent {
request_id: RequestId::new(),
session_id: ProxySessionId::new(),
timestamp: Utc::now(),
event_type,
}
}
pub fn request_received_event() -> AuditEventType {
AuditEventType::RequestReceived {
method: HttpMethod::try_new("POST".to_string()).unwrap(),
uri: RequestUri::try_new("/v1/chat/completions".to_string()).unwrap(),
headers: Headers::new(),
body_size: BodySize::from(1024),
}
}
pub fn request_forwarded_event() -> AuditEventType {
AuditEventType::RequestForwarded {
target_url: TargetUrl::try_new(
"https://api.openai.com/v1/chat/completions".to_string(),
)
.unwrap(),
start_time: Utc::now(),
}
}
pub fn response_received_event() -> AuditEventType {
AuditEventType::ResponseReceived {
status: HttpStatusCode::try_new(200).unwrap(),
headers: Headers::new(),
body_size: BodySize::from(2048),
duration_ms: DurationMillis::from(150),
}
}
pub fn create_test_executor() -> Arc<CommandExecutor<InMemoryEventStore<DomainEvent>>> {
let event_store = InMemoryEventStore::new();
Arc::new(CommandExecutor::new(event_store))
}
pub fn create_openai_request_body() -> Vec<u8> {
serde_json::json!({
"model": "gpt-4",
"messages": [
{"role": "user", "content": "Hello, world!"}
],
"temperature": 0.7,
"max_tokens": 100
})
.to_string()
.as_bytes()
.to_vec()
}
pub fn create_anthropic_request_body() -> Vec<u8> {
serde_json::json!({
"model": "claude-3-opus-20240229",
"messages": [
{"role": "user", "content": "What is 2+2?"}
],
"max_tokens": 100
})
.to_string()
.as_bytes()
.to_vec()
}
}
use test_helpers::*;
mod concurrent_processing {
use super::*;
#[tokio::test]
async fn test_concurrent_event_processing_maintains_order() {
let executor = create_test_executor();
let session_id = SessionId::generate();
let request_id = RequestId::new();
let commands = vec![
RecordAuditEvent::from_audit_event(&AuditEvent {
request_id,
session_id: ProxySessionId::try_new(session_id.clone().into_inner()).unwrap(),
timestamp: Utc::now(),
event_type: request_received_event(),
})
.unwrap(),
RecordAuditEvent::from_audit_event(&AuditEvent {
request_id,
session_id: ProxySessionId::try_new(session_id.clone().into_inner()).unwrap(),
timestamp: Utc::now() + chrono::Duration::milliseconds(10),
event_type: request_forwarded_event(),
})
.unwrap(),
RecordAuditEvent::from_audit_event(&AuditEvent {
request_id,
session_id: ProxySessionId::try_new(session_id.clone().into_inner()).unwrap(),
timestamp: Utc::now() + chrono::Duration::milliseconds(100),
event_type: response_received_event(),
})
.unwrap(),
];
let handles: Vec<_> = commands
.into_iter()
.map(|cmd| {
let exec = executor.clone();
tokio::spawn(async move { exec.execute(cmd, ExecutionOptions::default()).await })
})
.collect();
let results: Vec<_> = futures_util::future::join_all(handles).await;
for result in results {
assert!(result.is_ok());
assert!(result.unwrap().is_ok());
}
let session_stream =
StreamId::try_new(format!("session-{}", session_id.clone().into_inner())).unwrap();
let events = executor
.event_store()
.read_streams(&[session_stream], &eventcore::ReadOptions::default())
.await
.unwrap();
assert!(!events.events.is_empty());
if let DomainEvent::LlmRequestReceived { .. } = &events.events[0].payload {
} else {
panic!("Expected first event to be LlmRequestReceived");
}
}
#[tokio::test]
async fn test_concurrent_writes_to_different_streams() {
let executor = create_test_executor();
let num_requests = 10;
let handles: Vec<_> = (0..num_requests)
.map(|_| {
let exec = executor.clone();
tokio::spawn(async move {
let audit_event = create_test_audit_event(request_received_event());
let command = RecordAuditEvent::from_audit_event(&audit_event).unwrap();
exec.execute(command, ExecutionOptions::default()).await
})
})
.collect();
let results: Vec<_> = futures_util::future::join_all(handles).await;
for result in results {
assert!(result.is_ok());
assert!(result.unwrap().is_ok());
}
}
}
mod event_store_failures {
use super::*;
use crate::infrastructure::eventcore::service::EventCoreService;
#[tokio::test]
async fn test_command_execution_with_retry_options() {
let executor = create_test_executor();
let audit_event = create_test_audit_event(request_received_event());
let command = RecordAuditEvent::from_audit_event(&audit_event).unwrap();
let options = ExecutionOptions::default();
let result = executor.execute(command, options).await;
assert!(result.is_ok());
}
#[tokio::test]
async fn test_command_execution_with_eventcore_service() {
let service = EventCoreService::with_memory_store();
let audit_event = create_test_audit_event(request_received_event());
let command = RecordAuditEvent::from_audit_event(&audit_event).unwrap();
let result = service.execute_command_memory(command).await;
assert!(result.is_ok());
}
}
mod malformed_events {
use super::*;
#[tokio::test]
async fn test_malformed_request_body_uses_fallback() {
let executor = create_test_executor();
let malformed_body = b"{ invalid json }";
let audit_event = create_test_audit_event(request_received_event());
let command = RecordAuditEvent::from_audit_event(&audit_event)
.unwrap()
.with_body(malformed_body);
let result = executor.execute(command, ExecutionOptions::default()).await;
assert!(result.is_ok());
let session_stream =
StreamId::try_new(format!("session-{}", audit_event.session_id.as_ref())).unwrap();
let events = executor
.event_store()
.read_streams(&[session_stream], &eventcore::ReadOptions::default())
.await
.unwrap();
assert_eq!(events.events.len(), 1);
if let DomainEvent::LlmRequestReceived {
model_version,
prompt,
..
} = &events.events[0].payload
{
assert_eq!(model_version.model_id.as_ref(), "unknown-model");
assert!(prompt.as_ref().contains("Failed to parse"));
} else {
panic!("Expected LlmRequestReceived event");
}
}
#[tokio::test]
async fn test_empty_request_body_uses_fallback() {
let executor = create_test_executor();
let audit_event = create_test_audit_event(request_received_event());
let command = RecordAuditEvent::from_audit_event(&audit_event)
.unwrap()
.with_body(&[]);
let result = executor.execute(command, ExecutionOptions::default()).await;
assert!(result.is_ok());
}
#[tokio::test]
async fn test_non_utf8_request_body_uses_fallback() {
let executor = create_test_executor();
let invalid_utf8 = vec![0xFF, 0xFE, 0xFD];
let audit_event = create_test_audit_event(request_received_event());
let command = RecordAuditEvent::from_audit_event(&audit_event)
.unwrap()
.with_body(&invalid_utf8);
let result = executor.execute(command, ExecutionOptions::default()).await;
assert!(result.is_ok());
}
}
mod event_ordering {
use super::*;
#[tokio::test]
async fn test_events_ordered_within_stream() {
let executor = create_test_executor();
let session_id = SessionId::generate();
let request_id = RequestId::new();
let base_time = Utc::now();
let timestamps = [
base_time,
base_time + chrono::Duration::milliseconds(100),
base_time + chrono::Duration::milliseconds(200),
];
for (i, event_type) in vec![
request_received_event(),
request_forwarded_event(),
response_received_event(),
]
.into_iter()
.enumerate()
{
let audit_event = AuditEvent {
request_id,
session_id: ProxySessionId::try_new(session_id.clone().into_inner()).unwrap(),
timestamp: timestamps[i],
event_type,
};
let command = RecordAuditEvent::from_audit_event(&audit_event).unwrap();
executor
.execute(command, ExecutionOptions::default())
.await
.unwrap();
}
let session_stream =
StreamId::try_new(format!("session-{}", session_id.clone().into_inner())).unwrap();
let session_events = executor
.event_store()
.read_streams(
&[session_stream.clone()],
&eventcore::ReadOptions::default(),
)
.await
.unwrap();
assert_eq!(session_events.events.len(), 1);
let request_stream = StreamId::try_new(format!("request-{request_id}")).unwrap();
let request_events = executor
.event_store()
.read_streams(&[request_stream], &eventcore::ReadOptions::default())
.await
.unwrap();
assert_eq!(request_events.events.len(), 2);
for window in request_events.events.windows(2) {
assert!(window[0].timestamp <= window[1].timestamp);
}
}
#[tokio::test]
async fn test_multiple_stream_reads() {
let executor = create_test_executor();
let num_sessions = 5;
let mut stream_ids = Vec::new();
for _ in 0..num_sessions {
let audit_event = create_test_audit_event(request_received_event());
let session_stream =
StreamId::try_new(format!("session-{}", audit_event.session_id.as_ref())).unwrap();
stream_ids.push(session_stream);
let command = RecordAuditEvent::from_audit_event(&audit_event).unwrap();
executor
.execute(command, ExecutionOptions::default())
.await
.unwrap();
}
let all_events = executor
.event_store()
.read_streams(&stream_ids, &eventcore::ReadOptions::default())
.await
.unwrap();
assert_eq!(all_events.events.len(), num_sessions);
}
}
mod idempotency {
use super::*;
#[tokio::test]
async fn test_duplicate_request_received_ignored() {
let executor = create_test_executor();
let audit_event = create_test_audit_event(request_received_event());
let command = RecordAuditEvent::from_audit_event(&audit_event).unwrap();
executor
.execute(command.clone(), ExecutionOptions::default())
.await
.unwrap();
executor
.execute(command, ExecutionOptions::default())
.await
.unwrap();
let session_stream =
StreamId::try_new(format!("session-{}", audit_event.session_id.as_ref())).unwrap();
let events = executor
.event_store()
.read_streams(&[session_stream], &eventcore::ReadOptions::default())
.await
.unwrap();
assert_eq!(events.events.len(), 1);
}
#[tokio::test]
async fn test_duplicate_request_forwarded_ignored() {
let executor = create_test_executor();
let session_id = SessionId::generate();
let request_id = RequestId::new();
let received_event = AuditEvent {
request_id,
session_id: ProxySessionId::try_new(session_id.clone().into_inner()).unwrap(),
timestamp: Utc::now(),
event_type: request_received_event(),
};
let cmd = RecordAuditEvent::from_audit_event(&received_event).unwrap();
executor
.execute(cmd, ExecutionOptions::default())
.await
.unwrap();
let forwarded_event = AuditEvent {
request_id,
session_id: ProxySessionId::try_new(session_id.clone().into_inner()).unwrap(),
timestamp: Utc::now() + chrono::Duration::milliseconds(10),
event_type: request_forwarded_event(),
};
let cmd = RecordAuditEvent::from_audit_event(&forwarded_event).unwrap();
executor
.execute(cmd.clone(), ExecutionOptions::default())
.await
.unwrap();
executor
.execute(cmd, ExecutionOptions::default())
.await
.unwrap();
let request_stream = StreamId::try_new(format!("request-{request_id}")).unwrap();
let events = executor
.event_store()
.read_streams(&[request_stream], &eventcore::ReadOptions::default())
.await
.unwrap();
let forwarded_count = events
.events
.iter()
.filter(|e| matches!(e.payload, DomainEvent::LlmRequestStarted { .. }))
.count();
assert_eq!(forwarded_count, 1);
}
#[tokio::test]
async fn test_idempotency_across_different_timestamps() {
let executor = create_test_executor();
let session_id = SessionId::generate();
let request_id = RequestId::new();
let base_time = Utc::now();
for i in 0..3 {
let audit_event = AuditEvent {
request_id,
session_id: ProxySessionId::try_new(session_id.clone().into_inner()).unwrap(),
timestamp: base_time + chrono::Duration::seconds(i),
event_type: request_received_event(),
};
let cmd = RecordAuditEvent::from_audit_event(&audit_event).unwrap();
executor
.execute(cmd, ExecutionOptions::default())
.await
.unwrap();
}
let session_stream =
StreamId::try_new(format!("session-{}", session_id.clone().into_inner())).unwrap();
let events = executor
.event_store()
.read_streams(&[session_stream], &eventcore::ReadOptions::default())
.await
.unwrap();
assert_eq!(events.events.len(), 1);
}
}
mod property_tests {
use super::*;
prop_compose! {
fn arb_audit_event_type()(
method in "[A-Z]{3,6}",
uri in "/[a-z/]+",
status in 100..600u16,
body_size in 0..1_000_000usize,
duration_ms in 1..10_000u32,
) -> AuditEventType {
match body_size % 4 {
0 => AuditEventType::RequestReceived {
method: HttpMethod::try_new(method).unwrap(),
uri: RequestUri::try_new(uri).unwrap(),
headers: Headers::new(),
body_size: BodySize::from(body_size),
},
1 => AuditEventType::RequestForwarded {
target_url: TargetUrl::try_new(format!("https://api.example.com{uri}")).unwrap(),
start_time: Utc::now(),
},
2 => AuditEventType::ResponseReceived {
status: HttpStatusCode::try_new(status).unwrap(),
headers: Headers::new(),
body_size: BodySize::from(body_size),
duration_ms: DurationMillis::from(duration_ms as u64),
},
_ => AuditEventType::ResponseReturned {
duration_ms: DurationMillis::from(duration_ms as u64),
},
}
}
}
proptest! {
#[test]
fn test_any_valid_audit_event_can_be_converted_to_command(
event_type in arb_audit_event_type()
) {
let audit_event = AuditEvent {
request_id: RequestId::new(),
session_id: ProxySessionId::new(),
timestamp: Utc::now(),
event_type,
};
let result = RecordAuditEvent::from_audit_event(&audit_event);
prop_assert!(result.is_ok());
}
#[test]
fn test_stream_ids_are_deterministic(
_seed1: u128,
_seed2: u128
) {
let request_id = RequestId::new();
let session_id = ProxySessionId::new();
let audit_event = AuditEvent {
request_id,
session_id,
timestamp: Utc::now(),
event_type: request_received_event(),
};
let command1 = RecordAuditEvent::from_audit_event(&audit_event).unwrap();
let command2 = RecordAuditEvent::from_audit_event(&audit_event).unwrap();
prop_assert_eq!(command1.session_stream, command2.session_stream);
prop_assert_eq!(command1.request_stream, command2.request_stream);
}
}
proptest! {
#[test]
fn test_state_machine_never_goes_backwards(
events in prop::collection::vec(arb_audit_event_type(), 1..20)
) {
let mut state = RequestState::default();
let request_id = crate::domain::llm::RequestId::generate();
let session_id = SessionId::generate();
let timestamp = Timestamp::now();
let mut has_been_received = false;
let mut has_been_forwarded = false;
let mut has_been_responded = false;
for event_type in events {
let domain_event = match event_type {
AuditEventType::RequestReceived { .. } => {
Some(DomainEvent::LlmRequestReceived {
request_id: request_id.clone(),
session_id: session_id.clone(),
model_version: crate::domain::llm::ModelVersion {
provider: crate::domain::llm::LlmProvider::Other(
crate::domain::config_types::ProviderName::try_new("test".to_string()).unwrap()
),
model_id: crate::domain::types::ModelId::try_new("test".to_string()).unwrap(),
},
prompt: crate::domain::types::Prompt::try_new("test".to_string()).unwrap(),
parameters: crate::domain::types::LlmParameters::new(Default::default()),
received_at: timestamp,
})
},
AuditEventType::RequestForwarded { .. } => {
Some(DomainEvent::LlmRequestStarted {
request_id: request_id.clone(),
started_at: timestamp,
})
},
AuditEventType::ResponseReceived { .. } => {
Some(DomainEvent::LlmResponseReceived {
request_id: request_id.clone(),
response_text: crate::domain::types::ResponseText::try_new("test".to_string()).unwrap(),
metadata: Default::default(),
received_at: timestamp,
})
},
_ => None,
};
if let Some(event) = domain_event {
let old_state = state.clone();
state.apply(&event);
if old_state.is_request_received() {
has_been_received = true;
}
if old_state.is_request_forwarded() {
has_been_forwarded = true;
}
if old_state.is_response_received() {
has_been_responded = true;
}
if has_been_received {
prop_assert!(state.is_request_received());
}
if has_been_forwarded {
prop_assert!(state.is_request_forwarded());
}
if has_been_responded {
prop_assert!(state.is_response_received());
}
}
}
}
}
}
mod recovery_scenarios {
use super::*;
#[tokio::test]
async fn test_recovery_after_partial_processing() {
let executor = create_test_executor();
let session_id = SessionId::generate();
let request_id = RequestId::new();
let received_event = AuditEvent {
request_id,
session_id: ProxySessionId::try_new(session_id.clone().into_inner()).unwrap(),
timestamp: Utc::now(),
event_type: request_received_event(),
};
let cmd = RecordAuditEvent::from_audit_event(&received_event).unwrap();
executor
.execute(cmd, ExecutionOptions::default())
.await
.unwrap();
let response_event = AuditEvent {
request_id,
session_id: ProxySessionId::try_new(session_id.clone().into_inner()).unwrap(),
timestamp: Utc::now() + chrono::Duration::milliseconds(200),
event_type: response_received_event(),
};
let cmd = RecordAuditEvent::from_audit_event(&response_event).unwrap();
let result = executor.execute(cmd, ExecutionOptions::default()).await;
assert!(result.is_ok());
let request_stream = StreamId::try_new(format!("request-{request_id}")).unwrap();
let events = executor
.event_store()
.read_streams(&[request_stream], &eventcore::ReadOptions::default())
.await
.unwrap();
let response_count = events
.events
.iter()
.filter(|e| matches!(e.payload, DomainEvent::LlmResponseReceived { .. }))
.count();
assert_eq!(response_count, 0);
}
#[tokio::test]
async fn test_recovery_with_out_of_order_events() {
let executor = create_test_executor();
let session_id = SessionId::generate();
let request_id = RequestId::new();
let base_time = Utc::now();
let events = vec![
(
response_received_event(),
base_time + chrono::Duration::milliseconds(200),
),
(
request_forwarded_event(),
base_time + chrono::Duration::milliseconds(100),
),
(request_received_event(), base_time),
];
for (event_type, timestamp) in events {
let audit_event = AuditEvent {
request_id,
session_id: ProxySessionId::try_new(session_id.clone().into_inner()).unwrap(),
timestamp,
event_type,
};
let cmd = RecordAuditEvent::from_audit_event(&audit_event).unwrap();
executor
.execute(cmd, ExecutionOptions::default())
.await
.unwrap();
}
let session_stream =
StreamId::try_new(format!("session-{}", session_id.clone().into_inner())).unwrap();
let session_events = executor
.event_store()
.read_streams(&[session_stream], &eventcore::ReadOptions::default())
.await
.unwrap();
assert_eq!(session_events.events.len(), 1);
assert!(matches!(
session_events.events[0].payload,
DomainEvent::LlmRequestReceived { .. }
));
}
}
mod process_request_body_tests {
use super::*;
#[tokio::test]
async fn test_process_request_body_parses_openai_format() {
let executor = create_test_executor();
let session_id = SessionId::generate();
let request_id = RequestId::new();
let body = create_openai_request_body();
let command = ProcessRequestBody {
session_stream: StreamId::try_new(format!(
"session-{}",
session_id.clone().into_inner()
))
.unwrap(),
request_stream: StreamId::try_new(format!("request-{request_id}")).unwrap(),
request_id,
session_id: session_id.clone(),
method: HttpMethod::try_new("POST".to_string()).unwrap(),
uri: RequestUri::try_new("/v1/chat/completions".to_string()).unwrap(),
headers: Headers::new(),
body,
timestamp: Timestamp::now(),
};
let result = executor.execute(command, ExecutionOptions::default()).await;
assert!(result.is_ok());
let session_stream =
StreamId::try_new(format!("session-{}", session_id.clone().into_inner())).unwrap();
let events = executor
.event_store()
.read_streams(&[session_stream], &eventcore::ReadOptions::default())
.await
.unwrap();
assert_eq!(events.events.len(), 1);
if let DomainEvent::LlmRequestReceived {
model_version,
prompt,
..
} = &events.events[0].payload
{
assert_eq!(model_version.model_id.as_ref(), "gpt-4");
assert!(prompt.as_ref().contains("Hello, world!"));
} else {
panic!("Expected LlmRequestReceived event");
}
}
#[tokio::test]
async fn test_process_request_body_parses_anthropic_format() {
let executor = create_test_executor();
let session_id = SessionId::generate();
let request_id = RequestId::new();
let body = create_anthropic_request_body();
let command = ProcessRequestBody {
session_stream: StreamId::try_new(format!(
"session-{}",
session_id.clone().into_inner()
))
.unwrap(),
request_stream: StreamId::try_new(format!("request-{request_id}")).unwrap(),
request_id,
session_id: session_id.clone(),
method: HttpMethod::try_new("POST".to_string()).unwrap(),
uri: RequestUri::try_new("/v1/messages".to_string()).unwrap(),
headers: Headers::new(),
body,
timestamp: Timestamp::now(),
};
let result = executor.execute(command, ExecutionOptions::default()).await;
assert!(result.is_ok());
let session_stream =
StreamId::try_new(format!("session-{}", session_id.clone().into_inner())).unwrap();
let events = executor
.event_store()
.read_streams(&[session_stream], &eventcore::ReadOptions::default())
.await
.unwrap();
assert_eq!(events.events.len(), 1);
if let DomainEvent::LlmRequestReceived {
model_version,
prompt,
..
} = &events.events[0].payload
{
assert_eq!(model_version.model_id.as_ref(), "claude-3-opus-20240229");
assert!(prompt.as_ref().contains("What is 2+2?"));
} else {
panic!("Expected LlmRequestReceived event");
}
}
#[tokio::test]
async fn test_process_request_body_idempotent() {
let executor = create_test_executor();
let session_id = SessionId::generate();
let request_id = RequestId::new();
let body = create_openai_request_body();
let command = ProcessRequestBody {
session_stream: StreamId::try_new(format!(
"session-{}",
session_id.clone().into_inner()
))
.unwrap(),
request_stream: StreamId::try_new(format!("request-{request_id}")).unwrap(),
request_id,
session_id: session_id.clone(),
method: HttpMethod::try_new("POST".to_string()).unwrap(),
uri: RequestUri::try_new("/v1/chat/completions".to_string()).unwrap(),
headers: Headers::new(),
body,
timestamp: Timestamp::now(),
};
executor
.execute(command.clone(), ExecutionOptions::default())
.await
.unwrap();
executor
.execute(command, ExecutionOptions::default())
.await
.unwrap();
let session_stream =
StreamId::try_new(format!("session-{}", session_id.clone().into_inner())).unwrap();
let events = executor
.event_store()
.read_streams(&[session_stream], &eventcore::ReadOptions::default())
.await
.unwrap();
assert_eq!(events.events.len(), 1);
}
}
mod edge_cases {
use super::*;
#[tokio::test]
async fn test_extremely_large_request_body() {
let executor = create_test_executor();
let session_id = SessionId::generate();
let request_id = RequestId::new();
let large_content = "x".repeat(10 * 1024 * 1024);
let body = serde_json::json!({
"model": "gpt-4",
"messages": [{
"role": "user",
"content": large_content
}]
})
.to_string()
.as_bytes()
.to_vec();
let command = ProcessRequestBody {
session_stream: StreamId::try_new(format!(
"session-{}",
session_id.clone().into_inner()
))
.unwrap(),
request_stream: StreamId::try_new(format!("request-{request_id}")).unwrap(),
request_id,
session_id: session_id.clone(),
method: HttpMethod::try_new("POST".to_string()).unwrap(),
uri: RequestUri::try_new("/v1/chat/completions".to_string()).unwrap(),
headers: Headers::new(),
body,
timestamp: Timestamp::now(),
};
let result = executor.execute(command, ExecutionOptions::default()).await;
assert!(result.is_ok());
}
#[tokio::test]
async fn test_request_with_special_characters_in_uri() {
let executor = create_test_executor();
let test_uris = vec![
"/v1/chat/completions?api-version=2023-05-15",
"/v1/models/gpt-4-turbo%2Flatest/chat",
"/v1/assistants/asst_abc123/threads/thread_xyz789",
"/v1/fine-tunes/ft-1234567890/events?stream=true",
];
for uri_str in test_uris {
let audit_event = AuditEvent {
request_id: RequestId::new(),
session_id: ProxySessionId::new(),
timestamp: Utc::now(),
event_type: AuditEventType::RequestReceived {
method: HttpMethod::try_new("POST".to_string()).unwrap(),
uri: RequestUri::try_new(uri_str.to_string()).unwrap(),
headers: Headers::new(),
body_size: BodySize::from(100),
},
};
let command = RecordAuditEvent::from_audit_event(&audit_event).unwrap();
let result = executor.execute(command, ExecutionOptions::default()).await;
assert!(result.is_ok());
}
}
#[tokio::test]
async fn test_zero_duration_response() {
let executor = create_test_executor();
let audit_event = AuditEvent {
request_id: RequestId::new(),
session_id: ProxySessionId::new(),
timestamp: Utc::now(),
event_type: AuditEventType::ResponseReceived {
status: HttpStatusCode::try_new(200).unwrap(),
headers: Headers::new(),
body_size: BodySize::from(0),
duration_ms: DurationMillis::from(0), },
};
let command = RecordAuditEvent::from_audit_event(&audit_event).unwrap();
let result = executor.execute(command, ExecutionOptions::default()).await;
assert!(result.is_ok());
}
}
mod headers_tests {
use super::*;
#[tokio::test]
async fn test_headers_with_authorization() {
let executor = create_test_executor();
let session_id = SessionId::generate();
let request_id = RequestId::new();
let headers = Headers::from_vec(vec![
(
"Authorization".to_string(),
"Bearer sk-1234567890abcdef".to_string(),
),
("Content-Type".to_string(), "application/json".to_string()),
])
.unwrap();
let body = create_openai_request_body();
let command = ProcessRequestBody {
session_stream: StreamId::try_new(format!(
"session-{}",
session_id.clone().into_inner()
))
.unwrap(),
request_stream: StreamId::try_new(format!("request-{request_id}")).unwrap(),
request_id,
session_id: session_id.clone(),
method: HttpMethod::try_new("POST".to_string()).unwrap(),
uri: RequestUri::try_new("/v1/chat/completions".to_string()).unwrap(),
headers,
body,
timestamp: Timestamp::now(),
};
let result = executor.execute(command, ExecutionOptions::default()).await;
assert!(result.is_ok());
let session_stream =
StreamId::try_new(format!("session-{}", session_id.clone().into_inner())).unwrap();
let events = executor
.event_store()
.read_streams(&[session_stream], &eventcore::ReadOptions::default())
.await
.unwrap();
assert_eq!(events.events.len(), 1);
}
}
#[cfg(test)]
mod benchmarks {
use super::*;
use std::time::Instant;
#[tokio::test]
async fn bench_single_command_execution() {
let executor = create_test_executor();
let iterations = 100;
let start = Instant::now();
for _ in 0..iterations {
let audit_event = create_test_audit_event(request_received_event());
let command = RecordAuditEvent::from_audit_event(&audit_event).unwrap();
executor
.execute(command, ExecutionOptions::default())
.await
.unwrap();
}
let duration = start.elapsed();
let per_command = duration / iterations;
println!("Average command execution time: {per_command:?}");
assert!(per_command.as_millis() < 10); }
#[tokio::test]
async fn bench_concurrent_command_execution() {
let executor = create_test_executor();
let concurrent_commands = 50;
let start = Instant::now();
let handles: Vec<_> = (0..concurrent_commands)
.map(|_| {
let exec = executor.clone();
tokio::spawn(async move {
let audit_event = create_test_audit_event(request_received_event());
let command = RecordAuditEvent::from_audit_event(&audit_event).unwrap();
exec.execute(command, ExecutionOptions::default()).await
})
})
.collect();
let results: Vec<_> = futures_util::future::join_all(handles).await;
let duration = start.elapsed();
for result in results {
assert!(result.is_ok());
assert!(result.unwrap().is_ok());
}
println!("Concurrent execution of {concurrent_commands} commands took: {duration:?}");
assert!(duration.as_secs() < 2); }
}