#![cfg(test)]
use std::time::Duration;
use prax_orm::{Model, PraxClient, client};
use prax_postgres::{PgEngine, PgPool, PgPoolBuilder};
#[derive(Model, Debug, Clone, PartialEq)]
#[prax(table = "rel_posts")]
pub struct Post {
#[prax(id, auto)]
pub id: i32,
pub title: String,
pub author_id: i32,
}
#[derive(Model, Debug, Clone, PartialEq)]
#[prax(table = "rel_users")]
pub struct User {
#[prax(id, auto)]
pub id: i32,
pub email: String,
#[prax(relation(target = "Post", foreign_key = "author_id"))]
pub posts: Vec<Post>,
}
client!(User, Post);
fn postgres_url() -> 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 setup() -> Option<PraxClient<PgEngine>> {
let url = postgres_url()?;
let pool: PgPool = PgPoolBuilder::new()
.url(url)
.max_connections(4)
.connection_timeout(Duration::from_secs(10))
.build()
.await
.expect("connect to postgres");
let conn = pool.get().await.expect("acquire conn");
conn.batch_execute(
"DROP TABLE IF EXISTS rel_posts; \
DROP TABLE IF EXISTS rel_users; \
CREATE TABLE rel_users ( \
id SERIAL PRIMARY KEY, \
email TEXT UNIQUE NOT NULL \
); \
CREATE TABLE rel_posts ( \
id SERIAL PRIMARY KEY, \
title TEXT NOT NULL, \
author_id INTEGER NOT NULL REFERENCES rel_users(id) \
)",
)
.await
.expect("create rel_users/rel_posts");
drop(conn);
Some(PraxClient::new(PgEngine::new(pool)))
}
#[tokio::test]
#[ignore = "requires docker-compose postgres (PRAX_E2E=1)"]
async fn find_many_include_posts_stitches_children_onto_parents() {
let Some(c) = setup().await else {
eprintln!("skipping: PRAX_E2E not set");
return;
};
let alice = c
.user()
.create()
.set("email", "alice@rel.example.com")
.exec()
.await
.expect("create alice");
assert!(alice.id > 0);
for title in ["First", "Second", "Third"] {
c.post()
.create()
.set("title", title)
.set("author_id", alice.id)
.exec()
.await
.expect("create post");
}
let all_posts = c
.post()
.find_many()
.exec()
.await
.expect("find posts directly");
assert_eq!(all_posts.len(), 3, "seeded three posts");
let users = c
.user()
.find_many()
.include(user::posts::fetch())
.exec()
.await
.expect("find_many with include");
assert_eq!(users.len(), 1, "exactly one seeded user");
assert_eq!(users[0].id, alice.id);
assert_eq!(users[0].posts.len(), 3, "all three posts attached");
let mut titles: Vec<_> = users[0].posts.iter().map(|p| p.title.clone()).collect();
titles.sort();
assert_eq!(titles, vec!["First", "Second", "Third"]);
for post in &users[0].posts {
assert_eq!(post.author_id, alice.id);
}
}