Some checks failed
Rhai Tests / Run Rhai Tests (pull_request) Has been cancelled
- Pull the PostgreSQL image before installation to ensure the latest version is used. This improves reliability and reduces the chance of using outdated images. Improves the robustness of the installation process. - Added comprehensive unit tests for `PostgresInstallerConfig`, `PostgresInstallerError`, `install_postgres`, `create_database`, `execute_sql`, and `is_postgres_running` functions to ensure correctness and handle potential errors effectively. Improves code quality and reduces the risk of regressions.
844 lines
27 KiB
Rust
844 lines
27 KiB
Rust
use super::*;
|
|
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 crate::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());
|
|
}
|
|
}
|