Some checks are pending
Rhai Tests / Run Rhai Tests (push) Waiting to run
- Move src/postgresclient/ to postgresclient/ package structure - Add comprehensive test suite (28 tests) with real PostgreSQL operations - Maintain Rhai integration with all 10 wrapper functions - Update workspace configuration and dependencies - Add complete documentation with usage examples - Remove old module and update all references - Ensure zero regressions in existing functionality Closes: postgresclient monorepo conversion
844 lines
27 KiB
Rust
844 lines
27 KiB
Rust
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());
|
|
}
|
|
}
|