//! Service Health Smoke Tests //! //! Tests to validate that gRPC services are running and responsive. //! Uses gRPC health check protocol to verify service availability. use super::common::*; #[tokio::test] async fn test_trading_service_health() { let result = with_timeout(async { let url = trading_service_url(); println!("Testing Trading Service at {}", url); // Try HTTP health endpoint first (faster) let client = reqwest::Client::new(); let http_health_url = url.replace("50052", "8080").replace("http://", "http://"); match client.get(format!("{}/health", http_health_url)).send().await { Ok(response) if response.status().is_success() => { println!("✅ Trading Service HTTP health check passed"); return Ok(()); } _ => { println!(" HTTP health check not available, trying gRPC..."); } } // Fallback to gRPC connection test // Note: We can't use tonic_health without the proto definitions, // so we'll just test if we can connect match tonic::transport::Channel::from_shared(url.clone()) { Ok(endpoint) => { match endpoint.connect().await { Ok(_channel) => { println!("✅ Trading Service gRPC connection successful"); Ok(()) } Err(e) => Err(Box::new(e) as Box), } } Err(e) => Err(Box::new(e) as Box), } }).await; skip_if_unavailable!("Trading Service", { result }); } #[tokio::test] async fn test_api_health() { let result = with_timeout(async { let url = api_url(); println!("Testing API Gateway at {}", url); // Try gRPC connection match tonic::transport::Channel::from_shared(url.clone()) { Ok(endpoint) => { match endpoint.connect().await { Ok(_channel) => { println!("✅ API Gateway gRPC connection successful"); Ok(()) } Err(e) => Err(Box::new(e) as Box), } } Err(e) => Err(Box::new(e) as Box), } }).await; skip_if_unavailable!("API", { result }); } #[tokio::test] #[ignore = "Backtesting Service may not be available due to configuration issues"] async fn test_backtesting_service_health() { let result = with_timeout(async { let url = backtesting_service_url(); println!("Testing Backtesting Service at {}", url); match tonic::transport::Channel::from_shared(url.clone()) { Ok(endpoint) => { match endpoint.connect().await { Ok(_channel) => { println!("✅ Backtesting Service gRPC connection successful"); Ok(()) } Err(e) => Err(Box::new(e) as Box), } } Err(e) => Err(Box::new(e) as Box), } }).await; skip_if_unavailable!("Backtesting Service", { result }); } #[tokio::test] #[ignore = "ML Training Service may not be available due to configuration issues"] async fn test_ml_training_service_health() { let result = with_timeout(async { let url = ml_training_service_url(); println!("Testing ML Training Service at {}", url); match tonic::transport::Channel::from_shared(url.clone()) { Ok(endpoint) => { match endpoint.connect().await { Ok(_channel) => { println!("✅ ML Training Service gRPC connection successful"); Ok(()) } Err(e) => Err(Box::new(e) as Box), } } Err(e) => Err(Box::new(e) as Box), } }).await; skip_if_unavailable!("ML Training Service", { result }); } #[tokio::test] async fn test_service_ports_listening() { println!("🔍 Checking service ports..."); let ports_to_check = vec![ (50051, "API"), // Primary entry point (50052, "Trading Service"), // Backend service (50053, "Backtesting Service"), (50054, "ML Training Service"), ]; let mut listening = 0; let mut not_listening = 0; for (port, service) in ports_to_check { match std::net::TcpStream::connect(format!("localhost:{}", port)) { Ok(_) => { println!(" ✓ Port {} ({}) is listening", port, service); listening += 1; } Err(_) => { println!(" ✗ Port {} ({}) is not listening", port, service); not_listening += 1; } } } println!("\n📊 Service Port Summary:"); println!(" Listening: {}", listening); println!(" Not listening: {}", not_listening); // Require at least Trading Service to be up assert!(listening >= 1, "No services are listening"); println!("✅ Service port check complete"); } #[tokio::test] async fn test_service_response_times() { println!("⏱️ Measuring service response times..."); let services = vec![ (trading_service_url(), "Trading Service"), (api_url(), "API"), ]; for (url, name) in services { let start = std::time::Instant::now(); match tonic::transport::Channel::from_shared(url.clone()) { Ok(endpoint) => { match endpoint.connect().await { Ok(_channel) => { let elapsed = start.elapsed(); println!(" ✓ {} connected in {:?}", name, elapsed); // Warn if connection takes too long if elapsed.as_millis() > 1000 { println!(" ⚠ Connection took longer than expected"); } } Err(_) => { println!(" ⚠ {} not available", name); } } } Err(_) => { println!(" ⚠ {} invalid URL", name); } } } println!("✅ Service response time check complete"); } #[tokio::test] async fn test_metrics_endpoints() { println!("📊 Checking metrics endpoints..."); let client = reqwest::Client::new(); let metrics_ports = vec![ (9091, "API"), (9092, "Trading Service"), (9093, "Backtesting Service"), (9094, "ML Training Service"), ]; let mut available = 0; let mut unavailable = 0; for (port, service) in metrics_ports { let url = format!("http://localhost:{}/metrics", port); match client.get(&url).send().await { Ok(response) if response.status().is_success() => { println!(" ✓ {} metrics available at port {}", service, port); available += 1; // Verify Prometheus format if let Ok(text) = response.text().await { assert!( text.contains("# HELP") || text.contains("# TYPE"), "{} metrics not in Prometheus format", service ); } } _ => { println!(" ⚠ {} metrics not available at port {}", service, port); unavailable += 1; } } } println!("\n📊 Metrics Endpoint Summary:"); println!(" Available: {}", available); println!(" Unavailable: {}", unavailable); println!("✅ Metrics endpoint check complete"); } #[tokio::test] async fn test_service_versions() { println!("🔖 Checking service versions..."); // Try to get version info from Trading Service health endpoint let client = reqwest::Client::new(); let health_url = "http://localhost:8080/health"; match client.get(health_url).send().await { Ok(response) if response.status().is_success() => { if let Ok(json) = response.json::().await { if let Some(version) = json.get("version") { println!(" ✓ Trading Service version: {}", version); } if let Some(service) = json.get("service") { println!(" ✓ Service name: {}", service); } } } _ => { println!(" ⚠ Could not retrieve version information"); } } println!("✅ Service version check complete"); }