//! Database integration tests for TLI system //! //! This module tests all database operations including SQLite configuration management, //! PostgreSQL event storage, InfluxDB time-series data, and transaction integrity. use chrono::{DateTime, Utc}; use sqlx::{Pool, Postgres, Row, Sqlite}; use std::collections::HashMap; use std::time::Duration; use tempfile::TempDir; use tokio::time::timeout; use uuid::Uuid; use crate::integration::{TestConfig, TestUtilities}; use tli::database::config::{ConfigurationManager, DatabaseConfig}; use tli::database::events::{EventStore, EventStoreConfig}; use tli::database::timeseries::{InfluxConfig, TimeSeriesStore}; use tli::prelude::*; /// Database integration test suite pub struct DatabaseIntegrationTests { config: TestConfig, temp_dir: Option, sqlite_pool: Option>, postgres_pool: Option>, config_manager: Option, event_store: Option, timeseries_store: Option, } impl DatabaseIntegrationTests { pub fn new(config: TestConfig) -> Self { Self { config, temp_dir: None, sqlite_pool: None, postgres_pool: None, config_manager: None, event_store: None, timeseries_store: None, } } /// Setup test database environment pub async fn setup(&mut self) -> TliResult<()> { tracing::info!("Setting up database integration test environment"); // Create temporary directory for SQLite databases self.temp_dir = Some(TempDir::new().map_err(|e| TliError::Database(e.to_string()))?); let temp_path = self.temp_dir.as_ref().unwrap().path(); // Setup SQLite for configuration management let sqlite_path = temp_path.join("test_config.db"); let sqlite_url = format!("sqlite:{}", sqlite_path.display()); let sqlite_pool = sqlx::SqlitePool::connect(&sqlite_url) .await .map_err(|e| TliError::Database(format!("Failed to connect to SQLite: {}", e)))?; // Run SQLite migrations sqlx::migrate!("./migrations/sqlite") .run(&sqlite_pool) .await .map_err(|e| TliError::Database(format!("SQLite migration failed: {}", e)))?; self.sqlite_pool = Some(sqlite_pool.clone()); // Setup configuration manager let config_db_config = DatabaseConfig { url: sqlite_url, max_connections: 5, connection_timeout: Duration::from_secs(10), idle_timeout: Some(Duration::from_secs(300)), max_lifetime: Some(Duration::from_secs(1800)), }; self.config_manager = Some(ConfigurationManager::new(config_db_config).await?); // Setup PostgreSQL for event storage (if available) if let Ok(postgres_url) = std::env::var("TEST_POSTGRES_URL") { match sqlx::PgPool::connect(&postgres_url).await { Ok(pool) => { // Run PostgreSQL migrations if let Err(e) = sqlx::migrate!("./migrations/postgres").run(&pool).await { tracing::warn!("PostgreSQL migration failed: {}", e); } else { self.postgres_pool = Some(pool.clone()); // Setup event store let event_config = EventStoreConfig { database_url: postgres_url, max_connections: 10, batch_size: 1000, flush_interval: Duration::from_secs(5), }; self.event_store = Some(EventStore::new(event_config).await?); } } Err(e) => { tracing::warn!("Failed to connect to PostgreSQL for testing: {}", e); } } } else { tracing::info!("PostgreSQL testing disabled (TEST_POSTGRES_URL not set)"); } // Setup InfluxDB for time-series data (if available) if let Ok(influx_url) = std::env::var("TEST_INFLUX_URL") { let influx_config = InfluxConfig { url: influx_url, token: std::env::var("TEST_INFLUX_TOKEN").unwrap_or_default(), org: std::env::var("TEST_INFLUX_ORG").unwrap_or("test_org".to_string()), bucket: "test_bucket".to_string(), }; match TimeSeriesStore::new(influx_config).await { Ok(store) => { self.timeseries_store = Some(store); } Err(e) => { tracing::warn!("Failed to connect to InfluxDB for testing: {}", e); } } } else { tracing::info!("InfluxDB testing disabled (TEST_INFLUX_URL not set)"); } tracing::info!("Database integration test environment setup complete"); Ok(()) } /// Cleanup test database environment pub async fn teardown(&mut self) -> TliResult<()> { tracing::info!("Tearing down database integration test environment"); // Close database connections if let Some(pool) = self.sqlite_pool.take() { pool.close().await; } if let Some(pool) = self.postgres_pool.take() { pool.close().await; } // Clean up temporary directory if let Some(temp_dir) = self.temp_dir.take() { let _ = temp_dir.close(); } tracing::info!("Database integration test environment teardown complete"); Ok(()) } } /// SQLite configuration management tests #[cfg(test)] mod sqlite_config_tests { use super::*; #[tokio::test] async fn test_configuration_crud_operations() { let mut test_env = DatabaseIntegrationTests::new(TestConfig::default()); test_env .setup() .await .expect("Failed to setup test environment"); let config_manager = test_env .config_manager .as_ref() .expect("Configuration manager not available"); // Test configuration creation let test_config = ConfigurationEntry { id: Uuid::new_v4().to_string(), config_type: ConfigurationType::TradingLimits, key: "max_daily_loss".to_string(), value: "50000.00".to_string(), account_id: Some("test_account".to_string()), description: Some("Maximum daily loss limit".to_string()), created_at: Utc::now(), updated_at: Utc::now(), version: 1, }; // Create configuration config_manager .create_configuration(&test_config) .await .expect("Failed to create configuration"); // Read configuration let retrieved_config = config_manager .get_configuration(&test_config.id) .await .expect("Failed to get configuration") .expect("Configuration not found"); assert_eq!(retrieved_config.id, test_config.id); assert_eq!(retrieved_config.key, test_config.key); assert_eq!(retrieved_config.value, test_config.value); // Update configuration let mut updated_config = retrieved_config.clone(); updated_config.value = "75000.00".to_string(); updated_config.updated_at = Utc::now(); config_manager .update_configuration(&updated_config) .await .expect("Failed to update configuration"); // Verify update let updated_retrieved = config_manager .get_configuration(&test_config.id) .await .expect("Failed to get updated configuration") .expect("Updated configuration not found"); assert_eq!(updated_retrieved.value, "75000.00"); assert!(updated_retrieved.updated_at > test_config.updated_at); // Delete configuration config_manager .delete_configuration(&test_config.id) .await .expect("Failed to delete configuration"); // Verify deletion let deleted_config = config_manager .get_configuration(&test_config.id) .await .expect("Failed to check deleted configuration"); assert!(deleted_config.is_none(), "Configuration should be deleted"); test_env .teardown() .await .expect("Failed to teardown test environment"); } #[tokio::test] async fn test_configuration_bulk_operations() { let mut test_env = DatabaseIntegrationTests::new(TestConfig::default()); test_env .setup() .await .expect("Failed to setup test environment"); let config_manager = test_env .config_manager .as_ref() .expect("Configuration manager not available"); // Create multiple configurations let mut configs = Vec::new(); for i in 1..=10 { configs.push(ConfigurationEntry { id: Uuid::new_v4().to_string(), config_type: ConfigurationType::TradingLimits, key: format!("test_limit_{}", i), value: format!("{}.00", i * 1000), account_id: Some("test_account".to_string()), description: Some(format!("Test limit {}", i)), created_at: Utc::now(), updated_at: Utc::now(), version: 1, }); } // Bulk create config_manager .bulk_create_configurations(&configs) .await .expect("Failed to bulk create configurations"); // Retrieve by type let retrieved_configs = config_manager .get_configurations_by_type(ConfigurationType::TradingLimits) .await .expect("Failed to get configurations by type"); assert_eq!(retrieved_configs.len(), 10, "Should have 10 configurations"); // Bulk update let mut updated_configs = retrieved_configs.clone(); for config in &mut updated_configs { config.value = format!("updated_{}", config.value); config.updated_at = Utc::now(); } config_manager .bulk_update_configurations(&updated_configs) .await .expect("Failed to bulk update configurations"); // Verify updates let final_configs = config_manager .get_configurations_by_type(ConfigurationType::TradingLimits) .await .expect("Failed to get final configurations"); for config in &final_configs { assert!( config.value.starts_with("updated_"), "Configuration should be updated" ); } test_env .teardown() .await .expect("Failed to teardown test environment"); } #[tokio::test] async fn test_configuration_versioning() { let mut test_env = DatabaseIntegrationTests::new(TestConfig::default()); test_env .setup() .await .expect("Failed to setup test environment"); let config_manager = test_env .config_manager .as_ref() .expect("Configuration manager not available"); let config_id = Uuid::new_v4().to_string(); let test_config = ConfigurationEntry { id: config_id.clone(), config_type: ConfigurationType::TradingLimits, key: "position_limit".to_string(), value: "1000000".to_string(), account_id: Some("test_account".to_string()), description: Some("Position limit with versioning".to_string()), created_at: Utc::now(), updated_at: Utc::now(), version: 1, }; // Create initial version config_manager .create_configuration(&test_config) .await .expect("Failed to create configuration"); // Update multiple times to test versioning for version in 2..=5 { let mut updated_config = config_manager .get_configuration(&config_id) .await .expect("Failed to get configuration") .expect("Configuration not found"); updated_config.value = format!("{}", version * 500000); updated_config.updated_at = Utc::now(); config_manager .update_configuration(&updated_config) .await .expect("Failed to update configuration"); // Verify version increment let current_config = config_manager .get_configuration(&config_id) .await .expect("Failed to get updated configuration") .expect("Updated configuration not found"); assert_eq!( current_config.version, version, "Version should be incremented" ); } // Get configuration history let history = config_manager .get_configuration_history(&config_id) .await .expect("Failed to get configuration history"); assert_eq!(history.len(), 5, "Should have 5 versions in history"); // Verify history ordering for (i, entry) in history.iter().enumerate() { assert_eq!( entry.version, (i + 1) as i32, "History should be ordered by version" ); } test_env .teardown() .await .expect("Failed to teardown test environment"); } #[tokio::test] async fn test_configuration_backup_restore() { let mut test_env = DatabaseIntegrationTests::new(TestConfig::default()); test_env .setup() .await .expect("Failed to setup test environment"); let config_manager = test_env .config_manager .as_ref() .expect("Configuration manager not available"); // Create test configurations let configs = create_test_configurations(5); config_manager .bulk_create_configurations(&configs) .await .expect("Failed to create test configurations"); // Create backup let backup_data = config_manager .create_backup() .await .expect("Failed to create backup"); assert!( !backup_data.configurations.is_empty(), "Backup should contain configurations" ); assert_eq!( backup_data.configurations.len(), 5, "Backup should contain all configurations" ); // Clear configurations for config in &configs { config_manager .delete_configuration(&config.id) .await .expect("Failed to delete configuration"); } // Verify configurations are deleted let remaining_configs = config_manager .get_configurations_by_type(ConfigurationType::TradingLimits) .await .expect("Failed to get configurations"); assert!( remaining_configs.is_empty(), "Configurations should be deleted" ); // Restore from backup config_manager .restore_from_backup(&backup_data) .await .expect("Failed to restore from backup"); // Verify restoration let restored_configs = config_manager .get_configurations_by_type(ConfigurationType::TradingLimits) .await .expect("Failed to get restored configurations"); assert_eq!( restored_configs.len(), 5, "All configurations should be restored" ); test_env .teardown() .await .expect("Failed to teardown test environment"); } fn create_test_configurations(count: usize) -> Vec { (1..=count) .map(|i| ConfigurationEntry { id: Uuid::new_v4().to_string(), config_type: ConfigurationType::TradingLimits, key: format!("test_config_{}", i), value: format!("value_{}", i), account_id: Some("test_account".to_string()), description: Some(format!("Test configuration {}", i)), created_at: Utc::now(), updated_at: Utc::now(), version: 1, }) .collect() } } /// PostgreSQL event storage tests #[cfg(test)] mod postgres_event_tests { use super::*; #[tokio::test] async fn test_event_storage_and_retrieval() { let mut test_env = DatabaseIntegrationTests::new(TestConfig::default()); test_env .setup() .await .expect("Failed to setup test environment"); let event_store = match test_env.event_store.as_ref() { Some(store) => store, None => { tracing::info!("Skipping PostgreSQL test (not available)"); return; } }; // Create test events let events = create_test_events(10); // Store events for event in &events { event_store .store_event(event) .await .expect("Failed to store event"); } // Retrieve events by type let order_events = event_store .get_events_by_type(EventType::OrderExecuted, None, None) .await .expect("Failed to get order events"); assert!(!order_events.is_empty(), "Should have order events"); // Retrieve events by time range let now = Utc::now(); let one_hour_ago = now - chrono::Duration::hours(1); let recent_events = event_store .get_events_by_time_range(one_hour_ago, now) .await .expect("Failed to get recent events"); assert_eq!(recent_events.len(), 10, "Should have all recent events"); // Test event filtering let filters = EventFilters { symbol: Some("AAPL".to_string()), account_id: Some("test_account".to_string()), min_severity: Some(EventSeverity::Info), }; let filtered_events = event_store .get_filtered_events(&filters, None, None) .await .expect("Failed to get filtered events"); assert!(!filtered_events.is_empty(), "Should have filtered events"); test_env .teardown() .await .expect("Failed to teardown test environment"); } #[tokio::test] async fn test_event_aggregation() { let mut test_env = DatabaseIntegrationTests::new(TestConfig::default()); test_env .setup() .await .expect("Failed to setup test environment"); let event_store = match test_env.event_store.as_ref() { Some(store) => store, None => { tracing::info!("Skipping PostgreSQL test (not available)"); return; } }; // Create events for aggregation testing let events = create_events_for_aggregation(100); // Store events in batches to test batch processing let batch_size = 10; for chunk in events.chunks(batch_size) { event_store .store_events_batch(chunk) .await .expect("Failed to store event batch"); } // Test event count aggregation let event_counts = event_store .get_event_counts_by_type(Utc::now() - chrono::Duration::hours(1), Utc::now()) .await .expect("Failed to get event counts"); assert!(!event_counts.is_empty(), "Should have event counts"); // Test event statistics let stats = event_store .get_event_statistics("test_account", chrono::Duration::hours(1)) .await .expect("Failed to get event statistics"); assert!(stats.total_events > 0, "Should have total events"); assert!( !stats.events_by_type.is_empty(), "Should have events by type" ); test_env .teardown() .await .expect("Failed to teardown test environment"); } #[tokio::test] async fn test_event_cleanup_and_archival() { let mut test_env = DatabaseIntegrationTests::new(TestConfig::default()); test_env .setup() .await .expect("Failed to setup test environment"); let event_store = match test_env.event_store.as_ref() { Some(store) => store, None => { tracing::info!("Skipping PostgreSQL test (not available)"); return; } }; // Create old events for cleanup testing let mut old_events = create_test_events(20); let old_timestamp = Utc::now() - chrono::Duration::days(30); // Modify timestamps to make events old for event in &mut old_events { event.timestamp = old_timestamp; } // Store old events event_store .store_events_batch(&old_events) .await .expect("Failed to store old events"); // Create recent events let recent_events = create_test_events(10); event_store .store_events_batch(&recent_events) .await .expect("Failed to store recent events"); // Test cleanup of old events let cleanup_before = Utc::now() - chrono::Duration::days(7); let cleaned_count = event_store .cleanup_old_events(cleanup_before) .await .expect("Failed to cleanup old events"); assert_eq!(cleaned_count, 20, "Should have cleaned up 20 old events"); // Verify recent events are still there let remaining_events = event_store .get_events_by_time_range(Utc::now() - chrono::Duration::hours(1), Utc::now()) .await .expect("Failed to get remaining events"); assert_eq!( remaining_events.len(), 10, "Should have 10 recent events remaining" ); test_env .teardown() .await .expect("Failed to teardown test environment"); } fn create_test_events(count: usize) -> Vec { (0..count) .map(|i| TliEvent { event_id: Uuid::new_v4().to_string(), event_type: if i % 2 == 0 { EventType::OrderExecuted } else { EventType::PositionChanged }, timestamp: Utc::now(), data: serde_json::json!({ "symbol": "AAPL", "quantity": (i + 1) * 100, "price": 150.0 + (i as f64 * 0.5) }), severity: EventSeverity::Info, source_service: "trading_service".to_string(), account_id: Some("test_account".to_string()), correlation_id: Some(Uuid::new_v4().to_string()), }) .collect() } fn create_events_for_aggregation(count: usize) -> Vec { let event_types = [ EventType::OrderExecuted, EventType::PositionChanged, EventType::RiskLimitBreached, EventType::MarketDataReceived, ]; (0..count) .map(|i| TliEvent { event_id: Uuid::new_v4().to_string(), event_type: event_types[i % event_types.len()], timestamp: Utc::now() - chrono::Duration::minutes((i % 60) as i64), data: serde_json::json!({ "test_data": format!("event_{}", i) }), severity: EventSeverity::Info, source_service: "trading_service".to_string(), account_id: Some("test_account".to_string()), correlation_id: Some(Uuid::new_v4().to_string()), }) .collect() } } /// InfluxDB time-series data tests #[cfg(test)] mod influx_timeseries_tests { use super::*; #[tokio::test] async fn test_timeseries_data_operations() { let mut test_env = DatabaseIntegrationTests::new(TestConfig::default()); test_env .setup() .await .expect("Failed to setup test environment"); let timeseries_store = match test_env.timeseries_store.as_ref() { Some(store) => store, None => { tracing::info!("Skipping InfluxDB test (not available)"); return; } }; // Create test time-series data let market_data_points = create_test_market_data(50); // Write data to InfluxDB timeseries_store .write_market_data(&market_data_points) .await .expect("Failed to write market data"); // Query recent data let query_start = Utc::now() - chrono::Duration::minutes(10); let query_end = Utc::now(); let retrieved_data = timeseries_store .query_market_data("AAPL", query_start, query_end) .await .expect("Failed to query market data"); assert!( !retrieved_data.is_empty(), "Should have retrieved market data" ); // Test aggregated queries let aggregated_data = timeseries_store .query_aggregated_data("AAPL", query_start, query_end, AggregationWindow::OneMinute) .await .expect("Failed to query aggregated data"); assert!(!aggregated_data.is_empty(), "Should have aggregated data"); test_env .teardown() .await .expect("Failed to teardown test environment"); } #[tokio::test] async fn test_performance_metrics_storage() { let mut test_env = DatabaseIntegrationTests::new(TestConfig::default()); test_env .setup() .await .expect("Failed to setup test environment"); let timeseries_store = match test_env.timeseries_store.as_ref() { Some(store) => store, None => { tracing::info!("Skipping InfluxDB test (not available)"); return; } }; // Create performance metrics let performance_metrics = create_test_performance_metrics(30); // Write performance data timeseries_store .write_performance_metrics(&performance_metrics) .await .expect("Failed to write performance metrics"); // Query performance data let query_start = Utc::now() - chrono::Duration::minutes(5); let query_end = Utc::now(); let retrieved_metrics = timeseries_store .query_performance_metrics(query_start, query_end) .await .expect("Failed to query performance metrics"); assert!( !retrieved_metrics.is_empty(), "Should have performance metrics" ); // Verify metric types let metric_types: std::collections::HashSet = retrieved_metrics .iter() .map(|m| m.metric_type.clone()) .collect(); assert!( metric_types.contains("latency"), "Should have latency metrics" ); assert!( metric_types.contains("throughput"), "Should have throughput metrics" ); test_env .teardown() .await .expect("Failed to teardown test environment"); } fn create_test_market_data(count: usize) -> Vec { (0..count) .map(|i| MarketDataPoint { symbol: "AAPL".to_string(), timestamp: Utc::now() - chrono::Duration::seconds(i as i64 * 10), bid_price: 150.0 + (i as f64 * 0.1), ask_price: 150.1 + (i as f64 * 0.1), bid_size: 100.0, ask_size: 100.0, last_price: Some(150.05 + (i as f64 * 0.1)), volume: Some((i + 1) as f64 * 1000.0), }) .collect() } fn create_test_performance_metrics(count: usize) -> Vec { let metric_types = ["latency", "throughput", "cpu_usage", "memory_usage"]; (0..count) .map(|i| PerformanceMetric { timestamp: Utc::now() - chrono::Duration::seconds(i as i64 * 5), metric_type: metric_types[i % metric_types.len()].to_string(), value: (i as f64 + 1.0) * 10.0, service_name: "trading_service".to_string(), tags: HashMap::from([ ("environment".to_string(), "test".to_string()), ("instance".to_string(), "test_instance".to_string()), ]), }) .collect() } } /// Transaction integrity and rollback tests #[cfg(test)] mod transaction_tests { use super::*; #[tokio::test] async fn test_configuration_transaction_rollback() { let mut test_env = DatabaseIntegrationTests::new(TestConfig::default()); test_env .setup() .await .expect("Failed to setup test environment"); let config_manager = test_env .config_manager .as_ref() .expect("Configuration manager not available"); // Create initial configuration let config = ConfigurationEntry { id: Uuid::new_v4().to_string(), config_type: ConfigurationType::TradingLimits, key: "test_transaction".to_string(), value: "initial_value".to_string(), account_id: Some("test_account".to_string()), description: Some("Transaction test config".to_string()), created_at: Utc::now(), updated_at: Utc::now(), version: 1, }; config_manager .create_configuration(&config) .await .expect("Failed to create initial configuration"); // Simulate transaction that should fail and rollback let result = config_manager .execute_transaction(|tx| async move { // Update configuration within transaction let mut updated_config = config.clone(); updated_config.value = "updated_value".to_string(); config_manager .update_configuration_tx(&mut updated_config, tx) .await?; // Simulate error that causes rollback Err(TliError::Database( "Simulated transaction failure".to_string(), )) }) .await; assert!(result.is_err(), "Transaction should have failed"); // Verify configuration was not updated (rollback successful) let final_config = config_manager .get_configuration(&config.id) .await .expect("Failed to get configuration after rollback") .expect("Configuration should still exist"); assert_eq!( final_config.value, "initial_value", "Configuration should not be updated after rollback" ); test_env .teardown() .await .expect("Failed to teardown test environment"); } #[tokio::test] async fn test_multi_database_consistency() { let mut test_env = DatabaseIntegrationTests::new(TestConfig::default()); test_env .setup() .await .expect("Failed to setup test environment"); let config_manager = test_env .config_manager .as_ref() .expect("Configuration manager not available"); let event_store = match test_env.event_store.as_ref() { Some(store) => store, None => { tracing::info!("Skipping multi-database test (PostgreSQL not available)"); return; } }; // Test coordinated operations across multiple databases let config_id = Uuid::new_v4().to_string(); let event_id = Uuid::new_v4().to_string(); // Create configuration let config = ConfigurationEntry { id: config_id.clone(), config_type: ConfigurationType::TradingLimits, key: "multi_db_test".to_string(), value: "test_value".to_string(), account_id: Some("test_account".to_string()), description: Some("Multi-database consistency test".to_string()), created_at: Utc::now(), updated_at: Utc::now(), version: 1, }; // Create event let event = TliEvent { event_id: event_id.clone(), event_type: EventType::ConfigurationChanged, timestamp: Utc::now(), data: serde_json::json!({ "config_id": config_id, "operation": "create" }), severity: EventSeverity::Info, source_service: "config_service".to_string(), account_id: Some("test_account".to_string()), correlation_id: Some(Uuid::new_v4().to_string()), }; // Execute coordinated operations config_manager .create_configuration(&config) .await .expect("Failed to create configuration"); event_store .store_event(&event) .await .expect("Failed to store event"); // Verify both operations succeeded let stored_config = config_manager .get_configuration(&config_id) .await .expect("Failed to get configuration") .expect("Configuration should exist"); let stored_events = event_store .get_events_by_correlation_id(&event.correlation_id.unwrap()) .await .expect("Failed to get events"); assert_eq!(stored_config.id, config_id); assert!(!stored_events.is_empty()); test_env .teardown() .await .expect("Failed to teardown test environment"); } } /// Database connection and failover tests #[cfg(test)] mod connection_tests { use super::*; #[tokio::test] async fn test_connection_pool_management() { let mut test_env = DatabaseIntegrationTests::new(TestConfig::default()); test_env .setup() .await .expect("Failed to setup test environment"); let sqlite_pool = test_env .sqlite_pool .as_ref() .expect("SQLite pool not available"); // Test connection pool behavior let initial_connections = sqlite_pool.size(); tracing::info!("Initial pool size: {}", initial_connections); // Execute multiple concurrent operations let handles: Vec<_> = (0..10) .map(|i| { let pool = sqlite_pool.clone(); tokio::spawn(async move { let query = "SELECT 1 as test_value"; let row: (i32,) = sqlx::query_as(query) .fetch_one(&pool) .await .expect("Failed to execute test query"); assert_eq!(row.0, 1); i }) }) .collect(); // Wait for all operations to complete for handle in handles { handle.await.expect("Task failed"); } // Pool should manage connections properly let final_connections = sqlite_pool.size(); tracing::info!("Final pool size: {}", final_connections); assert!( final_connections >= initial_connections, "Pool should have adequate connections" ); test_env .teardown() .await .expect("Failed to teardown test environment"); } #[tokio::test] async fn test_connection_timeout_handling() { let config = TestConfig { timeout: Duration::from_millis(100), // Very short timeout for testing ..TestConfig::default() }; let mut test_env = DatabaseIntegrationTests::new(config); test_env .setup() .await .expect("Failed to setup test environment"); let config_manager = test_env .config_manager .as_ref() .expect("Configuration manager not available"); // Test timeout behavior with short timeout let start_time = std::time::Instant::now(); let result = timeout( Duration::from_millis(200), config_manager.get_configurations_by_type(ConfigurationType::TradingLimits), ) .await; let elapsed = start_time.elapsed(); // Operation should complete within timeout assert!(result.is_ok(), "Operation should complete within timeout"); assert!( elapsed < Duration::from_millis(200), "Operation should be fast" ); test_env .teardown() .await .expect("Failed to teardown test environment"); } }