use std::sync::Arc;
use rstest::rstest;
use sqlx::postgres::PgPoolOptions;
use reinhardt_query::prelude::*;
mod common;
use common::{PgContainer, postgres_ddl, unique_database_name};
async fn admin_pool(port: u16) -> Arc<sqlx::PgPool> {
let url = format!("postgres://postgres:postgres@localhost:{}/postgres", port);
let pool = PgPoolOptions::new()
.max_connections(1)
.connect(&url)
.await
.expect("Failed to connect to postgres database");
Arc::new(pool)
}
#[rstest]
#[tokio::test]
async fn test_postgres_create_database_basic(
#[future] postgres_ddl: (PgContainer, Arc<sqlx::PgPool>, u16, String),
) {
let (_container, _pool, port, _url) = postgres_ddl.await;
let admin = admin_pool(port).await;
let db_name = unique_database_name("test_db");
let mut stmt = Query::create_database();
stmt.name(db_name.clone());
let builder = PostgresQueryBuilder::new();
let (sql, _values) = builder.build_create_database(&stmt);
sqlx::query(&sql)
.execute(admin.as_ref())
.await
.expect("Failed to create database");
let db_exists: bool =
sqlx::query_scalar("SELECT EXISTS(SELECT 1 FROM pg_database WHERE datname = $1)")
.bind(&db_name)
.fetch_one(admin.as_ref())
.await
.unwrap();
assert!(db_exists);
sqlx::query(&format!(r#"DROP DATABASE IF EXISTS "{}""#, db_name))
.execute(admin.as_ref())
.await
.unwrap();
}
#[rstest]
#[tokio::test]
async fn test_postgres_create_database_if_not_exists(
#[future] postgres_ddl: (PgContainer, Arc<sqlx::PgPool>, u16, String),
) {
let (_container, _pool, port, _url) = postgres_ddl.await;
let admin = admin_pool(port).await;
let db_name = unique_database_name("test_db_ine");
let builder = PostgresQueryBuilder::new();
let mut stmt1 = Query::create_database();
stmt1.name(db_name.clone()).if_not_exists();
let (sql, _values) = builder.build_create_database(&stmt1);
sqlx::query(&sql)
.execute(admin.as_ref())
.await
.expect("First create should succeed");
let mut stmt2 = Query::create_database();
stmt2.name(db_name.clone()).if_not_exists();
let (sql, _values) = builder.build_create_database(&stmt2);
let result = sqlx::query(&sql).execute(admin.as_ref()).await;
assert!(
result.is_err(),
"Second create should fail - PostgreSQL does not support IF NOT EXISTS for CREATE DATABASE"
);
sqlx::query(&format!(r#"DROP DATABASE IF EXISTS "{}""#, db_name))
.execute(admin.as_ref())
.await
.unwrap();
}
#[rstest]
#[tokio::test]
async fn test_postgres_create_database_with_options(
#[future] postgres_ddl: (PgContainer, Arc<sqlx::PgPool>, u16, String),
) {
let (_container, _pool, port, _url) = postgres_ddl.await;
let admin = admin_pool(port).await;
let db_name = unique_database_name("test_db_opts");
let mut stmt = Query::create_database();
stmt.name(db_name.clone())
.encoding("UTF8")
.template("template0");
let builder = PostgresQueryBuilder::new();
let (sql, _values) = builder.build_create_database(&stmt);
sqlx::query(&sql)
.execute(admin.as_ref())
.await
.expect("Failed to create database with options");
let encoding: String = sqlx::query_scalar(
"SELECT pg_encoding_to_char(encoding) FROM pg_database WHERE datname = $1",
)
.bind(&db_name)
.fetch_one(admin.as_ref())
.await
.unwrap();
assert_eq!(encoding, "UTF8");
sqlx::query(&format!(r#"DROP DATABASE IF EXISTS "{}""#, db_name))
.execute(admin.as_ref())
.await
.unwrap();
}
#[rstest]
#[tokio::test]
async fn test_postgres_drop_database(
#[future] postgres_ddl: (PgContainer, Arc<sqlx::PgPool>, u16, String),
) {
let (_container, _pool, port, _url) = postgres_ddl.await;
let admin = admin_pool(port).await;
let db_name = unique_database_name("test_db_drop");
sqlx::query(&format!(r#"CREATE DATABASE "{}""#, db_name))
.execute(admin.as_ref())
.await
.expect("Failed to create database");
let db_exists: bool =
sqlx::query_scalar("SELECT EXISTS(SELECT 1 FROM pg_database WHERE datname = $1)")
.bind(&db_name)
.fetch_one(admin.as_ref())
.await
.unwrap();
assert!(db_exists);
let mut stmt = Query::drop_database();
stmt.name(db_name.clone());
let builder = PostgresQueryBuilder::new();
let (sql, _values) = builder.build_drop_database(&stmt);
sqlx::query(&sql)
.execute(admin.as_ref())
.await
.expect("Failed to drop database");
let db_exists: bool =
sqlx::query_scalar("SELECT EXISTS(SELECT 1 FROM pg_database WHERE datname = $1)")
.bind(&db_name)
.fetch_one(admin.as_ref())
.await
.unwrap();
assert!(!db_exists);
}
#[rstest]
#[tokio::test]
async fn test_postgres_drop_database_if_exists_nonexistent(
#[future] postgres_ddl: (PgContainer, Arc<sqlx::PgPool>, u16, String),
) {
let (_container, _pool, port, _url) = postgres_ddl.await;
let admin = admin_pool(port).await;
let db_name = unique_database_name("nonexistent_db");
let mut stmt = Query::drop_database();
stmt.name(db_name.clone()).if_exists();
let builder = PostgresQueryBuilder::new();
let (sql, _values) = builder.build_drop_database(&stmt);
let result = sqlx::query(&sql).execute(admin.as_ref()).await;
assert!(result.is_ok());
}
#[rstest]
#[tokio::test]
async fn test_postgres_drop_database_nonexistent_fails(
#[future] postgres_ddl: (PgContainer, Arc<sqlx::PgPool>, u16, String),
) {
let (_container, _pool, port, _url) = postgres_ddl.await;
let admin = admin_pool(port).await;
let db_name = unique_database_name("nonexistent_db");
let mut stmt = Query::drop_database();
stmt.name(db_name.clone());
let builder = PostgresQueryBuilder::new();
let (sql, _values) = builder.build_drop_database(&stmt);
let result = sqlx::query(&sql).execute(admin.as_ref()).await;
assert!(result.is_err());
}
#[rstest]
#[tokio::test]
async fn test_postgres_database_state_transition(
#[future] postgres_ddl: (PgContainer, Arc<sqlx::PgPool>, u16, String),
) {
let (_container, _pool, port, _url) = postgres_ddl.await;
let admin = admin_pool(port).await;
let db_name = unique_database_name("state_db");
let builder = PostgresQueryBuilder::new();
let db_exists: bool =
sqlx::query_scalar("SELECT EXISTS(SELECT 1 FROM pg_database WHERE datname = $1)")
.bind(&db_name)
.fetch_one(admin.as_ref())
.await
.unwrap();
assert!(!db_exists, "State 1: Database should not exist");
let mut create_stmt = Query::create_database();
create_stmt.name(db_name.clone());
let (sql, _values) = builder.build_create_database(&create_stmt);
sqlx::query(&sql)
.execute(admin.as_ref())
.await
.expect("Failed to create database");
let db_exists: bool =
sqlx::query_scalar("SELECT EXISTS(SELECT 1 FROM pg_database WHERE datname = $1)")
.bind(&db_name)
.fetch_one(admin.as_ref())
.await
.unwrap();
assert!(db_exists, "State 2: Database should exist");
let mut drop_stmt = Query::drop_database();
drop_stmt.name(db_name.clone());
let (sql, _values) = builder.build_drop_database(&drop_stmt);
sqlx::query(&sql)
.execute(admin.as_ref())
.await
.expect("Failed to drop database");
let db_exists: bool =
sqlx::query_scalar("SELECT EXISTS(SELECT 1 FROM pg_database WHERE datname = $1)")
.bind(&db_name)
.fetch_one(admin.as_ref())
.await
.unwrap();
assert!(!db_exists, "State 3: Database should not exist");
}
#[rstest]
#[tokio::test]
async fn test_postgres_create_duplicate_database_fails(
#[future] postgres_ddl: (PgContainer, Arc<sqlx::PgPool>, u16, String),
) {
let (_container, _pool, port, _url) = postgres_ddl.await;
let admin = admin_pool(port).await;
let db_name = unique_database_name("dup_db");
let builder = PostgresQueryBuilder::new();
let mut stmt1 = Query::create_database();
stmt1.name(db_name.clone());
let (sql, _values) = builder.build_create_database(&stmt1);
sqlx::query(&sql)
.execute(admin.as_ref())
.await
.expect("First create should succeed");
let mut stmt2 = Query::create_database();
stmt2.name(db_name.clone());
let (sql, _values) = builder.build_create_database(&stmt2);
let result = sqlx::query(&sql).execute(admin.as_ref()).await;
assert!(result.is_err());
sqlx::query(&format!(r#"DROP DATABASE IF EXISTS "{}""#, db_name))
.execute(admin.as_ref())
.await
.unwrap();
}