use rightsize::{Container, ContainerGuard, Result, Wait};
const HTTP_PORT: u16 = 8123;
const NATIVE_PORT: u16 = 9000;
pub struct ClickHouseContainer {
container: Container,
username: String,
password: String,
database: String,
}
impl ClickHouseContainer {
pub fn new() -> Self {
Self::with_image("clickhouse/clickhouse-server:25.8")
}
pub fn with_image(image: &str) -> Self {
let username = "test".to_string();
let password = "test".to_string();
let database = "test".to_string();
let container = Container::new(image)
.with_exposed_ports(&[HTTP_PORT, NATIVE_PORT])
.with_env("CLICKHOUSE_USER", &username)
.with_env("CLICKHOUSE_PASSWORD", &password)
.with_env("CLICKHOUSE_DB", &database)
.waiting_for(Wait::for_http("/ping").for_port(HTTP_PORT));
Self {
container,
username,
password,
database,
}
}
pub fn with_username(mut self, username: &str) -> Self {
self.username = username.to_string();
self.container = self.container.with_env("CLICKHOUSE_USER", username);
self
}
pub fn with_password(mut self, password: &str) -> Self {
self.password = password.to_string();
self.container = self.container.with_env("CLICKHOUSE_PASSWORD", password);
self
}
pub fn with_database(mut self, database: &str) -> Self {
self.database = database.to_string();
self.container = self.container.with_env("CLICKHOUSE_DB", database);
self
}
pub async fn start(self) -> Result<ClickHouseGuard> {
let guard = self.container.start().await?;
Ok(ClickHouseGuard {
guard,
username: self.username,
password: self.password,
database: self.database,
})
}
}
impl Default for ClickHouseContainer {
fn default() -> Self {
Self::new()
}
}
pub struct ClickHouseGuard {
guard: ContainerGuard,
username: String,
password: String,
database: String,
}
impl ClickHouseGuard {
pub fn username(&self) -> &str {
&self.username
}
pub fn password(&self) -> &str {
&self.password
}
pub fn database_name(&self) -> &str {
&self.database
}
pub fn http_url(&self) -> String {
format!(
"http://{}:{}",
self.guard.host(),
self.guard.get_mapped_port(HTTP_PORT).unwrap()
)
}
pub async fn stop(self) -> Result<()> {
self.guard.stop().await
}
}
impl std::ops::Deref for ClickHouseGuard {
type Target = ContainerGuard;
fn deref(&self) -> &ContainerGuard {
&self.guard
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn defaults_are_the_test_trio() {
let c = ClickHouseContainer::new();
assert_eq!(c.username, "test");
assert_eq!(c.password, "test");
assert_eq!(c.database, "test");
}
#[test]
fn builders_override_the_defaults() {
let c = ClickHouseContainer::new()
.with_username("alice")
.with_password("s3cret")
.with_database("app");
assert_eq!(c.username, "alice");
assert_eq!(c.password, "s3cret");
assert_eq!(c.database, "app");
}
}