use std::sync::Arc; use hermesllm::apis::openai::{ChatCompletionsRequest, Message, MessageContent, Role}; use hyper::header::HeaderMap; use crate::handlers::agents::pipeline::PipelineProcessor; use crate::handlers::agents::selector::{AgentSelectionError, AgentSelector}; use crate::handlers::response::ResponseHandler; use crate::router::orchestrator::OrchestratorService; /// Integration test that demonstrates the modular agent chat flow /// This test shows how the three main components work together: /// 1. AgentSelector - selects the appropriate agents based on orchestration /// 2. PipelineProcessor - executes the agent pipeline /// 3. ResponseHandler - handles response streaming #[cfg(test)] mod tests { use super::*; use common::configuration::{Agent, AgentFilterChain, Listener, ListenerType}; fn create_test_orchestrator_service() -> Arc { Arc::new(OrchestratorService::new( "http://localhost:8080".to_string(), "test-model".to_string(), "plano-orchestrator".to_string(), )) } fn create_test_message(role: Role, content: &str) -> Message { Message { role, content: Some(MessageContent::Text(content.to_string())), name: None, tool_calls: None, tool_call_id: None, } } #[tokio::test] async fn test_modular_agent_chat_flow() { // Setup services let orchestrator_service = create_test_orchestrator_service(); let agent_selector = AgentSelector::new(orchestrator_service); let mut pipeline_processor = PipelineProcessor::default(); // Create test data let agents = vec![ Agent { id: "filter-agent".to_string(), agent_type: Some("filter".to_string()), url: "http://localhost:8081".to_string(), tool: None, transport: None, }, Agent { id: "terminal-agent".to_string(), agent_type: Some("terminal".to_string()), url: "http://localhost:8082".to_string(), tool: None, transport: None, }, ]; let agent_pipeline = AgentFilterChain { id: "terminal-agent".to_string(), input_filters: Some(vec![ "filter-agent".to_string(), "terminal-agent".to_string(), ]), description: Some("Test pipeline".to_string()), default: Some(true), }; let listener = Listener { listener_type: ListenerType::Agent, name: "test-listener".to_string(), agents: Some(vec![agent_pipeline.clone()]), input_filters: None, output_filters: None, port: 8080, router: None, }; let listeners = vec![listener]; let messages = vec![create_test_message(Role::User, "Hello world!")]; // Test 1: Agent Selection let selected_listener = agent_selector.find_listener(Some("test-listener"), &listeners); assert!(selected_listener.is_ok()); let listener = selected_listener.unwrap(); assert_eq!(listener.name, "test-listener"); // Test 2: Agent Map Creation let agent_map = agent_selector.create_agent_map(&agents); assert_eq!(agent_map.len(), 2); assert!(agent_map.contains_key("filter-agent")); assert!(agent_map.contains_key("terminal-agent")); // Test 3: Pipeline Processing (empty filter chain for testing) let request = ChatCompletionsRequest { messages: messages.clone(), model: "test-model".to_string(), ..Default::default() }; // Create a pipeline with empty filter chain to avoid network calls let test_pipeline = AgentFilterChain { id: "terminal-agent".to_string(), input_filters: Some(vec![]), // Empty filter chain - no network calls needed description: None, default: None, }; let headers = HeaderMap::new(); let request_bytes = serde_json::to_vec(&request).expect("failed to serialize request"); let result = pipeline_processor .process_raw_filter_chain( &request_bytes, &test_pipeline, &agent_map, &headers, "/v1/chat/completions", ) .await; println!("Pipeline processing result: {:?}", result); assert!(result.is_ok()); let processed_bytes = result.unwrap(); // With empty filter chain, should return the original bytes unchanged let processed_request: ChatCompletionsRequest = serde_json::from_slice(&processed_bytes).expect("failed to deserialize response"); assert_eq!(processed_request.messages.len(), 1); if let Some(MessageContent::Text(content)) = &processed_request.messages[0].content { assert_eq!(content, "Hello world!"); } else { panic!("Expected text content"); } // Test 4: Error Response Creation let error_response = ResponseHandler::create_bad_request("Test error"); assert_eq!(error_response.status(), hyper::StatusCode::BAD_REQUEST); println!("✅ All modular components working correctly!"); } #[tokio::test] async fn test_error_handling_flow() { let router_service = create_test_orchestrator_service(); let agent_selector = AgentSelector::new(router_service); // Test listener not found let result = agent_selector.find_listener(Some("nonexistent"), &[]); assert!(result.is_err()); assert!(matches!( result.unwrap_err(), AgentSelectionError::ListenerNotFound(_) )); // Test error response creation let error_response = ResponseHandler::create_internal_error("Pipeline failed"); assert_eq!( error_response.status(), hyper::StatusCode::INTERNAL_SERVER_ERROR ); println!("✅ Error handling working correctly!"); } }