sync from monorepo @ 2452e92e

This commit is contained in:
2026-05-08 01:59:04 +02:00
commit b03dc15371
459 changed files with 129586 additions and 0 deletions
@@ -0,0 +1,327 @@
//! Integration test for concurrent agent requests (T050)
//!
//! This test verifies that the system can handle multiple agent requests
//! simultaneously without cross-contamination.
//!
//! Test scenario:
//! 1. Register multiple pending requests concurrently
//! 2. Complete them in random/different order
//! 3. Verify each response goes to the correct request
//! 4. Verify no cross-contamination
use dirigent_acp_api::agent_requests::AgentRequestTracker;
use serde_json::json;
use tokio::task::JoinSet;
#[tokio::test]
async fn test_concurrent_requests_basic() {
let tracker = AgentRequestTracker::new();
let client_id = "test-client";
// Register 5 concurrent requests
let mut receivers = Vec::new();
for i in 0..5 {
let rx = tracker.register(client_id, json!(i));
receivers.push((i, rx));
}
assert_eq!(tracker.pending_count(), 5);
// Complete them in reverse order
for i in (0..5).rev() {
let response = json!({"request": i, "result": "success"});
tracker.complete(client_id, json!(i), response).unwrap();
}
assert_eq!(tracker.pending_count(), 0);
// Verify each receiver got the correct response
for (i, rx) in receivers {
let response = rx.await.unwrap();
assert_eq!(response["request"], i);
}
}
#[tokio::test]
async fn test_concurrent_requests_multiple_clients() {
let tracker = AgentRequestTracker::new();
// Two clients, each with 3 requests
let client1 = "client-1";
let client2 = "client-2";
let mut receivers1 = Vec::new();
let mut receivers2 = Vec::new();
for i in 0..3 {
let rx1 = tracker.register(client1, json!(i));
let rx2 = tracker.register(client2, json!(i));
receivers1.push((i, rx1));
receivers2.push((i, rx2));
}
assert_eq!(tracker.pending_count(), 6);
assert_eq!(tracker.client_pending_count(client1), 3);
assert_eq!(tracker.client_pending_count(client2), 3);
// Complete client1's requests
for i in 0..3 {
let response = json!({"client": 1, "request": i});
tracker.complete(client1, json!(i), response).unwrap();
}
assert_eq!(tracker.client_pending_count(client1), 0);
assert_eq!(tracker.client_pending_count(client2), 3);
// Complete client2's requests
for i in 0..3 {
let response = json!({"client": 2, "request": i});
tracker.complete(client2, json!(i), response).unwrap();
}
assert_eq!(tracker.pending_count(), 0);
// Verify each receiver got the correct response
for (i, rx) in receivers1 {
let response = rx.await.unwrap();
assert_eq!(response["client"], 1);
assert_eq!(response["request"], i);
}
for (i, rx) in receivers2 {
let response = rx.await.unwrap();
assert_eq!(response["client"], 2);
assert_eq!(response["request"], i);
}
}
#[tokio::test]
async fn test_concurrent_requests_same_id_different_clients() {
// Test that same request_id for different clients are handled independently
let tracker = AgentRequestTracker::new();
let client1 = "client-1";
let client2 = "client-2";
let request_id = json!(0); // Same ID for both
let rx1 = tracker.register(client1, request_id.clone());
let rx2 = tracker.register(client2, request_id.clone());
assert_eq!(tracker.pending_count(), 2);
// Complete client1's request
let response1 = json!({"client": "client-1"});
tracker.complete(client1, request_id.clone(), response1.clone()).unwrap();
// Complete client2's request
let response2 = json!({"client": "client-2"});
tracker.complete(client2, request_id, response2.clone()).unwrap();
assert_eq!(tracker.pending_count(), 0);
// Verify each got the correct response
let received1 = rx1.await.unwrap();
let received2 = rx2.await.unwrap();
assert_eq!(received1["client"], "client-1");
assert_eq!(received2["client"], "client-2");
}
#[tokio::test]
async fn test_concurrent_async_completion() {
// Test completing requests from multiple async tasks concurrently
let tracker = AgentRequestTracker::new();
let client_id = "test-client";
let num_requests = 10;
// Register requests
let mut receivers = Vec::new();
for i in 0..num_requests {
let rx = tracker.register(client_id, json!(i));
receivers.push((i, rx));
}
assert_eq!(tracker.pending_count(), num_requests);
// Spawn tasks to complete requests concurrently
let mut join_set = JoinSet::new();
for i in 0..num_requests {
let tracker_clone = tracker.clone();
join_set.spawn(async move {
// Small delay to ensure concurrency
tokio::time::sleep(tokio::time::Duration::from_millis(((i % 3) * 10) as u64)).await;
let response = json!({"request": i, "result": "success"});
tracker_clone.complete(client_id, json!(i), response)
});
}
// Wait for all completions
while let Some(result) = join_set.join_next().await {
assert!(result.unwrap().is_ok());
}
assert_eq!(tracker.pending_count(), 0);
// Verify all receivers got correct responses
for (i, rx) in receivers {
let response = rx.await.unwrap();
assert_eq!(response["request"], i);
}
}
#[tokio::test]
async fn test_concurrent_register_and_complete() {
// Test registering and completing requests concurrently
let tracker = AgentRequestTracker::new();
let client_id = "test-client";
let num_requests = 20;
let mut join_set = JoinSet::new();
// Spawn tasks to register and complete requests
for i in 0..num_requests {
let tracker_clone = tracker.clone();
join_set.spawn(async move {
// Register
let rx = tracker_clone.register(client_id, json!(i));
// Small delay
tokio::time::sleep(tokio::time::Duration::from_millis(10)).await;
// Complete
let response = json!({"request": i});
tracker_clone.complete(client_id, json!(i), response.clone()).unwrap();
// Wait for response
let received = rx.await.unwrap();
assert_eq!(received["request"], i);
i
});
}
// Wait for all tasks
let mut completed = Vec::new();
while let Some(result) = join_set.join_next().await {
completed.push(result.unwrap());
}
// All requests should have completed
assert_eq!(completed.len(), num_requests);
assert_eq!(tracker.pending_count(), 0);
}
#[tokio::test]
async fn test_concurrent_mixed_operations() {
// Test mix of register, complete, and timeout operations
let tracker = AgentRequestTracker::new();
let client_id = "test-client";
let mut join_set = JoinSet::new();
// Spawn 15 tasks with different behaviors
for i in 0..15 {
let tracker_clone = tracker.clone();
join_set.spawn(async move {
let rx = tracker_clone.register(client_id, json!(i));
tokio::time::sleep(tokio::time::Duration::from_millis(10)).await;
match i % 3 {
0 => {
// Complete normally
let response = json!({"request": i, "type": "complete"});
tracker_clone.complete(client_id, json!(i), response.clone()).unwrap();
let received = rx.await.unwrap();
assert_eq!(received["type"], "complete");
"completed"
}
1 => {
// Timeout
tracker_clone.timeout(client_id, json!(i));
assert!(rx.await.is_err());
"timeout"
}
_ => {
// Complete with delay
tokio::time::sleep(tokio::time::Duration::from_millis(20)).await;
let response = json!({"request": i, "type": "delayed"});
tracker_clone.complete(client_id, json!(i), response.clone()).unwrap();
let received = rx.await.unwrap();
assert_eq!(received["type"], "delayed");
"delayed"
}
}
});
}
// Wait for all tasks
let mut results = Vec::new();
while let Some(result) = join_set.join_next().await {
results.push(result.unwrap());
}
assert_eq!(results.len(), 15);
// Count outcomes
let completed = results.iter().filter(|&r| r == &"completed").count();
let timeout = results.iter().filter(|&r| r == &"timeout").count();
let delayed = results.iter().filter(|&r| r == &"delayed").count();
assert_eq!(completed, 5);
assert_eq!(timeout, 5);
assert_eq!(delayed, 5);
// All should be cleaned up
assert_eq!(tracker.pending_count(), 0);
}
#[tokio::test]
async fn test_high_concurrency() {
// Stress test with many concurrent requests
let tracker = AgentRequestTracker::new();
let client_id = "test-client";
let num_requests = 100;
let mut join_set = JoinSet::new();
for i in 0..num_requests {
let tracker_clone = tracker.clone();
join_set.spawn(async move {
let rx = tracker_clone.register(client_id, json!(i));
// Random-ish delay
let delay = ((i * 7) % 20) as u64;
tokio::time::sleep(tokio::time::Duration::from_millis(delay)).await;
let response = json!({"request": i});
tracker_clone.complete(client_id, json!(i), response).unwrap();
rx.await.unwrap()["request"].as_u64().unwrap()
});
}
// Collect all results
let mut results = Vec::new();
while let Some(result) = join_set.join_next().await {
results.push(result.unwrap());
}
// Verify all requests completed
assert_eq!(results.len(), num_requests);
// Verify all request IDs are present
results.sort();
for (idx, &val) in results.iter().enumerate() {
assert_eq!(val, idx as u64);
}
// All cleaned up
assert_eq!(tracker.pending_count(), 0);
}