Implement PostgreSQL Installer Module for Rhai #6
@@ -168,6 +168,20 @@ pub fn install_postgres(
 | 
			
		||||
    // Build the image name
 | 
			
		||||
    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
 | 
			
		||||
    let mut container = Container::new(&config.container_name).map_err(|e| {
 | 
			
		||||
        PostgresInstallerError::NerdctlError(format!("Failed to create container: {}", e))
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,5 @@
 | 
			
		||||
use super::*;
 | 
			
		||||
use std::collections::HashMap;
 | 
			
		||||
use std::env;
 | 
			
		||||
 | 
			
		||||
#[cfg(test)]
 | 
			
		||||
@@ -134,6 +135,234 @@ mod postgres_client_tests {
 | 
			
		||||
 | 
			
		||||
// 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::*;
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user