feat: Enhance PostgreSQL installation with image pulling
Some checks failed
Rhai Tests / Run Rhai Tests (pull_request) Has been cancelled
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.
This commit is contained in:
parent
49e85ff8e6
commit
138dce66fa
@ -168,6 +168,20 @@ pub fn install_postgres(
|
|||||||
// Build the image name
|
// Build the image name
|
||||||
let image = format!("postgres:{}", config.version);
|
let image = format!("postgres:{}", config.version);
|
||||||
|
|
||||||
|
// Pull the PostgreSQL image to ensure we have the latest version
|
||||||
|
println!("Pulling PostgreSQL image: {}...", image);
|
||||||
|
let pull_result = Command::new("nerdctl")
|
||||||
|
.args(&["pull", &image])
|
||||||
|
.output()
|
||||||
|
.map_err(|e| PostgresInstallerError::IoError(e))?;
|
||||||
|
|
||||||
|
if !pull_result.status.success() {
|
||||||
|
return Err(PostgresInstallerError::NerdctlError(format!(
|
||||||
|
"Failed to pull PostgreSQL image: {}",
|
||||||
|
String::from_utf8_lossy(&pull_result.stderr)
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
// Create the container
|
// Create the container
|
||||||
let mut container = Container::new(&config.container_name).map_err(|e| {
|
let mut container = Container::new(&config.container_name).map_err(|e| {
|
||||||
PostgresInstallerError::NerdctlError(format!("Failed to create container: {}", e))
|
PostgresInstallerError::NerdctlError(format!("Failed to create container: {}", e))
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
use super::*;
|
use super::*;
|
||||||
|
use std::collections::HashMap;
|
||||||
use std::env;
|
use std::env;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@ -134,6 +135,234 @@ mod postgres_client_tests {
|
|||||||
|
|
||||||
// Integration tests that require a real PostgreSQL server
|
// Integration tests that require a real PostgreSQL server
|
||||||
// These tests will be skipped if PostgreSQL is not available
|
// 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)]
|
#[cfg(test)]
|
||||||
mod postgres_integration_tests {
|
mod postgres_integration_tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
Loading…
Reference in New Issue
Block a user