#![cfg(feature = "testing")]
use docker_wrapper::template::HasConnectionString;
use docker_wrapper::testing::{ContainerGuard, ContainerGuardSet};
use docker_wrapper::{RedisTemplate, Template};
use std::sync::atomic::{AtomicU16, Ordering};
use std::time::Duration;
static PORT_COUNTER: AtomicU16 = AtomicU16::new(17000);
fn unique_name(prefix: &str) -> String {
format!("{}-{}", prefix, uuid::Uuid::new_v4())
}
fn next_port() -> u16 {
PORT_COUNTER.fetch_add(1, Ordering::SeqCst)
}
#[tokio::test]
async fn test_container_guard_basic_lifecycle() {
let name = unique_name("guard-basic");
let guard = ContainerGuard::new(RedisTemplate::new(&name).port(next_port()))
.start()
.await
.expect("Failed to start container");
assert!(
guard.is_running().await.expect("Failed to check running"),
"Container should be running"
);
assert!(guard.container_id().is_some(), "Should have container ID");
assert!(!guard.was_reused(), "Should not be reused");
let template = guard.template();
assert_eq!(template.config().name, name);
drop(guard);
tokio::time::sleep(Duration::from_millis(500)).await;
let template = RedisTemplate::new(&name);
assert!(
!template.is_running().await.unwrap_or(true),
"Container should be stopped after drop"
);
}
#[tokio::test]
async fn test_container_guard_no_remove_on_drop() {
let name = unique_name("guard-no-remove");
let guard = ContainerGuard::new(RedisTemplate::new(&name).port(next_port()))
.remove_on_drop(false)
.stop_on_drop(false)
.start()
.await
.expect("Failed to start container");
assert!(guard.is_running().await.expect("Failed to check running"));
drop(guard);
tokio::time::sleep(Duration::from_millis(500)).await;
let template = RedisTemplate::new(&name);
assert!(
template.is_running().await.unwrap_or(false),
"Container should still be running"
);
let _ = template.stop().await;
let _ = template.remove().await;
}
#[tokio::test]
async fn test_container_guard_reuse_if_running() {
let name = unique_name("guard-reuse");
let port = next_port();
let guard1 = ContainerGuard::new(RedisTemplate::new(&name).port(port))
.remove_on_drop(false)
.stop_on_drop(false)
.start()
.await
.expect("Failed to start container");
assert!(!guard1.was_reused(), "First start should not be reused");
let container_id = guard1.container_id().map(String::from);
drop(guard1);
let guard2 = ContainerGuard::new(RedisTemplate::new(&name).port(port))
.reuse_if_running(true)
.start()
.await
.expect("Failed to start/reuse container");
assert!(guard2.was_reused(), "Second start should reuse");
assert!(
guard2.container_id().is_none(),
"Reused container should not have ID"
);
drop(guard2);
let template = RedisTemplate::new(&name);
let _ = template.stop().await;
let _ = template.remove().await;
assert!(container_id.is_some(), "Original container should have ID");
}
#[tokio::test]
async fn test_container_guard_logs() {
let name = unique_name("guard-logs");
let guard = ContainerGuard::new(RedisTemplate::new(&name).port(next_port()))
.start()
.await
.expect("Failed to start container");
tokio::time::sleep(Duration::from_secs(1)).await;
let logs = guard.logs().await.expect("Failed to get logs");
assert!(!logs.is_empty(), "Logs should not be empty");
}
#[tokio::test]
async fn test_container_guard_manual_cleanup() {
let name = unique_name("guard-cleanup");
let guard = ContainerGuard::new(RedisTemplate::new(&name).port(next_port()))
.start()
.await
.expect("Failed to start container");
assert!(guard.is_running().await.expect("Failed to check running"));
guard.cleanup().await.expect("Failed to cleanup");
guard
.cleanup()
.await
.expect("Second cleanup should succeed");
tokio::time::sleep(Duration::from_millis(500)).await;
let template = RedisTemplate::new(&name);
assert!(
!template.is_running().await.unwrap_or(true),
"Container should be stopped after cleanup"
);
}
#[tokio::test]
async fn test_container_guard_wait_for_ready_method() {
let name = unique_name("guard-wait-ready");
let guard = ContainerGuard::new(RedisTemplate::new(&name).port(next_port()))
.start()
.await
.expect("Failed to start container");
guard
.wait_for_ready()
.await
.expect("Failed to wait for ready");
assert!(
guard.is_running().await.expect("Failed to check running"),
"Container should be running"
);
}
#[tokio::test]
async fn test_container_guard_auto_wait_for_ready() {
let name = unique_name("guard-auto-wait");
let guard = ContainerGuard::new(RedisTemplate::new(&name).port(next_port()))
.wait_for_ready(true) .start()
.await
.expect("Failed to start container");
assert!(
guard.is_running().await.expect("Failed to check running"),
"Container should be running"
);
guard
.wait_for_ready()
.await
.expect("wait_for_ready should succeed on already-ready container");
}
#[tokio::test]
async fn test_container_guard_with_network() {
let name = unique_name("guard-network");
let network_name = unique_name("test-network");
let guard = ContainerGuard::new(RedisTemplate::new(&name).port(next_port()))
.with_network(&network_name)
.remove_network_on_drop(true)
.start()
.await
.expect("Failed to start container");
assert!(
guard.is_running().await.expect("Failed to check running"),
"Container should be running"
);
assert_eq!(guard.network(), Some(network_name.as_str()));
assert_eq!(
guard.template().config().network,
Some(network_name.clone())
);
}
#[tokio::test]
async fn test_container_guard_network_no_auto_create() {
use docker_wrapper::{DockerCommand, NetworkCreateCommand, NetworkRmCommand};
let name = unique_name("guard-network-manual");
let network_name = unique_name("manual-network");
NetworkCreateCommand::new(&network_name)
.driver("bridge")
.execute()
.await
.expect("Failed to create network");
let guard = ContainerGuard::new(RedisTemplate::new(&name).port(next_port()))
.with_network(&network_name)
.create_network(false) .start()
.await
.expect("Failed to start container");
assert!(
guard.is_running().await.expect("Failed to check running"),
"Container should be running"
);
drop(guard);
tokio::time::sleep(Duration::from_millis(500)).await;
let _ = NetworkRmCommand::new(&network_name).execute().await;
}
#[tokio::test]
async fn test_container_guard_set_basic() {
let name1 = unique_name("guardset-1");
let name2 = unique_name("guardset-2");
let guards = ContainerGuardSet::new()
.add(RedisTemplate::new(&name1).port(next_port()))
.add(RedisTemplate::new(&name2).port(next_port()))
.start_all()
.await
.expect("Failed to start containers");
assert_eq!(guards.len(), 2);
assert!(!guards.is_empty());
assert!(guards.contains(&name1));
assert!(guards.contains(&name2));
let names: Vec<&str> = guards.names().collect();
assert!(names.contains(&name1.as_str()));
assert!(names.contains(&name2.as_str()));
}
#[tokio::test]
async fn test_container_guard_set_with_shared_network() {
let name1 = unique_name("guardset-net-1");
let name2 = unique_name("guardset-net-2");
let network_name = unique_name("guardset-network");
let guards = ContainerGuardSet::new()
.with_network(&network_name)
.add(RedisTemplate::new(&name1).port(next_port()))
.add(RedisTemplate::new(&name2).port(next_port()))
.start_all()
.await
.expect("Failed to start containers");
assert_eq!(guards.len(), 2);
assert_eq!(guards.network(), Some(network_name.as_str()));
}
#[tokio::test]
async fn test_container_guard_set_empty() {
let guards = ContainerGuardSet::new()
.start_all()
.await
.expect("Empty set should start successfully");
assert!(guards.is_empty());
assert_eq!(guards.len(), 0);
assert!(!guards.contains("nonexistent"));
}
#[tokio::test]
async fn test_container_guard_set_single_container() {
let name = unique_name("guardset-single");
let guards = ContainerGuardSet::new()
.add(RedisTemplate::new(&name).port(next_port()))
.start_all()
.await
.expect("Failed to start container");
assert_eq!(guards.len(), 1);
assert!(guards.contains(&name));
}
#[tokio::test]
async fn test_container_guard_set_network_no_auto_remove() {
use docker_wrapper::{DockerCommand, NetworkRmCommand};
let name = unique_name("guardset-net-keep");
let network_name = unique_name("guardset-keep-network");
{
let guards = ContainerGuardSet::new()
.with_network(&network_name)
.remove_network_on_drop(false) .add(RedisTemplate::new(&name).port(next_port()))
.start_all()
.await
.expect("Failed to start container");
assert_eq!(guards.len(), 1);
}
tokio::time::sleep(Duration::from_millis(500)).await;
let _ = NetworkRmCommand::new(&network_name).execute().await;
}
#[tokio::test]
async fn test_container_guard_set_wait_for_ready_disabled() {
let name = unique_name("guardset-no-wait");
let guards = ContainerGuardSet::new()
.wait_for_ready(false) .add(RedisTemplate::new(&name).port(next_port()))
.start_all()
.await
.expect("Failed to start container");
assert_eq!(guards.len(), 1);
assert!(guards.contains(&name));
}
#[tokio::test]
async fn test_container_guard_stop_timeout() {
let name = unique_name("guard-stop-timeout");
let guard = ContainerGuard::new(RedisTemplate::new(&name).port(next_port()))
.stop_timeout(Duration::from_secs(1))
.start()
.await
.expect("Failed to start container");
assert!(
guard.is_running().await.expect("Failed to check running"),
"Container should be running"
);
}
#[tokio::test]
async fn test_container_guard_stop_timeout_zero() {
let name = unique_name("guard-stop-timeout-zero");
let guard = ContainerGuard::new(RedisTemplate::new(&name).port(next_port()))
.stop_timeout(Duration::ZERO)
.start()
.await
.expect("Failed to start container");
assert!(
guard.is_running().await.expect("Failed to check running"),
"Container should be running"
);
}
#[tokio::test]
async fn test_container_guard_connection_string() {
let name = unique_name("guard-conn-string");
let port = next_port();
let guard = ContainerGuard::new(RedisTemplate::new(&name).port(port))
.start()
.await
.expect("Failed to start container");
let conn = guard.connection_string();
assert_eq!(conn, format!("redis://localhost:{}", port));
assert_eq!(conn, guard.template().connection_string());
}
#[tokio::test]
async fn test_container_guard_connection_string_with_password() {
let name = unique_name("guard-conn-string-pass");
let port = next_port();
let guard = ContainerGuard::new(RedisTemplate::new(&name).port(port).password("testpass123"))
.start()
.await
.expect("Failed to start container");
let conn = guard.connection_string();
assert_eq!(conn, format!("redis://:testpass123@localhost:{}", port));
}