#![allow(dead_code)]
use std::sync::atomic::{AtomicU16, Ordering};
use std::sync::Once;
use postgres::{Client, NoTls};
use testcontainers::runners::AsyncRunner;
use testcontainers_modules::postgres::Postgres;
use uuid::Uuid;
static POSTGRES_PORT: AtomicU16 = AtomicU16::new(0);
static POSTGRES_INIT: Once = Once::new();
static mut TOKIO_RT: Option<tokio::runtime::Runtime> = None;
const PG_USER: &str = "postgres";
const PG_PASSWORD: &str = "postgres";
const PG_DB: &str = "postgres";
fn ensure_postgres_started() {
POSTGRES_INIT.call_once(|| {
let rt = tokio::runtime::Runtime::new().expect("failed to create tokio runtime");
let port = rt.block_on(async {
let container = Postgres::default()
.start()
.await
.expect("failed to start postgres container");
let port = container
.get_host_port_ipv4(5432)
.await
.expect("failed to get postgres port");
std::mem::forget(container);
port
});
POSTGRES_PORT.store(port, Ordering::SeqCst);
unsafe {
TOKIO_RT = Some(rt);
}
});
}
fn get_postgres_port() -> u16 {
ensure_postgres_started();
POSTGRES_PORT.load(Ordering::SeqCst)
}
fn postgres_base_url() -> String {
let port = get_postgres_port();
format!(
"postgres://{}:{}@127.0.0.1:{}/{}",
PG_USER, PG_PASSWORD, port, PG_DB
)
}
fn url_with_db(db: &str) -> String {
let port = get_postgres_port();
format!(
"postgres://{}:{}@127.0.0.1:{}/{}",
PG_USER, PG_PASSWORD, port, db
)
}
pub fn fresh_postgres_db() -> (Client, String) {
let admin_url = postgres_base_url();
let mut admin = Client::connect(&admin_url, NoTls).expect("failed to connect as admin");
let db_name = format!("test_{}", Uuid::new_v4().simple());
admin
.execute(&format!("CREATE DATABASE \"{}\"", db_name), &[])
.expect("failed to create test database");
drop(admin);
let test_url = url_with_db(&db_name);
let client = Client::connect(&test_url, NoTls).expect("failed to connect to test database");
(client, db_name)
}
pub fn get_test_client() -> Client {
let (client, _db_name) = fresh_postgres_db();
client
}