#![cfg(feature = "blocking")]
use testcontainers::{
core::{
logs::{consumer::logging_consumer::LoggingConsumer, LogFrame},
wait::LogWaitStrategy,
CmdWaitFor, ExecCommand, Host, IntoContainerPort, WaitFor,
},
runners::SyncRunner,
*,
};
fn get_server_container(msg: Option<WaitFor>) -> GenericImage {
let msg = msg.unwrap_or(WaitFor::message_on_stdout("server is ready"));
GenericImage::new("simple_web_server", "latest").with_wait_for(msg)
}
#[derive(Debug, Default)]
pub struct HelloWorld;
impl Image for HelloWorld {
fn name(&self) -> &str {
"hello-world"
}
fn tag(&self) -> &str {
"latest"
}
fn ready_conditions(&self) -> Vec<WaitFor> {
vec![WaitFor::message_on_stdout("Hello from Docker!")]
}
}
#[test]
fn sync_can_run_hello_world() -> anyhow::Result<()> {
let _ = pretty_env_logger::try_init();
let _container = HelloWorld.start()?;
Ok(())
}
#[cfg(feature = "http_wait")]
#[test]
fn sync_wait_for_http() -> anyhow::Result<()> {
use crate::core::wait::HttpWaitStrategy;
let _ = pretty_env_logger::try_init();
use reqwest::StatusCode;
let image = GenericImage::new("simple_web_server", "latest")
.with_exposed_port(80.tcp())
.with_wait_for(WaitFor::http(
HttpWaitStrategy::new("/").with_expected_status_code(StatusCode::OK),
));
let _container = image.start()?;
Ok(())
}
#[test]
fn generic_image_with_custom_entrypoint() -> anyhow::Result<()> {
let generic = get_server_container(None);
let node = generic.start()?;
let port = node.get_host_port_ipv4(80.tcp())?;
assert_eq!(
"foo",
reqwest::blocking::get(format!("http://{}:{port}", node.get_host()?))?.text()?
);
let generic = get_server_container(None).with_entrypoint("./bar");
let node = generic.start()?;
let port = node.get_host_port_ipv4(80.tcp())?;
assert_eq!(
"bar",
reqwest::blocking::get(format!("http://{}:{port}", node.get_host()?))?.text()?
);
Ok(())
}
#[test]
fn generic_image_exposed_ports() -> anyhow::Result<()> {
let _ = pretty_env_logger::try_init();
let target_port = 8080;
let generic_server = GenericImage::new("no_expose_port", "latest")
.with_wait_for(WaitFor::message_on_stdout("listening on 0.0.0.0:8080"))
.with_exposed_port(target_port.tcp());
let node = generic_server.start()?;
let port = node.get_host_port_ipv4(target_port.tcp())?;
assert!(reqwest::blocking::get(format!("http://127.0.0.1:{port}"))?
.status()
.is_success());
Ok(())
}
#[test]
fn generic_image_running_with_extra_hosts_added() -> anyhow::Result<()> {
let server_1 = get_server_container(None);
let node = server_1.start()?;
let port = node.get_host_port_ipv4(80.tcp())?;
let msg = WaitFor::message_on_stdout("foo");
let server_2 = GenericImage::new("curlimages/curl", "latest")
.with_wait_for(msg)
.with_entrypoint("curl");
let server_2 = server_2
.with_cmd([format!("http://custom-host:{port}")])
.with_host("custom-host", Host::HostGateway);
server_2.start()?;
Ok(())
}
#[test]
fn generic_image_port_not_exposed() -> anyhow::Result<()> {
let _ = pretty_env_logger::try_init();
let target_port = 8080;
let generic_server = GenericImage::new("no_expose_port", "latest")
.with_wait_for(WaitFor::message_on_stdout("listening on 0.0.0.0:8080"));
let node = generic_server.start()?;
let res = node.get_host_port_ipv4(target_port.tcp());
assert!(res.is_err());
Ok(())
}
#[test]
fn start_multiple_containers() -> anyhow::Result<()> {
let _ = pretty_env_logger::try_init();
let image = GenericImage::new("hello-world", "latest").with_wait_for(WaitFor::seconds(2));
let _container_1 = image.clone().start()?;
let _container_2 = image.clone().start()?;
let _container_3 = image.start()?;
Ok(())
}
#[test]
fn sync_run_exec() -> anyhow::Result<()> {
let _ = pretty_env_logger::try_init();
let image = GenericImage::new("simple_web_server", "latest")
.with_wait_for(WaitFor::log(
LogWaitStrategy::stdout("server is ready").with_times(2),
))
.with_wait_for(WaitFor::seconds(1));
let container = image.start()?;
let res = container.exec(
ExecCommand::new(vec!["sleep".to_string(), "3".to_string()])
.with_cmd_ready_condition(CmdWaitFor::exit_code(0)),
)?;
assert_eq!(res.exit_code()?, Some(0));
let mut res = container.exec(
ExecCommand::new(vec!["ls".to_string()])
.with_cmd_ready_condition(CmdWaitFor::message_on_stdout("foo")),
)?;
let stdout = String::from_utf8(res.stdout_to_vec()?)?;
assert!(stdout.contains("foo"), "stdout must contain 'foo'");
let mut res = container.exec(ExecCommand::new([
"/bin/bash",
"-c",
"echo 'stdout 1' >&1 && echo 'stderr 1' >&2 \
&& echo 'stderr 2' >&2 && echo 'stdout 2' >&1",
]))?;
let stdout = String::from_utf8(res.stdout_to_vec()?)?;
assert_eq!(stdout, "stdout 1\nstdout 2\n");
let stderr = String::from_utf8(res.stderr_to_vec()?)?;
assert_eq!(stderr, "stderr 1\nstderr 2\n");
let mut res = container.exec(ExecCommand::new([
"/bin/bash",
"-c",
"echo 'stdout 1' >&1 && echo 'stderr 1' >&2 \
&& echo 'stderr 2' >&2 && echo 'stdout 2' >&1",
]))?;
let mut stdout = String::new();
res.stdout().read_to_string(&mut stdout)?;
assert_eq!(stdout, "stdout 1\nstdout 2\n");
let mut stderr = String::new();
res.stderr().read_to_string(&mut stderr)?;
assert_eq!(stderr, "stderr 1\nstderr 2\n");
Ok(())
}
#[test]
fn sync_run_with_log_consumer() -> anyhow::Result<()> {
let _ = pretty_env_logger::try_init();
let (tx, rx) = std::sync::mpsc::sync_channel(1);
let _container = HelloWorld
.with_log_consumer(move |frame: &LogFrame| {
if String::from_utf8_lossy(frame.bytes()) == "Hello from Docker!\n" {
let _ = tx.send(());
}
})
.with_log_consumer(LoggingConsumer::new().with_stderr_level(log::Level::Error))
.start()?;
rx.recv()?; Ok(())
}
#[test]
fn sync_copy_bytes_to_container() -> anyhow::Result<()> {
let _ = pretty_env_logger::try_init();
let container = GenericImage::new("alpine", "latest")
.with_wait_for(WaitFor::seconds(2))
.with_copy_to("/tmp/somefile", "foobar".to_string().into_bytes())
.with_cmd(vec!["cat", "/tmp/somefile"])
.start()?;
let mut out = String::new();
container.stdout(false).read_to_string(&mut out)?;
assert!(out.contains("foobar"));
Ok(())
}
#[test]
fn sync_copy_files_to_container() -> anyhow::Result<()> {
let temp_dir = temp_dir::TempDir::new()?;
let f1 = temp_dir.child("foo.txt");
let sub_dir = temp_dir.child("subdir");
std::fs::create_dir(&sub_dir)?;
let mut f2 = sub_dir.clone();
f2.push("bar.txt");
std::fs::write(&f1, "foofoofoo")?;
std::fs::write(&f2, "barbarbar")?;
let container = GenericImage::new("alpine", "latest")
.with_wait_for(WaitFor::seconds(2))
.with_copy_to("/tmp/somefile", f1)
.with_copy_to("/", temp_dir.path())
.with_cmd(vec!["cat", "/tmp/somefile", "&&", "cat", "/subdir/bar.txt"])
.start()?;
let mut out = String::new();
container.stdout(false).read_to_string(&mut out)?;
println!("{}", out);
assert!(out.contains("foofoofoo"));
assert!(out.contains("barbarbar"));
Ok(())
}