use camino::{Utf8Path, Utf8PathBuf};
#[cfg(feature = "diesel-support")]
use color_eyre::eyre::WrapErr;
use color_eyre::eyre::eyre;
use postgres::{Client, NoTls};
use postgresql_embedded::Settings;
use crate::{TestBootstrapSettings, error::BootstrapResult};
pub(crate) fn escape_identifier(name: &str) -> String { name.replace('"', "\"\"") }
pub(crate) fn connect_admin(url: &str) -> BootstrapResult<Client> {
Client::connect(url, NoTls).map_err(|err| {
crate::error::BootstrapError::from(eyre!("failed to connect to admin database: {err}"))
})
}
#[derive(Debug, Clone)]
pub struct ConnectionMetadata {
settings: Settings,
pgpass_file: Utf8PathBuf,
}
impl ConnectionMetadata {
pub(crate) fn from_settings(settings: &TestBootstrapSettings) -> Self {
Self {
settings: settings.settings.clone(),
pgpass_file: settings.environment.pgpass_file.clone(),
}
}
#[must_use]
pub fn host(&self) -> &str { self.settings.host.as_str() }
#[must_use]
pub const fn port(&self) -> u16 { self.settings.port }
#[must_use]
pub fn superuser(&self) -> &str { self.settings.username.as_str() }
#[must_use]
pub fn password(&self) -> &str { self.settings.password.as_str() }
#[must_use]
pub fn pgpass_file(&self) -> &Utf8Path { self.pgpass_file.as_ref() }
#[must_use]
pub fn database_url(&self, database: &str) -> String { self.settings.url(database) }
}
#[derive(Debug, Clone)]
pub struct TestClusterConnection {
metadata: ConnectionMetadata,
}
impl TestClusterConnection {
pub(crate) fn new(settings: &TestBootstrapSettings) -> Self {
Self {
metadata: ConnectionMetadata::from_settings(settings),
}
}
#[must_use]
pub fn host(&self) -> &str { self.metadata.host() }
#[must_use]
pub const fn port(&self) -> u16 { self.metadata.port() }
#[must_use]
pub fn superuser(&self) -> &str { self.metadata.superuser() }
#[must_use]
pub fn password(&self) -> &str { self.metadata.password() }
#[must_use]
pub fn pgpass_file(&self) -> &Utf8Path { self.metadata.pgpass_file() }
#[must_use]
pub fn metadata(&self) -> ConnectionMetadata { self.metadata.clone() }
#[must_use]
pub fn database_url(&self, database: &str) -> String { self.metadata.database_url(database) }
#[cfg(feature = "diesel-support")]
pub fn diesel_connection(&self, database: &str) -> BootstrapResult<diesel::PgConnection> {
use diesel::Connection;
diesel::PgConnection::establish(&self.database_url(database))
.wrap_err(format!("failed to connect to {database} via Diesel"))
.map_err(crate::error::BootstrapError::from)
}
pub(super) fn admin_client(&self) -> BootstrapResult<Client> {
connect_admin(&self.database_url("postgres"))
}
}
#[cfg(test)]
mod tests {
use std::time::Duration;
use postgresql_embedded::Settings;
use super::*;
use crate::{
CleanupMode,
TestBootstrapSettings,
bootstrap::{ExecutionMode, ExecutionPrivileges, TestBootstrapEnvironment},
};
fn sample_settings() -> TestBootstrapSettings {
let settings = Settings {
host: "127.0.0.1".into(),
port: 55_321,
username: "fixture_user".into(),
password: "fixture_pass".into(),
data_dir: "/tmp/cluster-data".into(),
installation_dir: "/tmp/cluster-install".into(),
..Settings::default()
};
TestBootstrapSettings {
privileges: ExecutionPrivileges::Unprivileged,
execution_mode: ExecutionMode::InProcess,
settings,
environment: TestBootstrapEnvironment {
home: Utf8PathBuf::from("/tmp/home"),
xdg_cache_home: Utf8PathBuf::from("/tmp/home/cache"),
xdg_runtime_dir: Utf8PathBuf::from("/tmp/home/run"),
pgpass_file: Utf8PathBuf::from("/tmp/home/.pgpass"),
tz_dir: Some(Utf8PathBuf::from("/usr/share/zoneinfo")),
timezone: "UTC".into(),
},
worker_binary: None,
setup_timeout: Duration::from_secs(1),
start_timeout: Duration::from_secs(1),
shutdown_timeout: Duration::from_secs(1),
cleanup_mode: CleanupMode::default(),
binary_cache_dir: None,
}
}
#[test]
fn metadata_reflects_underlying_settings() {
let settings = sample_settings();
let connection = TestClusterConnection::new(&settings);
let metadata = connection.metadata();
assert_eq!(metadata.host(), "127.0.0.1");
assert_eq!(metadata.port(), 55_321);
assert_eq!(metadata.superuser(), "fixture_user");
assert_eq!(metadata.password(), "fixture_pass");
assert_eq!(metadata.pgpass_file(), Utf8Path::new("/tmp/home/.pgpass"));
}
#[test]
fn database_url_matches_postgresql_embedded() {
let settings = sample_settings();
let connection = TestClusterConnection::new(&settings);
let expected = settings.settings.url("postgres");
assert_eq!(connection.database_url("postgres"), expected);
}
}