#![cfg(test)]
use std::sync::atomic::{AtomicU32, Ordering};
use std::time::Duration;
use prax_orm::{Model, PraxClient, client};
use prax_postgres::{PgEngine, PgPool, PgPoolBuilder};
use prax_query::raw::Sql;
#[derive(Debug, Model)]
#[prax(table = "raw_pg_users")]
struct User {
#[prax(id, auto)]
id: i32,
#[prax(unique)]
email: String,
name: Option<String>,
}
client!(User);
static TABLE_COUNTER: AtomicU32 = AtomicU32::new(0);
fn unique_table_suffix() -> String {
let n = TABLE_COUNTER.fetch_add(1, Ordering::SeqCst);
let pid = std::process::id();
format!("{pid}_{n}")
}
fn skip_unless_e2e() -> Option<String> {
if std::env::var("PRAX_E2E").ok().as_deref() != Some("1") {
return None;
}
Some(
std::env::var("POSTGRES_URL").unwrap_or_else(|_| {
"postgres://prax:prax_test_password@localhost:5432/prax_test".into()
}),
)
}
async fn build_pool(url: String) -> PgPool {
PgPoolBuilder::new()
.url(url)
.max_connections(4)
.connection_timeout(Duration::from_secs(10))
.build()
.await
.expect("connect to postgres")
}
async fn setup() -> Option<(PraxClient<PgEngine>, PgPool)> {
let url = skip_unless_e2e()?;
let pool = build_pool(url).await;
let _ = unique_table_suffix();
let conn = pool.get().await.expect("acquire conn for setup");
conn.batch_execute(
"BEGIN;
SELECT pg_advisory_xact_lock(0x7261775f706700);
CREATE TABLE IF NOT EXISTS raw_pg_users (
id SERIAL PRIMARY KEY,
email TEXT NOT NULL UNIQUE,
name TEXT
);
COMMIT",
)
.await
.expect("create raw_pg_users");
drop(conn);
Some((PraxClient::new(PgEngine::new(pool.clone())), pool))
}
async fn teardown(_pool: &PgPool) {
}
#[tokio::test]
#[ignore = "requires docker-compose postgres"]
async fn query_raw_decodes_rows() {
let Some((client, pool)) = setup().await else {
eprintln!("skipping: PRAX_E2E not set");
return;
};
let email = "query_raw_decodes_rows@example.com";
client
.execute_raw(Sql::new("DELETE FROM raw_pg_users WHERE email = ").bind(email))
.await
.expect("pre-clean");
client
.execute_raw(
Sql::new("INSERT INTO raw_pg_users (email, name) VALUES (")
.bind(email)
.push(", ")
.bind("Raw")
.push(")"),
)
.await
.expect("seed insert");
let users: Vec<User> = client
.query_raw(Sql::new("SELECT id, email, name FROM raw_pg_users WHERE email = ").bind(email))
.await
.expect("query_raw");
assert_eq!(users.len(), 1, "expected exactly one seeded row");
assert_eq!(users[0].email, email);
assert_eq!(users[0].name.as_deref(), Some("Raw"));
teardown(&pool).await;
}
#[tokio::test]
#[ignore = "requires docker-compose postgres"]
async fn execute_raw_returns_affected_count() {
let Some((client, pool)) = setup().await else {
eprintln!("skipping: PRAX_E2E not set");
return;
};
let email_a = "execute_raw__a@x.com";
let email_b = "execute_raw__b@y.com";
client
.execute_raw(
Sql::new("DELETE FROM raw_pg_users WHERE email IN (")
.bind(email_a)
.push(", ")
.bind(email_b)
.push(")"),
)
.await
.expect("pre-clean");
let n = client
.execute_raw(
Sql::new("INSERT INTO raw_pg_users (email, name) VALUES (")
.bind(email_a)
.push(", NULL), (")
.bind(email_b)
.push(", NULL)"),
)
.await
.expect("multi-insert");
assert_eq!(n, 2, "postgres should report two rows inserted");
let n = client
.execute_raw(
Sql::new("UPDATE raw_pg_users SET name = ")
.bind("touched")
.push(" WHERE email IN (")
.bind(email_a)
.push(", ")
.bind(email_b)
.push(")"),
)
.await
.expect("update");
assert_eq!(n, 2, "postgres should report two rows updated");
teardown(&pool).await;
}