#[cfg(any(
feature = "template-postgres",
feature = "template-mysql",
feature = "template-mongodb"
))]
mod database_template_tests {
#[allow(unused_imports)]
use docker_wrapper::{DockerCommand, Template};
#[allow(unused_imports)]
use std::time::Duration;
#[allow(unused_imports)]
use tokio::time::sleep;
fn test_container_name(db: &str, suffix: &str) -> String {
format!("test-{}-template-{}-{}", db, suffix, uuid::Uuid::new_v4())
}
#[allow(dead_code)]
fn random_port() -> u16 {
40000 + (uuid::Uuid::new_v4().as_u128() % 10000) as u16
}
#[cfg(feature = "template-postgres")]
mod postgres_tests {
use super::*;
use docker_wrapper::{PostgresConnectionString, PostgresTemplate};
#[tokio::test]
async fn test_postgres_basic_start_stop() -> Result<(), Box<dyn std::error::Error>> {
let name = test_container_name("postgres", "basic");
let postgres = PostgresTemplate::new(&name)
.port(random_port())
.database("testdb")
.user("testuser")
.password("testpass");
let container_id = postgres.start_and_wait().await?;
assert!(!container_id.is_empty());
assert!(postgres.is_running().await?);
let conn = PostgresConnectionString::from_template(&postgres);
assert!(conn.url().contains("testdb"));
assert!(conn.url().contains("testuser"));
let result = postgres
.exec(vec![
"psql",
"-U",
"testuser",
"-d",
"testdb",
"-c",
"SELECT version();",
])
.await?;
assert!(result.stdout.contains("PostgreSQL"));
postgres.stop().await?;
sleep(Duration::from_millis(500)).await;
assert!(!postgres.is_running().await?);
postgres.remove().await?;
Ok(())
}
#[tokio::test]
async fn test_postgres_with_persistence() -> Result<(), Box<dyn std::error::Error>> {
let name = test_container_name("postgres", "persistence");
let volume_name = format!("{}-data", name);
let postgres = PostgresTemplate::new(&name)
.database("persistdb")
.user("persistuser")
.password("persistpass")
.with_persistence(&volume_name);
let _container_id = postgres.start_and_wait().await?;
postgres
.exec(vec![
"psql",
"-U",
"persistuser",
"-d",
"persistdb",
"-c",
"CREATE TABLE test_table (id INT PRIMARY KEY, data TEXT);",
])
.await?;
postgres
.exec(vec![
"psql",
"-U",
"persistuser",
"-d",
"persistdb",
"-c",
"INSERT INTO test_table VALUES (1, 'test_data');",
])
.await?;
postgres.stop().await?;
postgres.remove().await?;
let postgres2 = PostgresTemplate::new(format!("{}-2", name))
.database("persistdb")
.user("persistuser")
.password("persistpass")
.with_persistence(&volume_name);
let _container_id2 = postgres2.start_and_wait().await?;
let result = postgres2
.exec(vec![
"psql",
"-U",
"persistuser",
"-d",
"persistdb",
"-c",
"SELECT data FROM test_table WHERE id = 1;",
])
.await?;
assert!(result.stdout.contains("test_data"));
postgres2.stop().await?;
postgres2.remove().await?;
use docker_wrapper::VolumeRmCommand;
VolumeRmCommand::new(&volume_name).force().execute().await?;
Ok(())
}
#[tokio::test]
async fn test_postgres_init_script() -> Result<(), Box<dyn std::error::Error>> {
let name = test_container_name("postgres", "init");
use std::io::Write;
let temp_dir = tempfile::tempdir()?;
let init_dir = temp_dir.path().join("initdb.d");
std::fs::create_dir(&init_dir)?;
let init_file = init_dir.join("init.sql");
let mut file = std::fs::File::create(&init_file)?;
writeln!(file, "CREATE TABLE init_test (id INT PRIMARY KEY);")?;
writeln!(file, "INSERT INTO init_test VALUES (42);")?;
file.sync_all()?;
let postgres = PostgresTemplate::new(&name)
.port(random_port())
.database("initdb")
.user("inituser")
.password("initpass")
.init_scripts(init_dir.to_str().unwrap());
let _container_id = postgres.start_and_wait().await?;
sleep(Duration::from_secs(2)).await;
let result = postgres
.exec(vec![
"psql",
"-U",
"inituser",
"-d",
"initdb",
"-c",
"SELECT id FROM init_test WHERE id = 42;",
])
.await?;
assert!(result.stdout.contains("42"));
postgres.stop().await?;
postgres.remove().await?;
Ok(())
}
}
#[cfg(feature = "template-mysql")]
mod mysql_tests {
use super::*;
use docker_wrapper::{MysqlConnectionString, MysqlTemplate};
#[tokio::test]
async fn test_mysql_basic_start_stop() -> Result<(), Box<dyn std::error::Error>> {
let name = test_container_name("mysql", "basic");
let mysql = MysqlTemplate::new(&name)
.port(random_port())
.database("testdb")
.user("testuser")
.password("testpass")
.root_password("rootpass");
let container_id = mysql.start_and_wait().await?;
assert!(!container_id.is_empty());
assert!(mysql.is_running().await?);
let conn = MysqlConnectionString::from_template(&mysql);
assert!(conn.url().contains("testdb"));
assert!(conn.url().contains("testuser"));
let result = mysql
.exec(vec![
"mysql",
"-h",
"127.0.0.1",
"-u",
"root",
"-prootpass",
"-e",
"SELECT VERSION();",
])
.await?;
assert!(
!result.stdout.is_empty(),
"MySQL version query should return output"
);
mysql.stop().await?;
sleep(Duration::from_millis(500)).await;
assert!(!mysql.is_running().await?);
mysql.remove().await?;
Ok(())
}
}
#[cfg(feature = "template-mongodb")]
mod mongodb_tests {
use super::*;
use docker_wrapper::{MongodbConnectionString, MongodbTemplate};
#[tokio::test]
async fn test_mongodb_basic_start_stop() -> Result<(), Box<dyn std::error::Error>> {
let name = test_container_name("mongodb", "basic");
let mongodb = MongodbTemplate::new(&name)
.port(random_port())
.database("testdb");
let container_id = mongodb.start_and_wait().await?;
assert!(!container_id.is_empty());
assert!(mongodb.is_running().await?);
let mongo_cmd = if mongodb.config().tag.starts_with("4.") {
"mongo"
} else {
"mongosh"
};
let result = mongodb
.exec(vec![
mongo_cmd,
"--host",
"localhost",
"--eval",
"db.runCommand({ ping: 1 })",
"--quiet",
])
.await?;
assert!(result.stdout.contains("ok") && result.stdout.contains("1"));
mongodb.stop().await?;
sleep(Duration::from_millis(500)).await;
assert!(!mongodb.is_running().await?);
mongodb.remove().await?;
Ok(())
}
#[tokio::test]
async fn test_mongodb_with_auth() -> Result<(), Box<dyn std::error::Error>> {
let name = test_container_name("mongodb", "auth");
let mongodb = MongodbTemplate::new(&name)
.port(random_port())
.root_username("admin")
.root_password("adminpass")
.database("authdb")
.with_auth();
let _container_id = mongodb.start_and_wait().await?;
let conn = MongodbConnectionString::from_template(&mongodb);
assert!(conn.url().contains("admin:adminpass"));
mongodb.stop().await?;
mongodb.remove().await?;
Ok(())
}
#[tokio::test]
async fn test_mongodb_replica_set() -> Result<(), Box<dyn std::error::Error>> {
let name = test_container_name("mongodb", "replica");
let mongodb = MongodbTemplate::new(&name)
.port(random_port())
.replica_set("rs0");
let _container_id = mongodb.start_and_wait().await?;
use docker_wrapper::InspectCommand;
let inspect = InspectCommand::new(&name).execute().await?;
let containers: serde_json::Value = serde_json::from_str(&inspect.stdout)?;
if let Some(first) = containers.as_array().and_then(|arr| arr.first()) {
if let Some(config) = first.get("Config") {
if let Some(cmd) = config.get("Cmd").and_then(|c| c.as_array()) {
let cmd_str = cmd
.iter()
.filter_map(|v| v.as_str())
.collect::<Vec<_>>()
.join(" ");
assert!(
cmd_str.contains("--replSet rs0"),
"MongoDB should be started with replica set parameter"
);
}
}
}
mongodb.stop().await?;
mongodb.remove().await?;
Ok(())
}
}
}