...
This commit is contained in:
		
							
								
								
									
										843
									
								
								packages/clients/postgresclient/tests/postgres_tests.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										843
									
								
								packages/clients/postgresclient/tests/postgres_tests.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,843 @@ | ||||
| use sal_postgresclient::*; | ||||
| use std::collections::HashMap; | ||||
| use std::env; | ||||
|  | ||||
| #[cfg(test)] | ||||
| mod postgres_client_tests { | ||||
|     use super::*; | ||||
|  | ||||
|     #[test] | ||||
|     fn test_env_vars() { | ||||
|         // Save original environment variables to restore later | ||||
|         let original_host = env::var("POSTGRES_HOST").ok(); | ||||
|         let original_port = env::var("POSTGRES_PORT").ok(); | ||||
|         let original_user = env::var("POSTGRES_USER").ok(); | ||||
|         let original_password = env::var("POSTGRES_PASSWORD").ok(); | ||||
|         let original_db = env::var("POSTGRES_DB").ok(); | ||||
|  | ||||
|         // Set test environment variables | ||||
|         env::set_var("POSTGRES_HOST", "test-host"); | ||||
|         env::set_var("POSTGRES_PORT", "5433"); | ||||
|         env::set_var("POSTGRES_USER", "test-user"); | ||||
|         env::set_var("POSTGRES_PASSWORD", "test-password"); | ||||
|         env::set_var("POSTGRES_DB", "test-db"); | ||||
|  | ||||
|         // Test with invalid port | ||||
|         env::set_var("POSTGRES_PORT", "invalid"); | ||||
|  | ||||
|         // Test with unset values | ||||
|         env::remove_var("POSTGRES_HOST"); | ||||
|         env::remove_var("POSTGRES_PORT"); | ||||
|         env::remove_var("POSTGRES_USER"); | ||||
|         env::remove_var("POSTGRES_PASSWORD"); | ||||
|         env::remove_var("POSTGRES_DB"); | ||||
|  | ||||
|         // Restore original environment variables | ||||
|         if let Some(host) = original_host { | ||||
|             env::set_var("POSTGRES_HOST", host); | ||||
|         } | ||||
|         if let Some(port) = original_port { | ||||
|             env::set_var("POSTGRES_PORT", port); | ||||
|         } | ||||
|         if let Some(user) = original_user { | ||||
|             env::set_var("POSTGRES_USER", user); | ||||
|         } | ||||
|         if let Some(password) = original_password { | ||||
|             env::set_var("POSTGRES_PASSWORD", password); | ||||
|         } | ||||
|         if let Some(db) = original_db { | ||||
|             env::set_var("POSTGRES_DB", db); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
|     fn test_postgres_config_builder() { | ||||
|         // Test the PostgreSQL configuration builder | ||||
|  | ||||
|         // Test default values | ||||
|         let config = PostgresConfigBuilder::new(); | ||||
|         assert_eq!(config.host, "localhost"); | ||||
|         assert_eq!(config.port, 5432); | ||||
|         assert_eq!(config.user, "postgres"); | ||||
|         assert_eq!(config.password, None); | ||||
|         assert_eq!(config.database, "postgres"); | ||||
|         assert_eq!(config.application_name, None); | ||||
|         assert_eq!(config.connect_timeout, None); | ||||
|         assert_eq!(config.ssl_mode, None); | ||||
|  | ||||
|         // Test setting values | ||||
|         let config = PostgresConfigBuilder::new() | ||||
|             .host("pg.example.com") | ||||
|             .port(5433) | ||||
|             .user("test-user") | ||||
|             .password("test-password") | ||||
|             .database("test-db") | ||||
|             .application_name("test-app") | ||||
|             .connect_timeout(30) | ||||
|             .ssl_mode("require"); | ||||
|  | ||||
|         assert_eq!(config.host, "pg.example.com"); | ||||
|         assert_eq!(config.port, 5433); | ||||
|         assert_eq!(config.user, "test-user"); | ||||
|         assert_eq!(config.password, Some("test-password".to_string())); | ||||
|         assert_eq!(config.database, "test-db"); | ||||
|         assert_eq!(config.application_name, Some("test-app".to_string())); | ||||
|         assert_eq!(config.connect_timeout, Some(30)); | ||||
|         assert_eq!(config.ssl_mode, Some("require".to_string())); | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
|     fn test_connection_string_building() { | ||||
|         // Test building connection strings | ||||
|  | ||||
|         // Test default connection string | ||||
|         let config = PostgresConfigBuilder::new(); | ||||
|         let conn_string = config.build_connection_string(); | ||||
|         assert!(conn_string.contains("host=localhost")); | ||||
|         assert!(conn_string.contains("port=5432")); | ||||
|         assert!(conn_string.contains("user=postgres")); | ||||
|         assert!(conn_string.contains("dbname=postgres")); | ||||
|         assert!(!conn_string.contains("password=")); | ||||
|  | ||||
|         // Test with all options | ||||
|         let config = PostgresConfigBuilder::new() | ||||
|             .host("pg.example.com") | ||||
|             .port(5433) | ||||
|             .user("test-user") | ||||
|             .password("test-password") | ||||
|             .database("test-db") | ||||
|             .application_name("test-app") | ||||
|             .connect_timeout(30) | ||||
|             .ssl_mode("require"); | ||||
|  | ||||
|         let conn_string = config.build_connection_string(); | ||||
|         assert!(conn_string.contains("host=pg.example.com")); | ||||
|         assert!(conn_string.contains("port=5433")); | ||||
|         assert!(conn_string.contains("user=test-user")); | ||||
|         assert!(conn_string.contains("password=test-password")); | ||||
|         assert!(conn_string.contains("dbname=test-db")); | ||||
|         assert!(conn_string.contains("application_name=test-app")); | ||||
|         assert!(conn_string.contains("connect_timeout=30")); | ||||
|         assert!(conn_string.contains("sslmode=require")); | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
|     fn test_reset_mock() { | ||||
|         // This is a simplified test that doesn't require an actual PostgreSQL server | ||||
|  | ||||
|         // Just verify that the reset function doesn't panic | ||||
|         if let Err(_) = reset() { | ||||
|             // If PostgreSQL is not available, this is expected to fail | ||||
|             // So we don't assert anything here | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| // Integration tests that require a real PostgreSQL server | ||||
| // These tests will be skipped if PostgreSQL is not available | ||||
| #[cfg(test)] | ||||
| mod postgres_installer_tests { | ||||
|     use super::*; | ||||
|     use sal_virt::nerdctl::Container; | ||||
|  | ||||
|     #[test] | ||||
|     fn test_postgres_installer_config() { | ||||
|         // Test default configuration | ||||
|         let config = PostgresInstallerConfig::default(); | ||||
|         assert_eq!(config.container_name, "postgres"); | ||||
|         assert_eq!(config.version, "latest"); | ||||
|         assert_eq!(config.port, 5432); | ||||
|         assert_eq!(config.username, "postgres"); | ||||
|         assert_eq!(config.password, "postgres"); | ||||
|         assert_eq!(config.data_dir, None); | ||||
|         assert_eq!(config.env_vars.len(), 0); | ||||
|         assert_eq!(config.persistent, true); | ||||
|  | ||||
|         // Test builder pattern | ||||
|         let config = PostgresInstallerConfig::new() | ||||
|             .container_name("my-postgres") | ||||
|             .version("15") | ||||
|             .port(5433) | ||||
|             .username("testuser") | ||||
|             .password("testpass") | ||||
|             .data_dir("/tmp/pgdata") | ||||
|             .env_var("POSTGRES_INITDB_ARGS", "--encoding=UTF8") | ||||
|             .persistent(false); | ||||
|  | ||||
|         assert_eq!(config.container_name, "my-postgres"); | ||||
|         assert_eq!(config.version, "15"); | ||||
|         assert_eq!(config.port, 5433); | ||||
|         assert_eq!(config.username, "testuser"); | ||||
|         assert_eq!(config.password, "testpass"); | ||||
|         assert_eq!(config.data_dir, Some("/tmp/pgdata".to_string())); | ||||
|         assert_eq!(config.env_vars.len(), 1); | ||||
|         assert_eq!( | ||||
|             config.env_vars.get("POSTGRES_INITDB_ARGS").unwrap(), | ||||
|             "--encoding=UTF8" | ||||
|         ); | ||||
|         assert_eq!(config.persistent, false); | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
|     fn test_postgres_installer_error() { | ||||
|         // Test IoError | ||||
|         let io_error = std::io::Error::new(std::io::ErrorKind::NotFound, "File not found"); | ||||
|         let installer_error = PostgresInstallerError::IoError(io_error); | ||||
|         assert!(format!("{}", installer_error).contains("I/O error")); | ||||
|  | ||||
|         // Test NerdctlError | ||||
|         let nerdctl_error = PostgresInstallerError::NerdctlError("Container not found".to_string()); | ||||
|         assert!(format!("{}", nerdctl_error).contains("Nerdctl error")); | ||||
|  | ||||
|         // Test PostgresError | ||||
|         let postgres_error = | ||||
|             PostgresInstallerError::PostgresError("Database not found".to_string()); | ||||
|         assert!(format!("{}", postgres_error).contains("PostgreSQL error")); | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
|     fn test_install_postgres_with_defaults() { | ||||
|         // This is a unit test that doesn't actually install PostgreSQL | ||||
|         // It just tests the configuration and error handling | ||||
|  | ||||
|         // Test with default configuration | ||||
|         let config = PostgresInstallerConfig::default(); | ||||
|  | ||||
|         // We expect this to fail because nerdctl is not available | ||||
|         let result = install_postgres(config); | ||||
|         assert!(result.is_err()); | ||||
|  | ||||
|         // Check that the error is a NerdctlError or IoError | ||||
|         match result { | ||||
|             Err(PostgresInstallerError::NerdctlError(_)) => { | ||||
|                 // This is fine, we expected a NerdctlError | ||||
|             } | ||||
|             Err(PostgresInstallerError::IoError(_)) => { | ||||
|                 // This is also fine, we expected an error | ||||
|             } | ||||
|             _ => panic!("Expected NerdctlError or IoError"), | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
|     fn test_install_postgres_with_custom_config() { | ||||
|         // Test with custom configuration | ||||
|         let config = PostgresInstallerConfig::new() | ||||
|             .container_name("test-postgres") | ||||
|             .version("15") | ||||
|             .port(5433) | ||||
|             .username("testuser") | ||||
|             .password("testpass") | ||||
|             .data_dir("/tmp/pgdata") | ||||
|             .env_var("POSTGRES_INITDB_ARGS", "--encoding=UTF8") | ||||
|             .persistent(true); | ||||
|  | ||||
|         // We expect this to fail because nerdctl is not available | ||||
|         let result = install_postgres(config); | ||||
|         assert!(result.is_err()); | ||||
|  | ||||
|         // Check that the error is a NerdctlError or IoError | ||||
|         match result { | ||||
|             Err(PostgresInstallerError::NerdctlError(_)) => { | ||||
|                 // This is fine, we expected a NerdctlError | ||||
|             } | ||||
|             Err(PostgresInstallerError::IoError(_)) => { | ||||
|                 // This is also fine, we expected an error | ||||
|             } | ||||
|             _ => panic!("Expected NerdctlError or IoError"), | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
|     fn test_create_database() { | ||||
|         // Create a mock container | ||||
|         // In a real test, we would use mockall to create a mock container | ||||
|         // But for this test, we'll just test the error handling | ||||
|  | ||||
|         // We expect this to fail because the container is not running | ||||
|         let result = create_database( | ||||
|             &Container { | ||||
|                 name: "test-postgres".to_string(), | ||||
|                 container_id: None, | ||||
|                 image: Some("postgres:15".to_string()), | ||||
|                 config: HashMap::new(), | ||||
|                 ports: Vec::new(), | ||||
|                 volumes: Vec::new(), | ||||
|                 env_vars: HashMap::new(), | ||||
|                 network: None, | ||||
|                 network_aliases: Vec::new(), | ||||
|                 cpu_limit: None, | ||||
|                 memory_limit: None, | ||||
|                 memory_swap_limit: None, | ||||
|                 cpu_shares: None, | ||||
|                 restart_policy: None, | ||||
|                 health_check: None, | ||||
|                 detach: false, | ||||
|                 snapshotter: None, | ||||
|             }, | ||||
|             "testdb", | ||||
|         ); | ||||
|  | ||||
|         assert!(result.is_err()); | ||||
|  | ||||
|         // Check that the error is a PostgresError | ||||
|         match result { | ||||
|             Err(PostgresInstallerError::PostgresError(msg)) => { | ||||
|                 assert!(msg.contains("Container is not running")); | ||||
|             } | ||||
|             _ => panic!("Expected PostgresError"), | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
|     fn test_execute_sql() { | ||||
|         // Create a mock container | ||||
|         // In a real test, we would use mockall to create a mock container | ||||
|         // But for this test, we'll just test the error handling | ||||
|  | ||||
|         // We expect this to fail because the container is not running | ||||
|         let result = execute_sql( | ||||
|             &Container { | ||||
|                 name: "test-postgres".to_string(), | ||||
|                 container_id: None, | ||||
|                 image: Some("postgres:15".to_string()), | ||||
|                 config: HashMap::new(), | ||||
|                 ports: Vec::new(), | ||||
|                 volumes: Vec::new(), | ||||
|                 env_vars: HashMap::new(), | ||||
|                 network: None, | ||||
|                 network_aliases: Vec::new(), | ||||
|                 cpu_limit: None, | ||||
|                 memory_limit: None, | ||||
|                 memory_swap_limit: None, | ||||
|                 cpu_shares: None, | ||||
|                 restart_policy: None, | ||||
|                 health_check: None, | ||||
|                 detach: false, | ||||
|                 snapshotter: None, | ||||
|             }, | ||||
|             "testdb", | ||||
|             "SELECT 1", | ||||
|         ); | ||||
|  | ||||
|         assert!(result.is_err()); | ||||
|  | ||||
|         // Check that the error is a PostgresError | ||||
|         match result { | ||||
|             Err(PostgresInstallerError::PostgresError(msg)) => { | ||||
|                 assert!(msg.contains("Container is not running")); | ||||
|             } | ||||
|             _ => panic!("Expected PostgresError"), | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
|     fn test_is_postgres_running() { | ||||
|         // Create a mock container | ||||
|         // In a real test, we would use mockall to create a mock container | ||||
|         // But for this test, we'll just test the error handling | ||||
|  | ||||
|         // We expect this to return false because the container is not running | ||||
|         let result = is_postgres_running(&Container { | ||||
|             name: "test-postgres".to_string(), | ||||
|             container_id: None, | ||||
|             image: Some("postgres:15".to_string()), | ||||
|             config: HashMap::new(), | ||||
|             ports: Vec::new(), | ||||
|             volumes: Vec::new(), | ||||
|             env_vars: HashMap::new(), | ||||
|             network: None, | ||||
|             network_aliases: Vec::new(), | ||||
|             cpu_limit: None, | ||||
|             memory_limit: None, | ||||
|             memory_swap_limit: None, | ||||
|             cpu_shares: None, | ||||
|             restart_policy: None, | ||||
|             health_check: None, | ||||
|             detach: false, | ||||
|             snapshotter: None, | ||||
|         }); | ||||
|  | ||||
|         assert!(result.is_ok()); | ||||
|         assert_eq!(result.unwrap(), false); | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[cfg(test)] | ||||
| mod postgres_integration_tests { | ||||
|     use super::*; | ||||
|     use std::time::Duration; | ||||
|  | ||||
|     // Helper function to check if PostgreSQL is available | ||||
|     fn is_postgres_available() -> bool { | ||||
|         match get_postgres_client() { | ||||
|             Ok(_) => true, | ||||
|             Err(_) => false, | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
|     fn test_postgres_client_integration() { | ||||
|         if !is_postgres_available() { | ||||
|             println!("Skipping PostgreSQL integration tests - PostgreSQL server not available"); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         println!("Running PostgreSQL integration tests..."); | ||||
|  | ||||
|         // Test basic operations | ||||
|         test_basic_postgres_operations(); | ||||
|  | ||||
|         // Test error handling | ||||
|         test_error_handling(); | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
|     fn test_connection_pool() { | ||||
|         if !is_postgres_available() { | ||||
|             println!("Skipping PostgreSQL connection pool tests - PostgreSQL server not available"); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         run_connection_pool_test(); | ||||
|     } | ||||
|  | ||||
|     fn run_connection_pool_test() { | ||||
|         println!("Running PostgreSQL connection pool tests..."); | ||||
|  | ||||
|         // Test creating a connection pool | ||||
|         let config = PostgresConfigBuilder::new() | ||||
|             .use_pool(true) | ||||
|             .pool_max_size(5) | ||||
|             .pool_min_idle(1) | ||||
|             .pool_connection_timeout(Duration::from_secs(5)); | ||||
|  | ||||
|         let pool_result = config.build_pool(); | ||||
|         assert!(pool_result.is_ok()); | ||||
|  | ||||
|         let pool = pool_result.unwrap(); | ||||
|  | ||||
|         // Test getting a connection from the pool | ||||
|         let conn_result = pool.get(); | ||||
|         assert!(conn_result.is_ok()); | ||||
|  | ||||
|         // Test executing a query with the connection | ||||
|         let mut conn = conn_result.unwrap(); | ||||
|         let query_result = conn.query("SELECT 1", &[]); | ||||
|         assert!(query_result.is_ok()); | ||||
|  | ||||
|         // Test the global pool | ||||
|         let global_pool_result = get_postgres_pool(); | ||||
|         assert!(global_pool_result.is_ok()); | ||||
|  | ||||
|         // Test executing queries with the pool | ||||
|         let create_table_query = " | ||||
|             CREATE TEMPORARY TABLE pool_test ( | ||||
|                 id SERIAL PRIMARY KEY, | ||||
|                 name TEXT NOT NULL | ||||
|             ) | ||||
|         "; | ||||
|  | ||||
|         let create_result = execute_with_pool(create_table_query, &[]); | ||||
|         assert!(create_result.is_ok()); | ||||
|  | ||||
|         // Test with parameters | ||||
|         let insert_result = execute_with_pool( | ||||
|             "INSERT INTO pool_test (name) VALUES ($1) RETURNING id", | ||||
|             &[&"test_pool"], | ||||
|         ); | ||||
|         assert!(insert_result.is_ok()); | ||||
|  | ||||
|         // Test with QueryParams | ||||
|         let mut params = QueryParams::new(); | ||||
|         params.add_str("test_pool_params"); | ||||
|  | ||||
|         let insert_params_result = execute_with_pool_params( | ||||
|             "INSERT INTO pool_test (name) VALUES ($1) RETURNING id", | ||||
|             ¶ms, | ||||
|         ); | ||||
|         assert!(insert_params_result.is_ok()); | ||||
|  | ||||
|         // Test query functions | ||||
|         let query_result = query_with_pool("SELECT * FROM pool_test", &[]); | ||||
|         assert!(query_result.is_ok()); | ||||
|         let rows = query_result.unwrap(); | ||||
|         assert_eq!(rows.len(), 2); | ||||
|  | ||||
|         // Test query_one | ||||
|         let query_one_result = | ||||
|             query_one_with_pool("SELECT * FROM pool_test WHERE name = $1", &[&"test_pool"]); | ||||
|         assert!(query_one_result.is_ok()); | ||||
|  | ||||
|         // Test query_opt | ||||
|         let query_opt_result = | ||||
|             query_opt_with_pool("SELECT * FROM pool_test WHERE name = $1", &[&"nonexistent"]); | ||||
|         assert!(query_opt_result.is_ok()); | ||||
|         assert!(query_opt_result.unwrap().is_none()); | ||||
|  | ||||
|         // Test resetting the pool | ||||
|         let reset_result = reset_pool(); | ||||
|         assert!(reset_result.is_ok()); | ||||
|  | ||||
|         // Test getting the pool again after reset | ||||
|         let pool_after_reset = get_postgres_pool(); | ||||
|         assert!(pool_after_reset.is_ok()); | ||||
|     } | ||||
|  | ||||
|     fn test_basic_postgres_operations() { | ||||
|         if !is_postgres_available() { | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         // Create a test table | ||||
|         let create_table_query = " | ||||
|             CREATE TEMPORARY TABLE test_table ( | ||||
|                 id SERIAL PRIMARY KEY, | ||||
|                 name TEXT NOT NULL, | ||||
|                 value INTEGER | ||||
|             ) | ||||
|         "; | ||||
|  | ||||
|         let create_result = execute(create_table_query, &[]); | ||||
|         assert!(create_result.is_ok()); | ||||
|  | ||||
|         // Insert data | ||||
|         let insert_query = " | ||||
|             INSERT INTO test_table (name, value) | ||||
|             VALUES ($1, $2) | ||||
|             RETURNING id | ||||
|         "; | ||||
|  | ||||
|         let insert_result = query(insert_query, &[&"test_name", &42]); | ||||
|         assert!(insert_result.is_ok()); | ||||
|  | ||||
|         let rows = insert_result.unwrap(); | ||||
|         assert_eq!(rows.len(), 1); | ||||
|  | ||||
|         let id: i32 = rows[0].get(0); | ||||
|         assert!(id > 0); | ||||
|  | ||||
|         // Query data | ||||
|         let select_query = " | ||||
|             SELECT id, name, value | ||||
|             FROM test_table | ||||
|             WHERE id = $1 | ||||
|         "; | ||||
|  | ||||
|         let select_result = query_one(select_query, &[&id]); | ||||
|         assert!(select_result.is_ok()); | ||||
|  | ||||
|         let row = select_result.unwrap(); | ||||
|         let name: String = row.get(1); | ||||
|         let value: i32 = row.get(2); | ||||
|  | ||||
|         assert_eq!(name, "test_name"); | ||||
|         assert_eq!(value, 42); | ||||
|  | ||||
|         // Update data | ||||
|         let update_query = " | ||||
|             UPDATE test_table | ||||
|             SET value = $1 | ||||
|             WHERE id = $2 | ||||
|         "; | ||||
|  | ||||
|         let update_result = execute(update_query, &[&100, &id]); | ||||
|         assert!(update_result.is_ok()); | ||||
|         assert_eq!(update_result.unwrap(), 1); // 1 row affected | ||||
|  | ||||
|         // Verify update | ||||
|         let verify_query = " | ||||
|             SELECT value | ||||
|             FROM test_table | ||||
|             WHERE id = $1 | ||||
|         "; | ||||
|  | ||||
|         let verify_result = query_one(verify_query, &[&id]); | ||||
|         assert!(verify_result.is_ok()); | ||||
|  | ||||
|         let row = verify_result.unwrap(); | ||||
|         let updated_value: i32 = row.get(0); | ||||
|         assert_eq!(updated_value, 100); | ||||
|  | ||||
|         // Delete data | ||||
|         let delete_query = " | ||||
|             DELETE FROM test_table | ||||
|             WHERE id = $1 | ||||
|         "; | ||||
|  | ||||
|         let delete_result = execute(delete_query, &[&id]); | ||||
|         assert!(delete_result.is_ok()); | ||||
|         assert_eq!(delete_result.unwrap(), 1); // 1 row affected | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
|     fn test_query_params() { | ||||
|         if !is_postgres_available() { | ||||
|             println!("Skipping PostgreSQL parameter tests - PostgreSQL server not available"); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         run_query_params_test(); | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
|     fn test_transactions() { | ||||
|         if !is_postgres_available() { | ||||
|             println!("Skipping PostgreSQL transaction tests - PostgreSQL server not available"); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         println!("Running PostgreSQL transaction tests..."); | ||||
|  | ||||
|         // Test successful transaction | ||||
|         let result = transaction(|client| { | ||||
|             // Create a temporary table | ||||
|             client.execute( | ||||
|                 "CREATE TEMPORARY TABLE transaction_test (id SERIAL PRIMARY KEY, name TEXT NOT NULL)", | ||||
|                 &[], | ||||
|             )?; | ||||
|  | ||||
|             // Insert data | ||||
|             client.execute( | ||||
|                 "INSERT INTO transaction_test (name) VALUES ($1)", | ||||
|                 &[&"test_transaction"], | ||||
|             )?; | ||||
|  | ||||
|             // Query data | ||||
|             let rows = client.query( | ||||
|                 "SELECT * FROM transaction_test WHERE name = $1", | ||||
|                 &[&"test_transaction"], | ||||
|             )?; | ||||
|  | ||||
|             assert_eq!(rows.len(), 1); | ||||
|             let name: String = rows[0].get(1); | ||||
|             assert_eq!(name, "test_transaction"); | ||||
|  | ||||
|             // Return success | ||||
|             Ok(true) | ||||
|         }); | ||||
|  | ||||
|         assert!(result.is_ok()); | ||||
|         assert_eq!(result.unwrap(), true); | ||||
|  | ||||
|         // Test failed transaction | ||||
|         let result = transaction(|client| { | ||||
|             // Create a temporary table | ||||
|             client.execute( | ||||
|                 "CREATE TEMPORARY TABLE transaction_test_fail (id SERIAL PRIMARY KEY, name TEXT NOT NULL)", | ||||
|                 &[], | ||||
|             )?; | ||||
|  | ||||
|             // Insert data | ||||
|             client.execute( | ||||
|                 "INSERT INTO transaction_test_fail (name) VALUES ($1)", | ||||
|                 &[&"test_transaction_fail"], | ||||
|             )?; | ||||
|  | ||||
|             // Cause an error with invalid SQL | ||||
|             client.execute("THIS IS INVALID SQL", &[])?; | ||||
|  | ||||
|             // This should not be reached | ||||
|             Ok(false) | ||||
|         }); | ||||
|  | ||||
|         assert!(result.is_err()); | ||||
|  | ||||
|         // Verify that the table was not created (transaction was rolled back) | ||||
|         let verify_result = query("SELECT * FROM transaction_test_fail", &[]); | ||||
|  | ||||
|         assert!(verify_result.is_err()); | ||||
|  | ||||
|         // Test transaction with pool | ||||
|         let result = transaction_with_pool(|client| { | ||||
|             // Create a temporary table | ||||
|             client.execute( | ||||
|                 "CREATE TEMPORARY TABLE transaction_pool_test (id SERIAL PRIMARY KEY, name TEXT NOT NULL)", | ||||
|                 &[], | ||||
|             )?; | ||||
|  | ||||
|             // Insert data | ||||
|             client.execute( | ||||
|                 "INSERT INTO transaction_pool_test (name) VALUES ($1)", | ||||
|                 &[&"test_transaction_pool"], | ||||
|             )?; | ||||
|  | ||||
|             // Query data | ||||
|             let rows = client.query( | ||||
|                 "SELECT * FROM transaction_pool_test WHERE name = $1", | ||||
|                 &[&"test_transaction_pool"], | ||||
|             )?; | ||||
|  | ||||
|             assert_eq!(rows.len(), 1); | ||||
|             let name: String = rows[0].get(1); | ||||
|             assert_eq!(name, "test_transaction_pool"); | ||||
|  | ||||
|             // Return success | ||||
|             Ok(true) | ||||
|         }); | ||||
|  | ||||
|         assert!(result.is_ok()); | ||||
|         assert_eq!(result.unwrap(), true); | ||||
|     } | ||||
|  | ||||
|     fn run_query_params_test() { | ||||
|         println!("Running PostgreSQL parameter tests..."); | ||||
|  | ||||
|         // Create a test table | ||||
|         let create_table_query = " | ||||
|             CREATE TEMPORARY TABLE param_test ( | ||||
|                 id SERIAL PRIMARY KEY, | ||||
|                 name TEXT NOT NULL, | ||||
|                 value INTEGER, | ||||
|                 active BOOLEAN, | ||||
|                 score REAL | ||||
|             ) | ||||
|         "; | ||||
|  | ||||
|         let create_result = execute(create_table_query, &[]); | ||||
|         assert!(create_result.is_ok()); | ||||
|  | ||||
|         // Test QueryParams builder | ||||
|         let mut params = QueryParams::new(); | ||||
|         params.add_str("test_name"); | ||||
|         params.add_int(42); | ||||
|         params.add_bool(true); | ||||
|         params.add_float(3.14); | ||||
|  | ||||
|         // Insert data using QueryParams | ||||
|         let insert_query = " | ||||
|             INSERT INTO param_test (name, value, active, score) | ||||
|             VALUES ($1, $2, $3, $4) | ||||
|             RETURNING id | ||||
|         "; | ||||
|  | ||||
|         let insert_result = query_with_params(insert_query, ¶ms); | ||||
|         assert!(insert_result.is_ok()); | ||||
|  | ||||
|         let rows = insert_result.unwrap(); | ||||
|         assert_eq!(rows.len(), 1); | ||||
|  | ||||
|         let id: i32 = rows[0].get(0); | ||||
|         assert!(id > 0); | ||||
|  | ||||
|         // Query data using QueryParams | ||||
|         let mut query_params = QueryParams::new(); | ||||
|         query_params.add_int(id); | ||||
|  | ||||
|         let select_query = " | ||||
|             SELECT id, name, value, active, score | ||||
|             FROM param_test | ||||
|             WHERE id = $1 | ||||
|         "; | ||||
|  | ||||
|         let select_result = query_one_with_params(select_query, &query_params); | ||||
|         assert!(select_result.is_ok()); | ||||
|  | ||||
|         let row = select_result.unwrap(); | ||||
|         let name: String = row.get(1); | ||||
|         let value: i32 = row.get(2); | ||||
|         let active: bool = row.get(3); | ||||
|         let score: f64 = row.get(4); | ||||
|  | ||||
|         assert_eq!(name, "test_name"); | ||||
|         assert_eq!(value, 42); | ||||
|         assert_eq!(active, true); | ||||
|         assert_eq!(score, 3.14); | ||||
|  | ||||
|         // Test optional parameters | ||||
|         let mut update_params = QueryParams::new(); | ||||
|         update_params.add_int(100); | ||||
|         update_params.add_opt::<String>(None); | ||||
|         update_params.add_int(id); | ||||
|  | ||||
|         let update_query = " | ||||
|             UPDATE param_test | ||||
|             SET value = $1, name = COALESCE($2, name) | ||||
|             WHERE id = $3 | ||||
|         "; | ||||
|  | ||||
|         let update_result = execute_with_params(update_query, &update_params); | ||||
|         assert!(update_result.is_ok()); | ||||
|         assert_eq!(update_result.unwrap(), 1); // 1 row affected | ||||
|  | ||||
|         // Verify update | ||||
|         let verify_result = query_one_with_params(select_query, &query_params); | ||||
|         assert!(verify_result.is_ok()); | ||||
|  | ||||
|         let row = verify_result.unwrap(); | ||||
|         let name: String = row.get(1); | ||||
|         let value: i32 = row.get(2); | ||||
|  | ||||
|         assert_eq!(name, "test_name"); // Name should be unchanged | ||||
|         assert_eq!(value, 100); // Value should be updated | ||||
|  | ||||
|         // Test query_opt_with_params | ||||
|         let mut nonexistent_params = QueryParams::new(); | ||||
|         nonexistent_params.add_int(9999); // ID that doesn't exist | ||||
|  | ||||
|         let opt_query = " | ||||
|             SELECT id, name | ||||
|             FROM param_test | ||||
|             WHERE id = $1 | ||||
|         "; | ||||
|  | ||||
|         let opt_result = query_opt_with_params(opt_query, &nonexistent_params); | ||||
|         assert!(opt_result.is_ok()); | ||||
|         assert!(opt_result.unwrap().is_none()); | ||||
|  | ||||
|         // Clean up | ||||
|         let delete_query = " | ||||
|             DELETE FROM param_test | ||||
|             WHERE id = $1 | ||||
|         "; | ||||
|  | ||||
|         let delete_result = execute_with_params(delete_query, &query_params); | ||||
|         assert!(delete_result.is_ok()); | ||||
|         assert_eq!(delete_result.unwrap(), 1); // 1 row affected | ||||
|     } | ||||
|  | ||||
|     fn test_error_handling() { | ||||
|         if !is_postgres_available() { | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         // Test invalid SQL | ||||
|         let invalid_query = "SELECT * FROM nonexistent_table"; | ||||
|         let invalid_result = query(invalid_query, &[]); | ||||
|         assert!(invalid_result.is_err()); | ||||
|  | ||||
|         // Test parameter type mismatch | ||||
|         let mismatch_query = "SELECT $1::integer"; | ||||
|         let mismatch_result = query(mismatch_query, &[&"not_an_integer"]); | ||||
|         assert!(mismatch_result.is_err()); | ||||
|  | ||||
|         // Test query_one with no results | ||||
|         let empty_query = "SELECT * FROM pg_tables WHERE tablename = 'nonexistent_table'"; | ||||
|         let empty_result = query_one(empty_query, &[]); | ||||
|         assert!(empty_result.is_err()); | ||||
|  | ||||
|         // Test query_opt with no results | ||||
|         let opt_query = "SELECT * FROM pg_tables WHERE tablename = 'nonexistent_table'"; | ||||
|         let opt_result = query_opt(opt_query, &[]); | ||||
|         assert!(opt_result.is_ok()); | ||||
|         assert!(opt_result.unwrap().is_none()); | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
|     fn test_notify() { | ||||
|         if !is_postgres_available() { | ||||
|             println!("Skipping PostgreSQL notification tests - PostgreSQL server not available"); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         println!("Running PostgreSQL notification tests..."); | ||||
|  | ||||
|         // Test sending a notification | ||||
|         let result = notify("test_channel", "test_payload"); | ||||
|         assert!(result.is_ok()); | ||||
|  | ||||
|         // Test sending a notification with the pool | ||||
|         let result = notify_with_pool("test_channel_pool", "test_payload_pool"); | ||||
|         assert!(result.is_ok()); | ||||
|     } | ||||
| } | ||||
		Reference in New Issue
	
	Block a user