//! Integration tests for API Gateway metrics system //! //! Verifies that all metrics are correctly registered and recorded use api::metrics::{AuthMetrics, ConfigMetrics, GatewayMetrics, ProxyMetrics}; use prometheus::Registry; use std::sync::Arc; #[test] fn test_gateway_metrics_initialization() { let metrics = GatewayMetrics::new().expect("Failed to create metrics"); // Verify all subsystems initialized assert!(Arc::strong_count(&metrics.auth) >= 1); assert!(Arc::strong_count(&metrics.proxy) >= 1); assert!(Arc::strong_count(&metrics.config) >= 1); assert!(Arc::strong_count(&metrics.registry) >= 1); } #[test] fn test_auth_metrics_registration() { let registry = Registry::new(); let auth_metrics = AuthMetrics::new(®istry).expect("Failed to create auth metrics"); // Record some events auth_metrics.auth_requests_total.inc(); auth_metrics.record_success(5.5); auth_metrics.record_failure("expired_jwt", Some("test_user")); auth_metrics.record_user_request("test_user"); auth_metrics.record_rate_limit("rate_limited_user"); // Verify metrics can be gathered let metrics = registry.gather(); assert!(!metrics.is_empty(), "No metrics gathered"); // Check specific metrics exist let metric_names: Vec = metrics.iter().map(|m| m.name().to_string()).collect(); assert!( metric_names.contains(&"api_auth_requests_total".to_string()), "Missing auth_requests_total metric" ); assert!( metric_names.contains(&"api_auth_requests_success".to_string()), "Missing auth_requests_success metric" ); assert!( metric_names.contains(&"api_auth_sla_met".to_string()), "Missing auth_sla_met metric" ); } #[test] fn test_proxy_metrics_registration() { let registry = Registry::new(); let proxy_metrics = ProxyMetrics::new(®istry).expect("Failed to create proxy metrics"); // Record backend events proxy_metrics.record_backend_success("trading", "ExecuteTrade", 15.5); proxy_metrics.record_backend_failure("backtesting", "timeout"); proxy_metrics.update_circuit_breaker_state("trading", 0); // closed proxy_metrics.update_health_status("trading", true); proxy_metrics.update_connection_pool("trading", 10, 5, 50); // Verify metrics can be gathered let metrics = registry.gather(); assert!(!metrics.is_empty(), "No metrics gathered"); let metric_names: Vec = metrics.iter().map(|m| m.name().to_string()).collect(); assert!( metric_names.contains(&"api_backend_requests_total".to_string()), "Missing backend_requests_total metric" ); assert!( metric_names.contains(&"api_circuit_breaker_state".to_string()), "Missing circuit_breaker_state metric" ); assert!( metric_names.contains(&"api_health_status".to_string()), "Missing health_status metric" ); } #[test] fn test_config_metrics_registration() { let registry = Registry::new(); let config_metrics = ConfigMetrics::new(®istry).expect("Failed to create config metrics"); // Record config events config_metrics.record_notify_event(true); config_metrics.record_config_reload("auth", 25.0); config_metrics.update_listener_status(true); config_metrics.record_reconnection(); // Verify metrics can be gathered let metrics = registry.gather(); assert!(!metrics.is_empty(), "No metrics gathered"); let metric_names: Vec = metrics.iter().map(|m| m.name().to_string()).collect(); assert!( metric_names.contains(&"api_notify_events_total".to_string()), "Missing notify_events_total metric" ); assert!( metric_names.contains(&"api_config_reload_duration_milliseconds".to_string()), "Missing config_reload_duration metric" ); assert!( metric_names.contains(&"api_notify_listener_connected".to_string()), "Missing notify_listener_connected metric" ); } #[test] fn test_auth_sla_tracking() { let registry = Registry::new(); let auth_metrics = AuthMetrics::new(®istry).expect("Failed to create auth metrics"); // Record requests meeting SLA (<10μs) auth_metrics.record_success(5.0); auth_metrics.record_success(8.5); auth_metrics.record_success(9.9); // Record requests exceeding SLA (>10μs) auth_metrics.record_success(15.0); auth_metrics.record_success(25.5); let metrics = registry.gather(); let sla_met = metrics .iter() .find(|m| m.name() == "api_auth_sla_met") .expect("SLA met metric not found"); let sla_exceeded = metrics .iter() .find(|m| m.name() == "api_auth_sla_exceeded") .expect("SLA exceeded metric not found"); // Verify counts (3 met, 2 exceeded) assert_eq!(sla_met.metric[0].counter.value, Some(3.0)); assert_eq!(sla_exceeded.metric[0].counter.value, Some(2.0)); } #[test] fn test_cache_hit_rate_tracking() { let registry = Registry::new(); let auth_metrics = AuthMetrics::new(®istry).expect("Failed to create auth metrics"); // Simulate 95% cache hit rate auth_metrics.jwt_cache_hits.inc_by(95.0); auth_metrics.jwt_cache_misses.inc_by(5.0); auth_metrics.rbac_cache_hits.inc_by(98.0); auth_metrics.rbac_cache_misses.inc_by(2.0); let metrics = registry.gather(); let jwt_hits = metrics .iter() .find(|m| m.name() == "api_jwt_cache_hits") .expect("JWT cache hits not found"); let jwt_misses = metrics .iter() .find(|m| m.name() == "api_jwt_cache_misses") .expect("JWT cache misses not found"); assert_eq!(jwt_hits.metric[0].counter.value, Some(95.0)); assert_eq!(jwt_misses.metric[0].counter.value, Some(5.0)); // Verify hit rate calculation let hit_rate = 100.0 * 95.0 / (95.0 + 5.0); assert_eq!(hit_rate, 95.0, "JWT cache hit rate should be 95%"); } #[test] fn test_circuit_breaker_state_transitions() { let registry = Registry::new(); let proxy_metrics = ProxyMetrics::new(®istry).expect("Failed to create proxy metrics"); // Initial state: closed (0) proxy_metrics.update_circuit_breaker_state("trading", 0); // Trip circuit breaker (open = 2) proxy_metrics.record_circuit_breaker_trip("trading"); let metrics = registry.gather(); let cb_state = metrics .iter() .find(|m| m.name() == "api_circuit_breaker_state") .expect("Circuit breaker state not found"); // Verify state is open (2) let trading_state = cb_state .metric .iter() .find(|m| { m.label .iter() .any(|l| l.name() == "service" && l.value() == "trading") }) .expect("Trading service state not found"); assert_eq!(trading_state.gauge.value, Some(2.0)); // Recover circuit breaker (closed = 0) proxy_metrics.record_circuit_breaker_recovery("trading"); let metrics = registry.gather(); let cb_state = metrics .iter() .find(|m| m.name() == "api_circuit_breaker_state") .expect("Circuit breaker state not found"); let trading_state = cb_state .metric .iter() .find(|m| { m.label .iter() .any(|l| l.name() == "service" && l.value() == "trading") }) .expect("Trading service state not found"); assert_eq!(trading_state.gauge.value, Some(0.0)); } #[test] fn test_per_user_metrics() { let registry = Registry::new(); let auth_metrics = AuthMetrics::new(®istry).expect("Failed to create auth metrics"); // Record requests for different users auth_metrics.record_user_request("user_1"); auth_metrics.record_user_request("user_1"); auth_metrics.record_user_request("user_2"); // Record failures per user auth_metrics.record_failure("expired_jwt", Some("user_1")); auth_metrics.record_failure("permission_denied", Some("user_2")); // Record rate limits auth_metrics.record_rate_limit("user_3"); auth_metrics.record_rate_limit("user_3"); let metrics = registry.gather(); // Verify per-user request counts let requests_by_user = metrics .iter() .find(|m| m.name() == "api_requests_by_user") .expect("Requests by user not found"); assert_eq!(requests_by_user.metric.len(), 2); // user_1 and user_2 // Verify per-user rate limits let rate_limits_by_user = metrics .iter() .find(|m| m.name() == "api_rate_limits_by_user") .expect("Rate limits by user not found"); let user_3_limits = rate_limits_by_user .metric .iter() .find(|m| { m.label .iter() .any(|l| l.name() == "user_id" && l.value() == "user_3") }) .expect("User 3 rate limits not found"); assert_eq!(user_3_limits.counter.value, Some(2.0)); } #[test] fn test_metrics_exporter_format() { use api::metrics::PrometheusExporter; let metrics = GatewayMetrics::new().expect("Failed to create metrics"); // Record some events metrics.auth.record_success(5.0); metrics .proxy .record_backend_success("trading", "ExecuteTrade", 15.0); metrics.config.record_notify_event(true); let exporter = PrometheusExporter::new(metrics.registry()); let output = exporter.gather_metrics().expect("Failed to gather metrics"); // Verify Prometheus text format assert!(output.contains("# HELP")); assert!(output.contains("# TYPE")); assert!(output.contains("api_auth_requests_success")); assert!(output.contains("api_backend_requests_success")); assert!(output.contains("api_notify_events_success")); }