use crate::prelude::*;
use hashbrown::HashMap;
#[driver_test(id(ID), scenario(crate::scenarios::has_many_belongs_to))]
pub async fn crud_user_todos(test: &mut Test) -> Result<()> {
let mut db = setup(test).await;
let user = User::create().name("User 1").exec(&mut db).await?;
assert_eq!(0, user.todos().exec(&mut db).await?.len());
let todo = user
.todos()
.create()
.title("hello world")
.exec(&mut db)
.await?;
let list = Todo::filter_by_id(todo.id).exec(&mut db).await?;
assert_eq!(1, list.len());
assert_eq!(todo.id, list[0].id);
let list = Todo::filter_by_user_id(user.id).exec(&mut db).await?;
assert_eq!(1, list.len());
assert_eq!(todo.id, list[0].id);
let user_reload = User::get_by_id(&mut db, &todo.user_id).await?;
assert_eq!(user.id, user_reload.id);
let mut created = HashMap::new();
let mut ids = vec![todo.id];
created.insert(todo.id, todo);
for i in 0..5 {
let title = format!("hello world {i}");
let todo = if i.is_even() {
user.todos().create().title(title).exec(&mut db).await?
} else {
Todo::create()
.user(&user)
.title(title)
.exec(&mut db)
.await?
};
ids.push(todo.id);
assert_none!(created.insert(todo.id, todo));
}
let list = user.todos().exec(&mut db).await?;
assert_eq!(6, list.len());
let loaded: HashMap<_, _> = list.into_iter().map(|todo| (todo.id, todo)).collect();
assert_eq!(6, loaded.len());
for (id, expect) in &created {
assert_eq!(expect.title, loaded[id].title);
}
let list = Todo::filter_by_user_id(user.id).exec(&mut db).await?;
assert_eq!(6, list.len());
let by_id: HashMap<_, _> = list.into_iter().map(|todo| (todo.id, todo)).collect();
assert_eq!(6, by_id.len());
for (id, expect) in by_id {
assert_eq!(expect.title, loaded[&id].title);
}
let user2 = User::create().name("User 2").exec(&mut db).await?;
assert_eq!(0, user2.todos().exec(&mut db).await?.len());
let u2_todo = user2
.todos()
.create()
.title("user 2 todo")
.exec(&mut db)
.await?;
{
let u1_todos = user.todos().exec(&mut db).await?;
for todo in u1_todos {
assert_ne!(u2_todo.id, todo.id);
}
}
let todo = Todo::get_by_id(&mut db, &ids[0]).await?;
todo.delete().exec(&mut db).await?;
assert_err!(Todo::get_by_id(&mut db, &ids[0]).await);
assert_err!(user.todos().get_by_id(&mut db, &ids[0]).await);
user.todos()
.filter_by_id(ids[1])
.delete()
.exec(&mut db)
.await?;
assert_err!(Todo::get_by_id(&mut db, &ids[1]).await);
assert_err!(user.todos().get_by_id(&mut db, &ids[1]).await);
user.todos()
.filter_by_id(ids[2])
.update()
.title("batch update 1")
.exec(&mut db)
.await?;
let todo = Todo::get_by_id(&mut db, &ids[2]).await?;
assert_eq!(todo.title, "batch update 1");
user2
.todos()
.filter_by_id(ids[2])
.update()
.title("batch update 2")
.exec(&mut db)
.await?;
let todo = Todo::get_by_id(&mut db, &ids[2]).await?;
assert_eq!(todo.title, "batch update 1");
let id = user.id;
user.delete().exec(&mut db).await?;
assert_err!(User::get_by_id(&mut db, &id).await);
assert_err!(Todo::get_by_id(&mut db, &ids[2]).await);
Ok(())
}
#[driver_test(id(ID), scenario(crate::scenarios::has_many_belongs_to))]
pub async fn has_many_insert_on_update(test: &mut Test) -> Result<()> {
let mut db = setup(test).await;
let mut user = User::create().name("Alice").exec(&mut db).await?;
assert!(user.todos().exec(&mut db).await?.is_empty());
user.update()
.name("Bob")
.todos(toasty::stmt::insert(Todo::create().title("change name")))
.exec(&mut db)
.await?;
assert_eq!("Bob", user.name);
let todos: Vec<_> = user.todos().exec(&mut db).await?;
assert_eq!(1, todos.len());
assert_eq!(todos[0].title, "change name");
Ok(())
}
#[driver_test(id(ID), scenario(crate::scenarios::has_many_belongs_to))]
pub async fn scoped_find_by_id(test: &mut Test) -> Result<()> {
let mut db = setup(test).await;
let user1 = User::create().name("User 1").exec(&mut db).await?;
let user2 = User::create().name("User 2").exec(&mut db).await?;
let todo = user1
.todos()
.create()
.title("hello world")
.exec(&mut db)
.await?;
let reloaded = user1.todos().get_by_id(&mut db, &todo.id).await?;
assert_eq!(reloaded.id, todo.id);
assert_eq!(reloaded.title, todo.title);
assert_none!(
user2
.todos()
.filter_by_id(todo.id)
.first()
.exec(&mut db)
.await?
);
let reloaded = User::filter_by_id(user1.id)
.todos()
.get_by_id(&mut db, &todo.id)
.await?;
assert_eq!(reloaded.id, todo.id);
assert_eq!(reloaded.title, todo.title);
user2
.todos()
.filter_by_id(todo.id)
.delete()
.exec(&mut db)
.await?;
let reloaded = user1.todos().get_by_id(&mut db, &todo.id).await?;
assert_eq!(reloaded.id, todo.id);
Ok(())
}
#[driver_test(id(ID))]
pub async fn has_many_on_target_pk(_test: &mut Test) {}
#[driver_test(id(ID))]
pub async fn has_many_when_target_indexes_fk_and_pk(_test: &mut Test) {}
#[driver_test(id(ID))]
pub async fn has_many_when_fk_is_composite(test: &mut Test) -> Result<()> {
#[derive(Debug, toasty::Model)]
struct User {
#[key]
#[auto]
id: ID,
#[has_many]
todos: toasty::HasMany<Todo>,
}
#[derive(Debug, toasty::Model)]
#[key(partition = user_id, local = id)]
struct Todo {
#[auto]
id: uuid::Uuid,
user_id: ID,
#[belongs_to(key = user_id, references = id)]
user: toasty::BelongsTo<User>,
title: String,
}
let mut db = test.setup_db(models!(User, Todo)).await;
let user = User::create().exec(&mut db).await?;
assert_eq!(0, user.todos().exec(&mut db).await?.len());
let todo = user
.todos()
.create()
.title("hello world")
.exec(&mut db)
.await?;
let list = Todo::filter_by_user_id_and_id(user.id, todo.id)
.exec(&mut db)
.await?;
assert_eq!(1, list.len());
assert_eq!(todo.id, list[0].id);
let list = Todo::filter_by_user_id(user.id).exec(&mut db).await?;
assert_eq!(1, list.len());
assert_eq!(todo.id, list[0].id);
let mut created = HashMap::new();
let mut ids = vec![todo.id];
created.insert(todo.id, todo);
for i in 0..5 {
let title = format!("hello world {i}");
let todo = if i.is_even() {
user.todos().create().title(title).exec(&mut db).await?
} else {
Todo::create()
.user(&user)
.title(title)
.exec(&mut db)
.await?
};
ids.push(todo.id);
assert_none!(created.insert(todo.id, todo));
}
let list = user.todos().exec(&mut db).await?;
assert_eq!(6, list.len());
let loaded: HashMap<_, _> = list.into_iter().map(|todo| (todo.id, todo)).collect();
assert_eq!(6, loaded.len());
for (id, expect) in &created {
assert_eq!(expect.title, loaded[id].title);
}
let list = Todo::filter_by_user_id(user.id).exec(&mut db).await?;
assert_eq!(6, list.len());
let by_id: HashMap<_, _> = list.into_iter().map(|todo| (todo.id, todo)).collect();
assert_eq!(6, by_id.len());
for (id, expect) in by_id {
assert_eq!(expect.title, loaded[&id].title);
}
let user2 = User::create().exec(&mut db).await?;
assert_eq!(0, user2.todos().exec(&mut db).await?.len());
let u2_todo = user2
.todos()
.create()
.title("user 2 todo")
.exec(&mut db)
.await?;
let u1_todos = user.todos().exec(&mut db).await?;
for todo in u1_todos {
assert_ne!(u2_todo.id, todo.id);
}
let todo = Todo::get_by_user_id_and_id(&mut db, &user.id, &ids[0]).await?;
todo.delete().exec(&mut db).await?;
assert_err!(Todo::get_by_user_id_and_id(&mut db, &user.id, &ids[0]).await);
assert_err!(user.todos().get_by_id(&mut db, &ids[0]).await);
user.todos()
.filter_by_id(ids[1])
.delete()
.exec(&mut db)
.await?;
assert_err!(Todo::get_by_user_id_and_id(&mut db, &user.id, &ids[1]).await);
assert_err!(user.todos().get_by_id(&mut db, &ids[1]).await);
user.todos()
.filter_by_id(ids[2])
.update()
.title("batch update 1")
.exec(&mut db)
.await?;
let todo = Todo::get_by_user_id_and_id(&mut db, &user.id, &ids[2]).await?;
assert_eq!(todo.title, "batch update 1");
user2
.todos()
.filter_by_id(ids[2])
.update()
.title("batch update 2")
.exec(&mut db)
.await?;
let todo = Todo::get_by_user_id_and_id(&mut db, &user.id, &ids[2]).await?;
assert_eq!(todo.title, "batch update 1");
Ok(())
}
#[driver_test(id(ID))]
pub async fn has_many_when_pk_is_composite(_test: &mut Test) {}
#[driver_test(id(ID))]
pub async fn has_many_when_fk_and_pk_are_composite(_test: &mut Test) {}
#[driver_test(id(ID), scenario(crate::scenarios::has_many_belongs_to))]
pub async fn belongs_to_required(test: &mut Test) {
let mut db = setup(test).await;
assert_err!(Todo::create().exec(&mut db).await);
}
#[driver_test(id(ID))]
pub async fn delete_when_belongs_to_optional(test: &mut Test) -> Result<()> {
#[derive(Debug, toasty::Model)]
struct User {
#[key]
#[auto]
id: ID,
#[has_many]
todos: toasty::HasMany<Todo>,
}
#[derive(Debug, toasty::Model)]
struct Todo {
#[key]
#[auto]
id: ID,
#[index]
user_id: Option<ID>,
#[belongs_to(key = user_id, references = id)]
user: toasty::BelongsTo<Option<User>>,
}
let mut db = test.setup_db(models!(User, Todo)).await;
let user = User::create().exec(&mut db).await?;
let mut ids = vec![];
for _ in 0..3 {
let todo = user.todos().create().exec(&mut db).await?;
ids.push(todo.id);
}
user.delete().exec(&mut db).await?;
for id in ids {
let todo = Todo::get_by_id(&mut db, id).await?;
assert_none!(todo.user_id);
}
Ok(())
}
#[driver_test(id(ID), scenario(crate::scenarios::has_many_belongs_to))]
pub async fn associate_new_user_with_todo_on_update_via_creation(test: &mut Test) -> Result<()> {
let mut db = setup(test).await;
let u1 = User::create()
.name("User 1")
.todo(Todo::create().title("hello world"))
.exec(&mut db)
.await?;
let todos: Vec<_> = u1.todos().exec(&mut db).await?;
assert_eq!(1, todos.len());
let mut todo = todos.into_iter().next().unwrap();
todo.update()
.user(User::create().name("User 2"))
.exec(&mut db)
.await?;
Ok(())
}
#[driver_test(id(ID), scenario(crate::scenarios::has_many_belongs_to))]
pub async fn associate_new_user_with_todo_on_update_query_via_creation(
test: &mut Test,
) -> Result<()> {
let mut db = setup(test).await;
let u1 = User::create()
.name("User 1")
.todo(Todo::create().title("a todo"))
.exec(&mut db)
.await?;
let todos: Vec<_> = u1.todos().exec(&mut db).await?;
assert_eq!(1, todos.len());
let todo = todos.into_iter().next().unwrap();
Todo::filter_by_id(todo.id)
.update()
.user(User::create().name("User 2"))
.exec(&mut db)
.await?;
Ok(())
}
#[driver_test(id(ID))]
#[should_panic]
pub async fn update_user_with_null_todo_is_err(test: &mut Test) -> Result<()> {
#[derive(Debug, toasty::Model)]
struct User {
#[key]
#[auto]
id: ID,
#[has_many]
todos: toasty::HasMany<Todo>,
}
#[derive(Debug, toasty::Model)]
struct Todo {
#[key]
#[auto]
id: ID,
#[index]
user_id: ID,
#[belongs_to(key = user_id, references = id)]
user: toasty::BelongsTo<User>,
}
use toasty::stmt::{self, IntoExpr};
let mut db = test.setup_db(models!(User, Todo)).await;
let u1 = User::create().todo(Todo::create()).exec(&mut db).await?;
let todos: Vec<_> = u1.todos().exec(&mut db).await?;
assert_eq!(1, todos.len());
let todo = todos.into_iter().next().unwrap();
let mut stmt: stmt::Update<Todo> =
stmt::Update::new(stmt::Query::from_expr((&todo).into_expr()));
stmt.set(2, toasty_core::stmt::Value::Null);
stmt.exec(&mut db).await?;
let u1_reloaded = User::get_by_id(&mut db, &u1.id).await?;
assert_eq!(u1_reloaded.id, u1.id);
Ok(())
}
#[driver_test(id(ID), scenario(crate::scenarios::has_many_belongs_to))]
pub async fn assign_todo_that_already_has_user_on_create(test: &mut Test) -> Result<()> {
let mut db = setup(test).await;
let todo = Todo::create()
.title("a todo")
.user(User::create().name("User 1"))
.exec(&mut db)
.await?;
let u1 = todo.user().exec(&mut db).await?;
let u2 = User::create()
.name("User 2")
.todo(&todo)
.exec(&mut db)
.await?;
let todo_reload = Todo::get_by_id(&mut db, &todo.id).await?;
assert_eq!(u2.id, todo_reload.user_id);
let todos: Vec<_> = u1.todos().exec(&mut db).await?;
assert_eq!(0, todos.len());
let todos: Vec<_> = u2.todos().exec(&mut db).await?;
assert_eq!(1, todos.len());
assert_eq!(todo.id, todos[0].id);
Ok(())
}
#[driver_test(id(ID), scenario(crate::scenarios::has_many_belongs_to))]
pub async fn assign_todo_that_already_has_user_on_update(test: &mut Test) -> Result<()> {
let mut db = setup(test).await;
let todo = Todo::create()
.title("a todo")
.user(User::create().name("User 1"))
.exec(&mut db)
.await?;
let u1 = todo.user().exec(&mut db).await?;
let mut u2 = User::create().name("User 2").exec(&mut db).await?;
u2.update()
.todos(toasty::stmt::insert(&todo))
.exec(&mut db)
.await?;
let todo_reload = Todo::get_by_id(&mut db, &todo.id).await?;
assert_eq!(u2.id, todo_reload.user_id);
let todos: Vec<_> = u1.todos().exec(&mut db).await?;
assert_eq!(0, todos.len());
let todos: Vec<_> = u2.todos().exec(&mut db).await?;
assert_eq!(1, todos.len());
assert_eq!(todo.id, todos[0].id);
Ok(())
}
#[driver_test(id(ID), scenario(crate::scenarios::has_many_belongs_to))]
pub async fn assign_existing_user_to_todo(test: &mut Test) -> Result<()> {
let mut db = setup(test).await;
let mut todo = Todo::create()
.title("hello")
.user(User::create().name("User 1"))
.exec(&mut db)
.await?;
let u1 = todo.user().exec(&mut db).await?;
let u2 = User::create().name("User 2").exec(&mut db).await?;
todo.update().user(&u2).exec(&mut db).await?;
let todo_reload = Todo::get_by_id(&mut db, &todo.id).await?;
assert_eq!(u2.id, todo_reload.user_id);
let todos: Vec<_> = u1.todos().exec(&mut db).await?;
assert_eq!(0, todos.len());
let todos: Vec<_> = u2.todos().exec(&mut db).await?;
assert_eq!(1, todos.len());
assert_eq!(todo.id, todos[0].id);
Ok(())
}
#[driver_test(id(ID), scenario(crate::scenarios::has_many_belongs_to))]
pub async fn assign_todo_to_user_on_update_query(test: &mut Test) -> Result<()> {
let mut db = setup(test).await;
let user = User::create().name("User 1").exec(&mut db).await?;
User::filter_by_id(user.id)
.update()
.todos(toasty::stmt::insert(Todo::create().title("hello")))
.exec(&mut db)
.await?;
let todos: Vec<_> = user.todos().exec(&mut db).await?;
assert_eq!(1, todos.len());
assert_eq!("hello", todos[0].title);
Ok(())
}
#[driver_test(id(ID))]
pub async fn has_many_when_fk_is_composite_with_snippets(test: &mut Test) -> Result<()> {
#[derive(Debug, toasty::Model)]
struct User {
#[key]
#[auto]
id: ID,
#[has_many]
todos: toasty::HasMany<Todo>,
}
#[derive(Debug, toasty::Model)]
#[key(partition = user_id, local = id)]
struct Todo {
#[auto]
id: uuid::Uuid,
user_id: ID,
#[belongs_to(key = user_id, references = id)]
user: toasty::BelongsTo<User>,
title: String,
}
let mut db = test.setup_db(models!(User, Todo)).await;
let user1 = User::create().exec(&mut db).await?;
let user2 = User::create().exec(&mut db).await?;
user1
.todos()
.create()
.title("hello world")
.exec(&mut db)
.await?;
let todo2 = user2
.todos()
.create()
.title("hello world")
.exec(&mut db)
.await?;
Todo::update_by_user_id(user1.id)
.title("Title 2")
.exec(&mut db)
.await?;
let todo = Todo::get_by_user_id(&mut db, user1.id).await?;
assert!(todo.title == "Title 2");
Todo::update_by_user_id_and_id(user2.id, todo2.id)
.title("Title 3")
.exec(&mut db)
.await?;
let todo = Todo::get_by_user_id_and_id(&mut db, user2.id, todo2.id).await?;
assert!(todo.title == "Title 3");
Todo::delete_by_user_id(&mut db, user1.id).await?;
assert_err!(Todo::get_by_user_id(&mut db, user1.id).await);
Todo::delete_by_user_id_and_id(&mut db, user2.id, todo2.id).await?;
assert_err!(Todo::get_by_user_id_and_id(&mut db, user2.id, todo2.id).await);
Ok(())
}