use std::fmt::Display;
use camino::Utf8PathBuf;
use proptest::{prelude::*, test_runner::TestCaseError};
use super::settings_paths_from_settings;
use crate::PgEnvCfg;
#[derive(Debug, Clone)]
struct PathResolutionCase {
install_segment: String,
data_segment: String,
uses_explicit_install: bool,
uses_explicit_data: bool,
repetition_count: usize,
}
#[derive(Debug, Eq, PartialEq)]
struct PathSnapshot {
install_dir: Utf8PathBuf,
data_dir: Utf8PathBuf,
password_file: Utf8PathBuf,
install_default: bool,
data_default: bool,
}
fn segment_strategy() -> impl Strategy<Value = String> {
"[a-z][a-z0-9_]{0,8}".prop_map(std::convert::identity)
}
fn path_resolution_case_strategy() -> impl Strategy<Value = PathResolutionCase> {
(
segment_strategy(),
segment_strategy(),
any::<bool>(),
any::<bool>(),
1usize..8,
)
.prop_map(
|(
install_segment,
data_segment,
uses_explicit_install,
uses_explicit_data,
repetition_count,
)| PathResolutionCase {
install_segment,
data_segment,
uses_explicit_install,
uses_explicit_data,
repetition_count,
},
)
}
fn testcase_failure<E: Display>(context: &str) -> impl FnOnce(E) -> TestCaseError + '_ {
move |err| TestCaseError::fail(format!("{context}: {err}"))
}
fn utf8_path(path: std::path::PathBuf, context: &str) -> Result<Utf8PathBuf, TestCaseError> {
Utf8PathBuf::from_path_buf(path).map_err(|bad_path| {
TestCaseError::fail(format!("{context} is not UTF-8: {}", bad_path.display()))
})
}
fn snapshot_for(
settings: &mut postgresql_embedded::Settings,
install_default: bool,
data_default: bool,
) -> Result<PathSnapshot, TestCaseError> {
let paths = settings_paths_from_settings(settings, install_default, data_default)
.map_err(testcase_failure("resolve settings paths"))?;
Ok(PathSnapshot {
install_dir: paths.install_dir,
data_dir: paths.data_dir,
password_file: paths.password_file,
install_default: paths.install_default,
data_default: paths.data_default,
})
}
proptest! {
#[test]
fn settings_path_resolution_converges_under_repetition(case in path_resolution_case_strategy()) {
let sandbox = tempfile::tempdir()
.map_err(|err| TestCaseError::fail(format!("create sandbox: {err}")))?;
let install_dir = utf8_path(
sandbox.path().join(&case.install_segment).join("install"),
"install directory",
)?;
let data_dir = utf8_path(
sandbox.path().join(&case.data_segment).join("data"),
"data directory",
)?;
let cfg = PgEnvCfg {
runtime_dir: case.uses_explicit_install.then_some(install_dir.clone()),
data_dir: case.uses_explicit_data.then_some(data_dir.clone()),
..PgEnvCfg::default()
};
let mut settings = cfg
.to_settings()
.map_err(testcase_failure("convert configuration"))?;
if !case.uses_explicit_install {
settings.installation_dir = install_dir.clone().into_std_path_buf();
}
if !case.uses_explicit_data {
settings.data_dir = data_dir.clone().into_std_path_buf();
}
let mut evolving_settings = settings;
let expected = snapshot_for(
&mut evolving_settings,
!case.uses_explicit_install,
!case.uses_explicit_data,
)?;
for _ in 0..case.repetition_count {
let observed = snapshot_for(
&mut evolving_settings,
!case.uses_explicit_install,
!case.uses_explicit_data,
)?;
prop_assert_eq!(&observed, &expected);
prop_assert_eq!(observed.password_file, observed.install_dir.join(".pgpass"));
}
}
}