#![allow(dead_code, unused_imports, unused_variables, deprecated, clippy::all)]
#![allow(
clippy::unwrap_used,
clippy::expect_used,
clippy::panic,
clippy::needless_raw_string_hashes,
clippy::duration_suboptimal_units,
clippy::branches_sharing_code,
clippy::used_underscore_binding,
clippy::single_char_pattern,
clippy::ignore_without_reason,
clippy::cloned_ref_to_slice_refs,
clippy::doc_overindented_list_items,
clippy::match_wildcard_for_single_variants,
clippy::ignored_unit_patterns,
clippy::needless_collect,
clippy::unnecessary_map_or,
clippy::manual_flatten,
clippy::manual_strip,
clippy::future_not_send,
clippy::unnested_or_patterns,
clippy::no_effect_underscore_binding,
clippy::literal_string_with_formatting_args
)]
#[cfg(test)]
mod a2a_agent_lifecycle_tests {
use std::collections::HashMap;
use std::time::{Duration, SystemTime};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum AgentState {
Initializing,
Ready,
Processing,
Idle,
Error,
Terminated,
}
#[derive(Debug, Clone)]
struct AgentMessage {
from: String,
to: String,
content: String,
timestamp: SystemTime,
}
#[derive(Debug, Clone)]
struct TestAgent {
id: String,
name: String,
state: AgentState,
uptime_secs: u64,
message_queue: Vec<AgentMessage>,
error_message: Option<String>,
max_concurrent_tasks: usize,
active_tasks: usize,
created_at: SystemTime,
}
impl TestAgent {
fn new(id: impl Into<String>, name: impl Into<String>) -> Self {
Self {
id: id.into(),
name: name.into(),
state: AgentState::Initializing,
uptime_secs: 0,
message_queue: Vec::new(),
error_message: None,
max_concurrent_tasks: 10,
active_tasks: 0,
created_at: SystemTime::now(),
}
}
fn initialize(&mut self) -> Result<(), String> {
match self.state {
AgentState::Initializing => {
self.state = AgentState::Ready;
Ok(())
}
_ => Err(format!("Cannot initialize agent in {:?} state", self.state)),
}
}
fn start_processing(&mut self, _task_id: &str) -> Result<(), String> {
if self.state != AgentState::Ready && self.state != AgentState::Idle {
return Err(format!(
"Agent must be Ready or Idle to start processing, currently {:?}",
self.state
));
}
if self.active_tasks >= self.max_concurrent_tasks {
return Err(format!(
"Agent at task limit: {}/{}",
self.active_tasks, self.max_concurrent_tasks
));
}
self.state = AgentState::Processing;
self.active_tasks += 1;
Ok(())
}
fn complete_task(&mut self, _task_id: &str) -> Result<(), String> {
if self.state != AgentState::Processing {
return Err(format!(
"Cannot complete task when in {:?} state",
self.state
));
}
if self.active_tasks > 0 {
self.active_tasks -= 1;
}
if self.active_tasks == 0 {
self.state = AgentState::Idle;
}
Ok(())
}
fn set_error(&mut self, error: impl Into<String>) {
self.state = AgentState::Error;
self.error_message = Some(error.into());
}
fn recover_from_error(&mut self) -> Result<(), String> {
if self.state != AgentState::Error {
return Err(format!(
"Cannot recover from error when in {:?} state",
self.state
));
}
self.state = AgentState::Idle;
self.error_message = None;
self.active_tasks = 0;
Ok(())
}
fn terminate(&mut self) -> Result<(), String> {
if self.state == AgentState::Terminated {
return Err("Agent already terminated".to_string());
}
self.state = AgentState::Terminated;
self.active_tasks = 0;
self.message_queue.clear();
Ok(())
}
fn enqueue_message(&mut self, message: AgentMessage) {
self.message_queue.push(message);
}
fn dequeue_message(&mut self) -> Option<AgentMessage> {
if self.message_queue.is_empty() {
None
} else {
Some(self.message_queue.remove(0))
}
}
fn update_uptime(&mut self) {
if let Ok(duration) = self.created_at.elapsed() {
self.uptime_secs = duration.as_secs();
}
}
fn get_status(&self) -> String {
format!(
"Agent(id={}, name={}, state={:?}, uptime={}s, active_tasks={}/{})",
self.id,
self.name,
self.state,
self.uptime_secs,
self.active_tasks,
self.max_concurrent_tasks
)
}
}
#[derive(Debug, Clone)]
struct AgentPool {
agents: HashMap<String, TestAgent>,
max_agents: usize,
}
impl AgentPool {
fn new(max_agents: usize) -> Self {
Self {
agents: HashMap::new(),
max_agents,
}
}
fn register_agent(&mut self, agent: TestAgent) -> Result<(), String> {
if self.agents.len() >= self.max_agents {
return Err(format!(
"Agent pool at capacity: {}/{}",
self.agents.len(),
self.max_agents
));
}
if self.agents.contains_key(&agent.id) {
return Err(format!("Agent with ID {} already exists", agent.id));
}
self.agents.insert(agent.id.clone(), agent);
Ok(())
}
fn get_agent(&self, agent_id: &str) -> Option<&TestAgent> {
self.agents.get(agent_id)
}
fn get_agent_mut(&mut self, agent_id: &str) -> Option<&mut TestAgent> {
self.agents.get_mut(agent_id)
}
fn remove_agent(&mut self, agent_id: &str) -> Result<TestAgent, String> {
self.agents
.remove(agent_id)
.ok_or_else(|| format!("Agent {} not found", agent_id))
}
fn get_ready_agents(&self) -> Vec<&TestAgent> {
self.agents
.values()
.filter(|a| a.state == AgentState::Ready)
.collect()
}
fn get_processing_agents(&self) -> Vec<&TestAgent> {
self.agents
.values()
.filter(|a| a.state == AgentState::Processing)
.collect()
}
fn get_error_agents(&self) -> Vec<&TestAgent> {
self.agents
.values()
.filter(|a| a.state == AgentState::Error)
.collect()
}
fn count_by_state(&self, state: AgentState) -> usize {
self.agents.values().filter(|a| a.state == state).count()
}
fn total_active_tasks(&self) -> usize {
self.agents.values().map(|a| a.active_tasks).sum()
}
}
#[tokio::test]
async fn test_agent_initialization_transition() {
let mut agent = TestAgent::new("agent-001", "test-agent");
assert_eq!(agent.state, AgentState::Initializing);
let result = agent.initialize();
assert!(result.is_ok(), "Initialization should succeed");
assert_eq!(
agent.state,
AgentState::Ready,
"Agent should transition to Ready"
);
}
#[tokio::test]
async fn test_agent_double_initialization_error() {
let mut agent = TestAgent::new("agent-002", "test-agent");
agent.initialize().unwrap();
assert_eq!(agent.state, AgentState::Ready);
let result = agent.initialize();
assert!(result.is_err(), "Double initialization should fail");
assert_eq!(
agent.state,
AgentState::Ready,
"State should not change on error"
);
}
#[tokio::test]
async fn test_agent_readiness_validation() {
let mut pool = AgentPool::new(10);
let agent1 = TestAgent::new("agent-003", "test-agent-1");
let agent2 = TestAgent::new("agent-004", "test-agent-2");
let agent3 = TestAgent::new("agent-005", "test-agent-3");
pool.register_agent(agent1).unwrap();
pool.register_agent(agent2).unwrap();
pool.register_agent(agent3).unwrap();
pool.get_agent_mut("agent-003")
.unwrap()
.initialize()
.unwrap();
assert_eq!(
pool.count_by_state(AgentState::Initializing),
2,
"Two agents should still be initializing"
);
assert_eq!(
pool.count_by_state(AgentState::Ready),
1,
"One agent should be ready"
);
}
#[tokio::test]
async fn test_ready_to_processing_transition() {
let mut agent = TestAgent::new("agent-006", "test-agent");
agent.initialize().unwrap();
assert_eq!(agent.state, AgentState::Ready);
let result = agent.start_processing("task-001");
assert!(result.is_ok(), "Should transition to Processing");
assert_eq!(
agent.state,
AgentState::Processing,
"State should be Processing"
);
assert_eq!(agent.active_tasks, 1, "Should have one active task");
}
#[tokio::test]
async fn test_processing_to_idle_transition() {
let mut agent = TestAgent::new("agent-007", "test-agent");
agent.initialize().unwrap();
agent.start_processing("task-001").unwrap();
assert_eq!(agent.state, AgentState::Processing);
assert_eq!(agent.active_tasks, 1);
let result = agent.complete_task("task-001");
assert!(result.is_ok(), "Should complete task successfully");
assert_eq!(agent.state, AgentState::Idle, "Should transition to Idle");
assert_eq!(agent.active_tasks, 0, "Should have zero active tasks");
}
#[tokio::test]
async fn test_full_state_cycle() {
let mut agent = TestAgent::new("agent-008", "test-agent");
assert_eq!(agent.state, AgentState::Initializing);
agent.initialize().unwrap();
assert_eq!(agent.state, AgentState::Ready);
agent.start_processing("task-001").unwrap();
assert_eq!(agent.state, AgentState::Processing);
agent.complete_task("task-001").unwrap();
assert_eq!(agent.state, AgentState::Idle);
agent.terminate().unwrap();
assert_eq!(agent.state, AgentState::Terminated);
}
#[tokio::test]
async fn test_error_state_transition() {
let mut agent = TestAgent::new("agent-009", "test-agent");
agent.initialize().unwrap();
assert_eq!(agent.state, AgentState::Ready);
assert_eq!(agent.error_message, None);
agent.set_error("Processing failed");
assert_eq!(agent.state, AgentState::Error);
assert_eq!(
agent.error_message,
Some("Processing failed".to_string()),
"Error message should be stored"
);
}
#[tokio::test]
async fn test_error_recovery_transition() {
let mut agent = TestAgent::new("agent-010", "test-agent");
agent.initialize().unwrap();
agent.set_error("Something failed");
assert_eq!(agent.state, AgentState::Error);
let result = agent.recover_from_error();
assert!(result.is_ok(), "Should recover successfully");
assert_eq!(agent.state, AgentState::Idle, "Should transition to Idle");
assert_eq!(agent.error_message, None, "Error message should be cleared");
assert_eq!(agent.active_tasks, 0, "Active tasks should be reset");
}
#[tokio::test]
async fn test_invalid_state_transitions() {
let mut agent = TestAgent::new("agent-011", "test-agent");
let err1 = agent.start_processing("task-001");
assert!(err1.is_err(), "Cannot process from Initializing state");
agent.initialize().unwrap();
agent.start_processing("task-001").unwrap();
let err2 = agent.start_processing("task-002");
assert!(
err2.is_err(),
"Cannot start new processing while already processing"
);
agent.complete_task("task-001").unwrap();
let err3 = agent.complete_task("task-001");
assert!(err3.is_err(), "Cannot complete from non-Processing state");
}
#[tokio::test]
async fn test_processing_timeout_detection() {
let mut agent = TestAgent::new("agent-012", "test-agent");
agent.initialize().unwrap();
agent.start_processing("long-task").unwrap();
let start = SystemTime::now();
let max_duration = Duration::from_millis(100);
let exceeded = start.elapsed().unwrap_or_default() > max_duration;
if !exceeded {
assert_eq!(
agent.state,
AgentState::Processing,
"Should still be processing"
);
}
}
#[tokio::test]
async fn test_timeout_causes_error_state() {
let mut agent = TestAgent::new("agent-013", "test-agent");
agent.initialize().unwrap();
agent.start_processing("timeout-task").unwrap();
agent.set_error("Task timeout - exceeded 30s limit");
assert_eq!(agent.state, AgentState::Error);
assert_eq!(
agent.error_message,
Some("Task timeout - exceeded 30s limit".to_string())
);
assert_eq!(
agent.active_tasks, 1,
"Task should still be counted until recovered"
);
}
#[tokio::test]
async fn test_concurrent_task_timeout_isolation() {
let mut agent = TestAgent::new("agent-014", "test-agent");
agent.initialize().unwrap();
agent.start_processing("task-1").unwrap();
assert_eq!(agent.active_tasks, 1);
agent.set_error("Task timeout");
assert_eq!(agent.state, AgentState::Error);
}
#[tokio::test]
async fn test_concurrent_agents_independent_states() {
let mut pool = AgentPool::new(10);
let mut agents = vec![];
for i in 0..5 {
let agent = TestAgent::new(format!("agent-{:03}", i + 100), format!("agent-{}", i));
agents.push(agent);
}
for agent in agents {
pool.register_agent(agent).unwrap();
}
for i in 0..3 {
let key = format!("agent-{:03}", i + 100);
pool.get_agent_mut(&key).unwrap().initialize().unwrap();
}
assert_eq!(pool.count_by_state(AgentState::Ready), 3);
assert_eq!(pool.count_by_state(AgentState::Initializing), 2);
}
#[tokio::test]
async fn test_concurrent_agent_task_processing() {
let mut pool = AgentPool::new(10);
for i in 0..4 {
let mut agent = TestAgent::new(format!("agent-{:03}", i + 200), format!("agent-{}", i));
agent.initialize().unwrap();
pool.register_agent(agent).unwrap();
}
for i in 0..4 {
let key = format!("agent-{:03}", i + 200);
pool.get_agent_mut(&key)
.unwrap()
.start_processing(&format!("task-{}", i))
.unwrap();
}
assert_eq!(
pool.count_by_state(AgentState::Processing),
4,
"All agents should be processing"
);
assert_eq!(
pool.total_active_tasks(),
4,
"Should have 4 active tasks total"
);
}
#[tokio::test]
async fn test_concurrent_agent_completion() {
let mut pool = AgentPool::new(10);
for i in 0..3 {
let mut agent = TestAgent::new(format!("agent-{:03}", i + 300), format!("agent-{}", i));
agent.initialize().unwrap();
agent.start_processing(&format!("task-{}", i)).unwrap();
pool.register_agent(agent).unwrap();
}
assert_eq!(pool.count_by_state(AgentState::Processing), 3);
for i in 0..3 {
let key = format!("agent-{:03}", i + 300);
pool.get_agent_mut(&key)
.unwrap()
.complete_task(&format!("task-{}", i))
.unwrap();
}
assert_eq!(
pool.count_by_state(AgentState::Idle),
3,
"All agents should be idle"
);
assert_eq!(
pool.total_active_tasks(),
0,
"Should have zero active tasks"
);
}
#[tokio::test]
async fn test_agent_message_enqueue_dequeue() {
let mut agent = TestAgent::new("agent-400", "test-agent");
let message = AgentMessage {
from: "agent-001".to_string(),
to: "agent-400".to_string(),
content: "Hello from agent 1".to_string(),
timestamp: SystemTime::now(),
};
agent.enqueue_message(message.clone());
assert_eq!(agent.message_queue.len(), 1);
let dequeued = agent.dequeue_message();
assert!(dequeued.is_some(), "Message should be dequeued");
let msg = dequeued.unwrap();
assert_eq!(msg.from, "agent-001");
assert_eq!(msg.to, "agent-400");
assert_eq!(msg.content, "Hello from agent 1");
assert_eq!(agent.message_queue.len(), 0);
}
#[tokio::test]
async fn test_agent_message_fifo_ordering() {
let mut agent = TestAgent::new("agent-401", "test-agent");
for i in 1..=5 {
let message = AgentMessage {
from: format!("agent-{}", i),
to: "agent-401".to_string(),
content: format!("Message {}", i),
timestamp: SystemTime::now(),
};
agent.enqueue_message(message);
}
assert_eq!(agent.message_queue.len(), 5);
let mut dequeued_order = vec![];
while let Some(msg) = agent.dequeue_message() {
dequeued_order.push(msg.content);
}
assert_eq!(dequeued_order.len(), 5);
for (i, content) in dequeued_order.iter().enumerate() {
assert_eq!(content, &format!("Message {}", i + 1));
}
}
#[tokio::test]
async fn test_agent_message_routing_to_multiple_recipients() {
let mut pool = AgentPool::new(10);
for i in 0..3 {
let agent = TestAgent::new(format!("agent-{:03}", i + 500), format!("agent-{}", i));
pool.register_agent(agent).unwrap();
}
let message = AgentMessage {
from: "controller".to_string(),
to: "broadcast".to_string(),
content: "System message".to_string(),
timestamp: SystemTime::now(),
};
for i in 0..3 {
let key = format!("agent-{:03}", i + 500);
if let Some(agent) = pool.get_agent_mut(&key) {
agent.enqueue_message(message.clone());
}
}
let total_messages: usize = pool.agents.values().map(|a| a.message_queue.len()).sum();
assert_eq!(total_messages, 3, "All agents should have received message");
}
#[tokio::test]
async fn test_message_in_error_state() {
let mut agent = TestAgent::new("agent-502", "test-agent");
agent.initialize().unwrap();
agent.set_error("Processing failed");
let message = AgentMessage {
from: "recovery-service".to_string(),
to: "agent-502".to_string(),
content: "Attempting recovery".to_string(),
timestamp: SystemTime::now(),
};
agent.enqueue_message(message);
assert_eq!(
agent.message_queue.len(),
1,
"Should accept messages in error state"
);
assert_eq!(agent.state, AgentState::Error, "State should not change");
}
#[tokio::test]
async fn test_agent_bridging_as_tool() {
let mut agent = TestAgent::new("executor-600", "execution-engine");
agent.initialize().unwrap();
let tool_name = format!("agent-{}", agent.name);
let bridge_mapping: HashMap<String, String> =
HashMap::from_iter(vec![(tool_name.clone(), agent.id.clone())]);
assert!(
bridge_mapping.contains_key(&tool_name),
"Tool should be bridged"
);
assert_eq!(
bridge_mapping.get(&tool_name).unwrap(),
&agent.id,
"Mapping should point to agent ID"
);
}
#[tokio::test]
async fn test_bridged_agent_task_execution() {
let mut agent = TestAgent::new("executor-601", "task-handler");
agent.initialize().unwrap();
let bridge_name = "handler-tool";
let mut bridge_state: HashMap<String, String> = HashMap::new();
bridge_state.insert(bridge_name.to_string(), agent.id.clone());
agent.start_processing("task-001").unwrap();
assert_eq!(agent.state, AgentState::Processing);
assert_eq!(agent.active_tasks, 1);
}
#[tokio::test]
async fn test_bridged_agent_error_handling() {
let mut agent = TestAgent::new("executor-602", "error-handler");
agent.initialize().unwrap();
agent.start_processing("problematic-task").unwrap();
agent.set_error("Task execution failed: timeout");
assert_eq!(agent.state, AgentState::Error);
assert!(
agent.error_message.is_some(),
"Error message should be recorded"
);
}
#[tokio::test]
async fn test_single_agent_recovery() {
let mut agent = TestAgent::new("recovery-700", "test-agent");
agent.initialize().unwrap();
agent.start_processing("task-001").unwrap();
agent.set_error("Processing failed");
assert_eq!(agent.state, AgentState::Error);
let result = agent.recover_from_error();
assert!(result.is_ok(), "Recovery should succeed");
assert_eq!(agent.state, AgentState::Idle);
assert_eq!(agent.error_message, None);
assert_eq!(agent.active_tasks, 0);
}
#[tokio::test]
async fn test_recovery_invalid_state() {
let mut agent = TestAgent::new("recovery-701", "test-agent");
agent.initialize().unwrap();
let result = agent.recover_from_error();
assert!(result.is_err(), "Should not recover from non-error state");
assert_eq!(agent.state, AgentState::Ready, "State should not change");
}
#[tokio::test]
async fn test_pool_recovery_multiple_agents() {
let mut pool = AgentPool::new(10);
let mut agents = vec![];
for i in 0..5 {
let mut agent = TestAgent::new(format!("agent-{:03}", i + 800), format!("agent-{}", i));
agent.initialize().unwrap();
if i % 2 == 0 {
agent.start_processing(&format!("task-{}", i)).unwrap();
agent.set_error("Simulated failure");
}
agents.push(agent);
}
for agent in agents {
pool.register_agent(agent).unwrap();
}
let error_ids: Vec<String> = pool
.get_error_agents()
.iter()
.map(|a| a.id.clone())
.collect();
let initial_error_count = error_ids.len();
assert!(initial_error_count > 0, "Should have some error agents");
for agent_id in error_ids {
if let Some(mut_agent) = pool.get_agent_mut(&agent_id) {
let _ = mut_agent.recover_from_error();
}
}
assert_eq!(
pool.count_by_state(AgentState::Error),
0,
"All error agents should be recovered"
);
assert_eq!(
pool.count_by_state(AgentState::Idle),
initial_error_count,
"All recovered agents should be idle"
);
assert_eq!(
pool.count_by_state(AgentState::Ready),
5 - initial_error_count,
"Non-error agents should remain ready"
);
}
#[tokio::test]
async fn test_agent_pool_capacity_limit() {
let mut pool = AgentPool::new(5);
for i in 0..5 {
let agent = TestAgent::new(format!("agent-{:03}", i + 900), format!("agent-{}", i));
let result = pool.register_agent(agent);
assert!(result.is_ok(), "Should register agent {}", i);
}
let overflow_agent = TestAgent::new("agent-999-overflow", "overflow-agent");
let result = pool.register_agent(overflow_agent);
assert!(result.is_err(), "Should reject agent beyond capacity");
assert_eq!(pool.agents.len(), 5, "Pool should remain at capacity");
}
#[tokio::test]
async fn test_agent_task_concurrency_limit() {
let mut agent = TestAgent::new("agent-1000", "limited-agent");
agent.max_concurrent_tasks = 5;
agent.initialize().unwrap();
let mut _count = 0;
for i in 0..10 {
if agent.active_tasks < agent.max_concurrent_tasks {
let result = agent.start_processing(&format!("task-{}", i));
if result.is_ok() {
_count += 1;
agent.active_tasks -= 1; }
}
}
assert_eq!(agent.active_tasks, 0, "Task counter should be reset");
}
#[tokio::test]
async fn test_concurrent_agent_limit_enforcement() {
let mut pool = AgentPool::new(10);
for i in 0..10 {
let mut agent = TestAgent::new(
format!("agent-{:04}", i + 1010),
format!("limited-agent-{}", i),
);
agent.initialize().unwrap();
pool.register_agent(agent).unwrap();
}
for agent in pool.agents.values_mut() {
let _ = agent.start_processing("task-id");
}
assert_eq!(pool.count_by_state(AgentState::Processing), 10);
}
#[tokio::test]
async fn test_agent_termination_cleanup() {
let mut agent = TestAgent::new("agent-1100", "cleanup-test");
agent.initialize().unwrap();
let message = AgentMessage {
from: "other-agent".to_string(),
to: "agent-1100".to_string(),
content: "Message to clean up".to_string(),
timestamp: SystemTime::now(),
};
agent.enqueue_message(message);
assert_eq!(agent.message_queue.len(), 1);
assert_eq!(agent.state, AgentState::Ready);
let result = agent.terminate();
assert!(result.is_ok(), "Termination should succeed");
assert_eq!(agent.state, AgentState::Terminated);
assert_eq!(agent.message_queue.len(), 0, "Messages should be cleared");
assert_eq!(agent.active_tasks, 0, "Active tasks should be reset");
}
#[tokio::test]
async fn test_terminate_processing_agent() {
let mut agent = TestAgent::new("agent-1101", "cleanup-test");
agent.initialize().unwrap();
agent.start_processing("active-task").unwrap();
assert_eq!(agent.state, AgentState::Processing);
assert_eq!(agent.active_tasks, 1);
let result = agent.terminate();
assert!(result.is_ok(), "Should terminate even while processing");
assert_eq!(agent.state, AgentState::Terminated);
assert_eq!(agent.active_tasks, 0, "Tasks should be cleared");
}
#[tokio::test]
async fn test_terminate_error_agent() {
let mut agent = TestAgent::new("agent-1102", "cleanup-test");
agent.initialize().unwrap();
agent.set_error("Processing failed");
assert_eq!(agent.state, AgentState::Error);
let result = agent.terminate();
assert!(result.is_ok(), "Should terminate error agent");
assert_eq!(agent.state, AgentState::Terminated);
}
#[tokio::test]
async fn test_double_termination_error() {
let mut agent = TestAgent::new("agent-1103", "cleanup-test");
agent.initialize().unwrap();
agent.terminate().unwrap();
assert_eq!(agent.state, AgentState::Terminated);
let result = agent.terminate();
assert!(result.is_err(), "Double termination should fail");
}
#[tokio::test]
async fn test_pool_termination_cleanup() {
let mut pool = AgentPool::new(10);
for i in 0..5 {
let mut agent = TestAgent::new(
format!("agent-{:03}", i + 1200),
format!("cleanup-agent-{}", i),
);
agent.initialize().unwrap();
pool.register_agent(agent).unwrap();
}
let agent_ids: Vec<_> = pool.agents.keys().cloned().collect();
for agent_id in agent_ids {
if let Some(agent) = pool.get_agent_mut(&agent_id) {
let _ = agent.terminate();
}
}
assert_eq!(pool.count_by_state(AgentState::Terminated), 5);
assert_eq!(
pool.count_by_state(AgentState::Ready),
0,
"No agents should be ready"
);
}
#[tokio::test]
async fn test_agent_status_reporting() {
let agent = TestAgent::new("agent-1300", "status-test");
let initial_status = agent.get_status();
assert!(initial_status.contains("agent-1300"));
assert!(initial_status.contains("status-test"));
assert!(initial_status.contains("Initializing"));
assert!(initial_status.contains("uptime=0s"));
}
#[tokio::test]
async fn test_agent_uptime_tracking() {
let agent = TestAgent::new("agent-1301", "uptime-test");
let created = agent.created_at;
assert_eq!(agent.uptime_secs, 0);
let mut agent = agent.clone();
agent.update_uptime();
let elapsed = created.elapsed().unwrap_or_default().as_secs();
assert!(
agent.uptime_secs <= elapsed + 1,
"Uptime should not exceed actual elapsed"
);
}
#[tokio::test]
async fn test_pool_status_summary() {
let mut pool = AgentPool::new(10);
let mut ready_agent = TestAgent::new("pool-1400", "ready");
ready_agent.initialize().unwrap();
pool.register_agent(ready_agent).unwrap();
let mut processing_agent = TestAgent::new("pool-1401", "processing");
processing_agent.initialize().unwrap();
processing_agent.start_processing("task").unwrap();
pool.register_agent(processing_agent).unwrap();
let mut error_agent = TestAgent::new("pool-1402", "error");
error_agent.initialize().unwrap();
error_agent.set_error("Test error");
pool.register_agent(error_agent).unwrap();
let initializing_agent = TestAgent::new("pool-1403", "initializing");
pool.register_agent(initializing_agent).unwrap();
assert_eq!(pool.count_by_state(AgentState::Ready), 1);
assert_eq!(pool.count_by_state(AgentState::Processing), 1);
assert_eq!(pool.count_by_state(AgentState::Error), 1);
assert_eq!(pool.count_by_state(AgentState::Initializing), 1);
let total_tasks = pool.total_active_tasks();
assert_eq!(total_tasks, 1, "Should have 1 active task");
}
#[tokio::test]
async fn test_agent_list_by_state() {
let mut pool = AgentPool::new(10);
for i in 0..3 {
let mut agent = TestAgent::new(format!("ready-{}", i), format!("ready-agent-{}", i));
agent.initialize().unwrap();
pool.register_agent(agent).unwrap();
}
for i in 0..2 {
let mut agent =
TestAgent::new(format!("proc-{}", i), format!("processing-agent-{}", i));
agent.initialize().unwrap();
agent.start_processing("task").unwrap();
pool.register_agent(agent).unwrap();
}
let ready = pool.get_ready_agents();
assert_eq!(ready.len(), 3, "Should have 3 ready agents");
let processing = pool.get_processing_agents();
assert_eq!(processing.len(), 2, "Should have 2 processing agents");
let error = pool.get_error_agents();
assert_eq!(error.len(), 0, "Should have 0 error agents");
}
#[tokio::test]
async fn test_agent_list_with_error_recovery() {
let mut pool = AgentPool::new(10);
for i in 0..4 {
let mut agent = TestAgent::new(format!("test-{}", i), format!("test-agent-{}", i));
agent.initialize().unwrap();
if i % 2 == 0 {
agent.set_error("Simulated error");
}
pool.register_agent(agent).unwrap();
}
assert_eq!(pool.count_by_state(AgentState::Error), 2);
let error_ids: Vec<String> = pool
.get_error_agents()
.iter()
.map(|a| a.id.clone())
.collect();
for agent_id in error_ids {
if let Some(agent) = pool.get_agent_mut(&agent_id) {
let _ = agent.recover_from_error();
}
}
assert_eq!(pool.count_by_state(AgentState::Error), 0);
assert_eq!(pool.count_by_state(AgentState::Idle), 2);
assert_eq!(pool.count_by_state(AgentState::Ready), 2);
}
}