use persistant_relational::{Migration, RelationalBackend, Value};
use persistant_sqlx::{PgBackend, TestDb};
async fn admin() -> Option<PgBackend> {
match std::env::var("PERSISTANT_TEST_DATABASE_URL") {
Ok(url) => Some(PgBackend::connect(&url).await.expect("connect")),
Err(_) => {
assert!(
std::env::var("PERSISTANT_REQUIRE_DATABASE").as_deref() != Ok("1"),
"PERSISTANT_REQUIRE_DATABASE=1 but PERSISTANT_TEST_DATABASE_URL is not set"
);
eprintln!("skipped: PERSISTANT_TEST_DATABASE_URL is not set");
None
}
}
}
async fn exists(admin: &PgBackend, name: &str) -> bool {
let rows = admin
.query(
"SELECT COUNT(*) FROM pg_database WHERE datname = $1",
&[Value::Text(name.to_string())],
)
.await
.unwrap();
rows[0].int(0) == Some(1)
}
#[tokio::test]
async fn each_test_database_is_its_own() {
let Some(admin) = admin().await else { return };
let (a, b) = (
TestDb::create(&admin).await.unwrap(),
TestDb::create(&admin).await.unwrap(),
);
assert_ne!(a.name(), b.name());
for (db, n) in [(&a, 1_i64), (&b, 2)] {
db.backend()
.execute("CREATE TABLE t (n BIGINT NOT NULL)", &[])
.await
.unwrap();
db.backend()
.execute("INSERT INTO t VALUES ($1)", &[Value::Int(n)])
.await
.unwrap();
}
for (db, n) in [(&a, 1_i64), (&b, 2)] {
let rows = db.backend().query("SELECT n FROM t", &[]).await.unwrap();
assert_eq!(rows.len(), 1);
assert_eq!(rows[0].int(0), Some(n));
}
a.drop_database().await.unwrap();
b.drop_database().await.unwrap();
}
#[tokio::test]
async fn migrations_apply_to_the_test_database_only() {
let Some(admin) = admin().await else { return };
let db = TestDb::create(&admin).await.unwrap();
let report = db
.migrate(&[Migration::new(
1,
"things",
"CREATE TABLE things (id BIGINT PRIMARY KEY);",
)])
.await
.unwrap();
assert_eq!(report.applied.len(), 1);
db.backend()
.execute("INSERT INTO things VALUES (1)", &[])
.await
.unwrap();
let elsewhere = admin
.query("SELECT to_regclass('things') IS NULL", &[])
.await
.unwrap();
assert_eq!(elsewhere[0].values, vec![Value::Bool(true)]);
db.drop_database().await.unwrap();
}
#[tokio::test]
async fn drop_database_removes_it_even_with_a_connection_left_open() {
let Some(admin) = admin().await else { return };
let db = TestDb::create(&admin).await.unwrap();
let name = db.name().to_string();
let _held = db.backend().pool().acquire().await.unwrap();
assert!(exists(&admin, &name).await);
db.drop_database().await.unwrap();
assert!(!exists(&admin, &name).await);
}
#[tokio::test(flavor = "current_thread")]
async fn a_test_database_that_goes_out_of_scope_is_dropped_on_a_current_thread_runtime() {
let Some(admin) = admin().await else { return };
let name = {
let db = TestDb::create(&admin).await.unwrap();
db.backend()
.execute("CREATE TABLE t (n BIGINT)", &[])
.await
.unwrap();
assert!(exists(&admin, db.name()).await);
db.name().to_string()
};
assert!(!exists(&admin, &name).await, "{name} outlived its TestDb");
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn a_test_database_that_goes_out_of_scope_is_dropped_on_a_multi_thread_runtime() {
let Some(admin) = admin().await else { return };
let name = {
let db = TestDb::create(&admin).await.unwrap();
db.name().to_string()
};
assert!(!exists(&admin, &name).await, "{name} outlived its TestDb");
}