use super::{exists, version, Cluster, ClusterError};
use crate::runtime::{self, strategy::RuntimeStrategy, Runtime};
use crate::version::{PartialVersion, Version};
use std::collections::HashSet;
use std::fs::File;
use std::path::{Path, PathBuf};
use std::str::FromStr;
type TestResult = Result<(), ClusterError>;
fn runtimes() -> Box<dyn Iterator<Item = Runtime>> {
let runtimes = runtime::strategy::default().runtimes().collect::<Vec<_>>();
Box::new(runtimes.into_iter())
}
#[test]
fn cluster_new() -> TestResult {
for runtime in runtimes() {
println!("{runtime:?}");
let cluster = Cluster::new("some/path", runtime)?;
assert_eq!(Path::new("some/path"), cluster.datadir);
assert!(!cluster.running()?);
}
Ok(())
}
#[test]
fn cluster_does_not_exist() -> TestResult {
for runtime in runtimes() {
println!("{runtime:?}");
let cluster = Cluster::new("some/path", runtime)?;
assert!(!exists(&cluster));
}
Ok(())
}
#[test]
fn cluster_does_exist() -> TestResult {
for runtime in runtimes() {
println!("{runtime:?}");
let data_dir = tempdir::TempDir::new("data")?;
let cluster = Cluster::new(&data_dir, runtime.clone())?;
cluster.create()?;
assert!(exists(&cluster));
let cluster = Cluster::new(&data_dir, runtime)?;
assert!(exists(&cluster));
}
Ok(())
}
#[test]
fn cluster_has_no_version_when_it_does_not_exist() -> TestResult {
for runtime in runtimes() {
println!("{runtime:?}");
let cluster = Cluster::new("some/path", runtime)?;
assert!(matches!(version(&cluster), Ok(None)));
}
Ok(())
}
#[test]
fn cluster_has_version_when_it_does_exist() -> TestResult {
let data_dir = tempdir::TempDir::new("data")?;
let version_file = data_dir.path().join("PG_VERSION");
File::create(&version_file)?;
for runtime in runtimes() {
println!("{runtime:?}");
let pg_version: PartialVersion = runtime.version.into();
let pg_version = pg_version.widened(); std::fs::write(&version_file, format!("{pg_version}\n"))?;
let cluster = Cluster::new(&data_dir, runtime)?;
assert!(matches!(version(&cluster), Ok(Some(_))));
}
Ok(())
}
#[test]
fn cluster_has_pid_file() -> TestResult {
let data_dir = PathBuf::from("/some/where");
for runtime in runtimes() {
println!("{runtime:?}");
let cluster = Cluster::new(&data_dir, runtime)?;
assert_eq!(
PathBuf::from("/some/where/postmaster.pid"),
cluster.pidfile()
);
}
Ok(())
}
#[test]
fn cluster_has_log_file() -> TestResult {
let data_dir = PathBuf::from("/some/where");
for runtime in runtimes() {
println!("{runtime:?}");
let cluster = Cluster::new(&data_dir, runtime)?;
assert_eq!(
PathBuf::from("/some/where/postmaster.log"),
cluster.logfile()
);
}
Ok(())
}
#[test]
fn cluster_create_creates_cluster() -> TestResult {
for runtime in runtimes() {
println!("{runtime:?}");
let data_dir = tempdir::TempDir::new("data")?;
let cluster = Cluster::new(&data_dir, runtime)?;
assert!(!exists(&cluster));
assert!(cluster.create()?);
assert!(exists(&cluster));
}
Ok(())
}
#[test]
fn cluster_create_creates_cluster_with_neutral_locale_and_timezone() -> TestResult {
for runtime in runtimes() {
println!("{runtime:?}");
let data_dir = tempdir::TempDir::new("data")?;
let cluster = Cluster::new(&data_dir, runtime.clone())?;
cluster.start()?;
let mut conn = cluster.connect("postgres")?;
let result = conn.query("SHOW ALL", &[])?;
let params: std::collections::HashMap<String, String> = result
.into_iter()
.map(|row| (row.get::<usize, String>(0), row.get::<usize, String>(1)))
.collect();
if runtime.version < Version::from_str("9.4.22")? {
let dealias = |tz: &String| (if tz == "UCT" { "UTC" } else { tz }).to_owned();
assert_eq!(params.get("TimeZone").map(dealias), Some("UTC".into()));
assert_eq!(params.get("log_timezone").map(dealias), Some("UTC".into()));
} else {
assert_eq!(params.get("TimeZone"), Some(&"UTC".into()));
assert_eq!(params.get("log_timezone"), Some(&"UTC".into()));
}
if runtime.version >= Version::from_str("16.0")? {
assert_eq!(params.get("lc_collate"), None);
assert_eq!(params.get("lc_ctype"), None);
assert_eq!(params.get("lc_messages"), Some(&String::new()));
} else {
assert_eq!(params.get("lc_collate"), Some(&"C".into()));
assert_eq!(params.get("lc_ctype"), Some(&"C".into()));
assert_eq!(params.get("lc_messages"), Some(&"C".into()));
}
assert_eq!(params.get("lc_monetary"), Some(&"C".into()));
assert_eq!(params.get("lc_numeric"), Some(&"C".into()));
assert_eq!(params.get("lc_time"), Some(&"C".into()));
cluster.stop()?;
}
Ok(())
}
#[test]
fn cluster_create_does_nothing_when_it_already_exists() -> TestResult {
for runtime in runtimes() {
println!("{runtime:?}");
let data_dir = tempdir::TempDir::new("data")?;
let cluster = Cluster::new(&data_dir, runtime)?;
assert!(!exists(&cluster));
assert!(cluster.create()?);
assert!(exists(&cluster));
assert!(!cluster.create()?);
}
Ok(())
}
#[test]
fn cluster_start_stop_starts_and_stops_cluster() -> TestResult {
for runtime in runtimes() {
println!("{runtime:?}");
let data_dir = tempdir::TempDir::new("data")?;
let cluster = Cluster::new(&data_dir, runtime)?;
cluster.create()?;
assert!(!cluster.running()?);
cluster.start()?;
assert!(cluster.running()?);
cluster.stop()?;
assert!(!cluster.running()?);
}
Ok(())
}
#[test]
fn cluster_destroy_stops_and_removes_cluster() -> TestResult {
for runtime in runtimes() {
println!("{runtime:?}");
let data_dir = tempdir::TempDir::new("data")?;
let cluster = Cluster::new(&data_dir, runtime)?;
cluster.create()?;
cluster.start()?;
assert!(exists(&cluster));
cluster.destroy()?;
assert!(!exists(&cluster));
}
Ok(())
}
#[test]
fn cluster_connect_connects() -> TestResult {
for runtime in runtimes() {
println!("{runtime:?}");
let data_dir = tempdir::TempDir::new("data")?;
let cluster = Cluster::new(&data_dir, runtime)?;
cluster.start()?;
cluster.connect("template1")?;
cluster.destroy()?;
}
Ok(())
}
#[test]
fn cluster_databases_returns_vec_of_database_names() -> TestResult {
for runtime in runtimes() {
println!("{runtime:?}");
let data_dir = tempdir::TempDir::new("data")?;
let cluster = Cluster::new(&data_dir, runtime)?;
cluster.start()?;
let expected: HashSet<String> = ["postgres", "template0", "template1"]
.iter()
.map(ToString::to_string)
.collect();
let observed: HashSet<String> = cluster.databases()?.iter().cloned().collect();
assert_eq!(expected, observed);
cluster.destroy()?;
}
Ok(())
}
#[test]
fn cluster_databases_with_non_plain_names_can_be_created_and_dropped() -> TestResult {
for runtime in runtimes() {
println!("{runtime:?}");
let data_dir = tempdir::TempDir::new("data")?;
let cluster = Cluster::new(&data_dir, runtime)?;
cluster.start()?;
cluster.createdb("foo-bar")?;
cluster.createdb("Foo-BAR")?;
let expected: HashSet<String> =
["foo-bar", "Foo-BAR", "postgres", "template0", "template1"]
.iter()
.map(ToString::to_string)
.collect();
let observed: HashSet<String> = cluster.databases()?.iter().cloned().collect();
assert_eq!(expected, observed);
cluster.dropdb("foo-bar")?;
cluster.dropdb("Foo-BAR")?;
cluster.destroy()?;
}
Ok(())
}