use crate::error::TypedError;
use crate::query::Client;
pub const DEFAULT_TEST_ADDR: &str = "127.0.0.1:54322";
pub const DEFAULT_TEST_USER: &str = "postgres";
pub const DEFAULT_TEST_PASSWORD: &str = "postgres";
pub const DEFAULT_TEST_DB: &str = "postgrest_test";
fn cached(slot: &'static std::sync::OnceLock<String>, var: &str, default: &str) -> &'static str {
slot.get_or_init(|| std::env::var(var).unwrap_or_else(|_| default.to_string()))
.as_str()
}
pub fn test_addr() -> &'static str {
static V: std::sync::OnceLock<String> = std::sync::OnceLock::new();
cached(&V, "RESOLUTE_TEST_ADDR", DEFAULT_TEST_ADDR)
}
pub fn test_user() -> &'static str {
static V: std::sync::OnceLock<String> = std::sync::OnceLock::new();
cached(&V, "RESOLUTE_TEST_USER", DEFAULT_TEST_USER)
}
pub fn test_password() -> &'static str {
static V: std::sync::OnceLock<String> = std::sync::OnceLock::new();
cached(&V, "RESOLUTE_TEST_PASSWORD", DEFAULT_TEST_PASSWORD)
}
pub fn test_database() -> &'static str {
static V: std::sync::OnceLock<String> = std::sync::OnceLock::new();
cached(&V, "RESOLUTE_TEST_DB", DEFAULT_TEST_DB)
}
pub fn test_database_url() -> String {
format!(
"postgres://{}:{}@{}/{}",
test_user(),
test_password(),
test_addr(),
test_database()
)
}
pub struct TestDb {
pub addr: String,
pub user: String,
pub password: String,
pub database: String,
}
impl TestDb {
pub async fn create(addr: &str, user: &str, password: &str) -> Result<Self, TypedError> {
let database = format!(
"resolute_test_{}",
std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_nanos()
);
let maint = Client::connect(addr, user, password, "postgres").await?;
maint
.simple_query(&format!(
"CREATE DATABASE \"{}\"",
database.replace('"', "\"\"")
))
.await?;
tracing::info!(database = %database, "test database created");
Ok(Self {
addr: addr.to_string(),
user: user.to_string(),
password: password.to_string(),
database,
})
}
pub async fn create_with_migrations(
addr: &str,
user: &str,
password: &str,
migrations_dir: &str,
) -> Result<Self, TypedError> {
let db = Self::create(addr, user, password).await?;
let url = format!(
"postgres://{}:{}@{}/{}",
db.user, db.password, db.addr, db.database
);
crate::migrate::run(&url, migrations_dir).await?;
Ok(db)
}
pub async fn client(&self) -> Result<Client, TypedError> {
Client::connect(&self.addr, &self.user, &self.password, &self.database).await
}
pub async fn drop_db(&self) -> Result<(), TypedError> {
let maint = Client::connect(&self.addr, &self.user, &self.password, "postgres").await?;
let _ = maint
.simple_query(&format!(
"SELECT pg_terminate_backend(pid) FROM pg_stat_activity \
WHERE datname = '{}' AND pid != pg_backend_pid()",
self.database.replace('\'', "''")
))
.await;
maint
.simple_query(&format!(
"DROP DATABASE IF EXISTS \"{}\"",
self.database.replace('"', "\"\"")
))
.await?;
tracing::info!(database = %self.database, "test database dropped");
Ok(())
}
}