docker-wrapper 0.11.1

A Docker CLI wrapper for Rust
Documentation
//! Integration tests for Redis Cluster template
//!
//! These tests verify that the Redis Cluster template API works correctly
//! and can generate proper configurations for Redis clusters.

#![cfg(feature = "template-redis-cluster")]

use docker_wrapper::{RedisClusterConnection, RedisClusterTemplate, Template};
use std::time::Duration;
use tokio::time::timeout;

const TEST_TIMEOUT: Duration = Duration::from_secs(180);

/// Test basic Redis Cluster template API
#[tokio::test]
async fn test_redis_cluster_basic_api() {
    let cluster_name = format!("test-cluster-basic-{}", uuid::Uuid::new_v4());

    let cluster = RedisClusterTemplate::new(&cluster_name)
        .num_masters(3)
        .port_base(8100);

    // Test connection info generation without starting
    let conn = RedisClusterConnection::from_template(&cluster);
    let nodes_string = conn.nodes_string();
    assert!(nodes_string.contains("8100"), "Should contain base port");
    assert!(nodes_string.contains("8101"), "Should contain second port");
    assert!(nodes_string.contains("8102"), "Should contain third port");

    let cluster_url = conn.cluster_url();
    assert!(
        cluster_url.starts_with("redis-cluster://"),
        "Should generate valid cluster URL, got: {}",
        cluster_url
    );
}

/// Test Redis Cluster with replicas - API only
#[tokio::test]
async fn test_redis_cluster_with_replicas_api() {
    let cluster_name = format!("test-cluster-replicas-{}", uuid::Uuid::new_v4());

    let cluster = RedisClusterTemplate::new(&cluster_name)
        .num_masters(3)
        .num_replicas(1) // 1 replica per master = 6 total nodes
        .port_base(8200);

    // Verify connection info includes all nodes (masters + replicas)
    let conn = RedisClusterConnection::from_template(&cluster);
    let nodes_string = conn.nodes_string();

    // Should have 6 nodes total (3 masters + 3 replicas)
    let node_count = nodes_string.matches("8").count(); // Count port occurrences
    assert!(node_count >= 6, "Should have at least 6 nodes in cluster");
}

/// Test Redis Cluster with authentication - API only
#[tokio::test]
async fn test_redis_cluster_with_password_api() {
    let cluster_name = format!("test-cluster-auth-{}", uuid::Uuid::new_v4());
    let test_password = "secure-cluster-password";

    let cluster = RedisClusterTemplate::new(&cluster_name)
        .num_masters(3)
        .port_base(8300)
        .password(test_password);

    // Verify password is included in connection URL
    let conn = RedisClusterConnection::from_template(&cluster);
    let cluster_url = conn.cluster_url();
    assert!(
        cluster_url.contains(test_password),
        "Cluster URL should include password"
    );
}

/// Test Redis Cluster with persistence - API only
#[tokio::test]
async fn test_redis_cluster_with_persistence_api() {
    let cluster_name = format!("test-cluster-persist-{}", uuid::Uuid::new_v4());
    let data_volume = format!("{}-data", cluster_name);

    let cluster = RedisClusterTemplate::new(&cluster_name)
        .num_masters(3)
        .port_base(8400)
        .with_persistence(&data_volume);

    // Test that the configuration was applied (API test)
    let conn = RedisClusterConnection::from_template(&cluster);
    let nodes_string = conn.nodes_string();
    assert!(
        nodes_string.contains("8400"),
        "Should contain configured port"
    );
}

/// Test Redis Stack cluster functionality - API only
#[tokio::test]
async fn test_redis_stack_cluster_api() {
    let cluster_name = format!("test-stack-cluster-{}", uuid::Uuid::new_v4());

    let cluster = RedisClusterTemplate::new(&cluster_name)
        .num_masters(3)
        .port_base(8500)
        .with_redis_stack(); // Enable Redis Stack modules

    // Test API configuration
    let conn = RedisClusterConnection::from_template(&cluster);
    let nodes_string = conn.nodes_string();
    assert!(
        nodes_string.contains("8500"),
        "Should contain configured port"
    );
}

/// Test custom image and platform support - API only
#[tokio::test]
async fn test_redis_cluster_custom_image_platform_api() {
    let cluster_name = format!("test-cluster-custom-{}", uuid::Uuid::new_v4());

    let cluster = RedisClusterTemplate::new(&cluster_name)
        .num_masters(3)
        .port_base(8600)
        .custom_redis_image("redis", "7-alpine")
        .platform("linux/amd64");

    // Test API configuration
    let conn = RedisClusterConnection::from_template(&cluster);
    let nodes_string = conn.nodes_string();
    assert!(
        nodes_string.contains("8600"),
        "Should contain configured port"
    );
}

/// Test error handling for invalid configurations - API only
#[tokio::test]
async fn test_redis_cluster_error_handling_api() {
    let cluster_name = format!("test-cluster-error-{}", uuid::Uuid::new_v4());

    // Test with invalid configuration - this should not panic during construction
    let cluster = RedisClusterTemplate::new(&cluster_name)
        .num_masters(1000) // Large number but should not cause overflow
        .port_base(8900);

    // The API should still work for connection info generation
    let conn = RedisClusterConnection::from_template(&cluster);
    let cluster_url = conn.cluster_url();
    assert!(cluster_url.contains("8900"), "Should contain base port");
}

/// Test cluster builder pattern and method chaining - API only
#[tokio::test]
async fn test_redis_cluster_builder_pattern_api() {
    let cluster_name = format!("test-cluster-builder-{}", uuid::Uuid::new_v4());

    // Test comprehensive builder chain
    let cluster = RedisClusterTemplate::new(&cluster_name)
        .num_masters(3)
        .num_replicas(1)
        .port_base(8800)
        .password("builder-password")
        .cluster_announce_ip("127.0.0.1")
        .cluster_node_timeout(15000)
        .memory_limit("128m")
        .auto_remove();

    // Verify builder created proper configuration
    let conn = RedisClusterConnection::from_template(&cluster);
    let cluster_url = conn.cluster_url();

    assert!(
        cluster_url.contains("builder-password"),
        "Should include configured password"
    );
    assert!(
        cluster_url.contains("8800"),
        "Should include configured port"
    );
}

/// Test actual Redis Cluster container creation (single integration test)
/// This test is more likely to fail in CI due to Docker limitations, but provides
/// a basic smoke test for actual container functionality.
#[tokio::test]
#[ignore] // Ignore by default since it requires Docker
async fn test_redis_cluster_container_smoke_test() {
    let cluster_name = format!("test-cluster-smoke-{}", uuid::Uuid::new_v4());

    let cluster = RedisClusterTemplate::new(&cluster_name)
        .num_masters(3)
        .port_base(9100)
        .auto_remove();

    // Try to start the cluster - if this fails due to Docker issues in CI,
    // the test will be ignored and won't fail the build
    match timeout(TEST_TIMEOUT, cluster.start()).await {
        Ok(Ok(result)) => {
            assert!(!result.is_empty(), "Cluster result should not be empty");

            // Clean up on success
            let _ = timeout(Duration::from_secs(30), cluster.stop()).await;
            let _ = timeout(Duration::from_secs(30), cluster.remove()).await;
        }
        Ok(Err(_)) | Err(_) => {
            // Expected in environments where Docker/Redis isn't available
            // This test provides value when Docker is available but doesn't fail CI
            println!("Cluster smoke test skipped - Docker/Redis not available in test environment");
        }
    }
}