use camino::Utf8PathBuf;
use color_eyre::eyre::{Result, eyre};
use rstest::{fixture, rstest};
use super::discard_orphaned_password_file;
use crate::error::BootstrapErrorKind;
struct Scratch {
_temp: tempfile::TempDir,
data_dir: Utf8PathBuf,
password_file: Utf8PathBuf,
}
#[fixture]
fn scratch() -> Result<Scratch> {
let temp = tempfile::tempdir()?;
let root = Utf8PathBuf::from_path_buf(temp.path().to_path_buf())
.map_err(|path| eyre!("non-UTF-8 tempdir {}", path.display()))?;
let data_dir = root.join("data");
std::fs::create_dir_all(&data_dir)?;
Ok(Scratch {
_temp: temp,
data_dir,
password_file: root.join(".pgpass"),
})
}
#[rstest]
fn a_password_file_without_a_cluster_is_removed(scratch: Result<Scratch>) {
let dir = scratch.expect("scratch directory");
std::fs::write(&dir.password_file, "previous-cluster-password").expect("seed the stale file");
let removed = discard_orphaned_password_file(&dir.data_dir, &dir.password_file)
.expect("a stale file is removable");
assert!(removed, "the stale file should be reported as removed");
assert!(!dir.password_file.exists(), "the stale file must be gone");
}
#[rstest]
fn a_password_file_beside_a_cluster_is_kept(scratch: Result<Scratch>) {
let dir = scratch.expect("scratch directory");
std::fs::write(dir.data_dir.join("PG_VERSION"), "17\n").expect("mark a cluster");
std::fs::write(&dir.password_file, "live-cluster-password").expect("seed the live file");
let removed = discard_orphaned_password_file(&dir.data_dir, &dir.password_file)
.expect("probing a cluster succeeds");
assert!(!removed);
assert_eq!(
std::fs::read_to_string(&dir.password_file).expect("the file is still there"),
"live-cluster-password"
);
}
#[rstest]
fn no_password_file_is_not_an_error(scratch: Result<Scratch>) {
let dir = scratch.expect("scratch directory");
let removed = discard_orphaned_password_file(&dir.data_dir, &dir.password_file)
.expect("a missing file is fine");
assert!(!removed);
}
#[rstest]
fn an_unremovable_stale_path_is_an_error(scratch: Result<Scratch>) {
let dir = scratch.expect("scratch directory");
std::fs::create_dir_all(&dir.password_file).expect("put a directory in the way");
let err = discard_orphaned_password_file(&dir.data_dir, &dir.password_file)
.expect_err("a directory cannot be removed as a file");
assert_eq!(err.kind(), BootstrapErrorKind::ClusterPasswordUnreadable);
assert!(format!("{err:?}").contains("cannot be removed"), "{err:?}");
}
#[cfg(unix)]
#[rstest]
fn a_probe_failure_propagates_and_keeps_the_file(scratch: Result<Scratch>) {
let dir = scratch.expect("scratch directory");
std::fs::remove_dir(&dir.data_dir).expect("clear the data directory");
std::fs::write(&dir.data_dir, "not a directory").expect("put a file in the way");
std::fs::write(&dir.password_file, "kept").expect("seed the password file");
let err = discard_orphaned_password_file(&dir.data_dir, &dir.password_file)
.expect_err("the probe cannot read through a file");
assert_eq!(err.kind(), BootstrapErrorKind::ClusterPasswordUnreadable);
assert!(dir.password_file.exists(), "a failed probe removes nothing");
}