1use crate::prelude::*;
5use hashbrown::HashMap;
6
7#[driver_test(id(ID), scenario(crate::scenarios::has_many_belongs_to))]
8pub async fn crud_user_todos(test: &mut Test) -> Result<()> {
9 let mut db = setup(test).await;
10
11 let user = User::create().name("User 1").exec(&mut db).await?;
13
14 assert_eq!(0, user.todos().exec(&mut db).await?.len());
16
17 let todo = user
19 .todos()
20 .create()
21 .title("hello world")
22 .exec(&mut db)
23 .await?;
24
25 let list = Todo::filter_by_id(todo.id).exec(&mut db).await?;
27
28 assert_eq!(1, list.len());
29 assert_eq!(todo.id, list[0].id);
30
31 let list = Todo::filter_by_user_id(user.id).exec(&mut db).await?;
33
34 assert_eq!(1, list.len());
35 assert_eq!(todo.id, list[0].id);
36
37 let user_reload = User::get_by_id(&mut db, &todo.user_id).await?;
39 assert_eq!(user.id, user_reload.id);
40
41 let mut created = HashMap::new();
42 let mut ids = vec![todo.id];
43 created.insert(todo.id, todo);
44
45 for i in 0..5 {
47 let title = format!("hello world {i}");
48
49 let todo = if i.is_even() {
50 user.todos().create().title(title).exec(&mut db).await?
52 } else {
53 Todo::create()
55 .user(&user)
56 .title(title)
57 .exec(&mut db)
58 .await?
59 };
60
61 ids.push(todo.id);
62 assert_none!(created.insert(todo.id, todo));
63 }
64
65 let list = user.todos().exec(&mut db).await?;
67
68 assert_eq!(6, list.len());
69
70 let loaded: HashMap<_, _> = list.into_iter().map(|todo| (todo.id, todo)).collect();
71 assert_eq!(6, loaded.len());
72
73 for (id, expect) in &created {
74 assert_eq!(expect.title, loaded[id].title);
75 }
76
77 let list = Todo::filter_by_user_id(user.id).exec(&mut db).await?;
79 assert_eq!(6, list.len());
80
81 let by_id: HashMap<_, _> = list.into_iter().map(|todo| (todo.id, todo)).collect();
82
83 assert_eq!(6, by_id.len());
84
85 for (id, expect) in by_id {
86 assert_eq!(expect.title, loaded[&id].title);
87 }
88
89 let user2 = User::create().name("User 2").exec(&mut db).await?;
91
92 assert_eq!(0, user2.todos().exec(&mut db).await?.len());
94
95 let u2_todo = user2
97 .todos()
98 .create()
99 .title("user 2 todo")
100 .exec(&mut db)
101 .await?;
102
103 {
104 let u1_todos = user.todos().exec(&mut db).await?;
105
106 for todo in u1_todos {
107 assert_ne!(u2_todo.id, todo.id);
108 }
109 }
110
111 let todo = Todo::get_by_id(&mut db, &ids[0]).await?;
113 todo.delete().exec(&mut db).await?;
114
115 assert_err!(Todo::get_by_id(&mut db, &ids[0]).await);
117
118 assert_err!(user.todos().get_by_id(&mut db, &ids[0]).await);
120
121 user.todos()
123 .filter_by_id(ids[1])
124 .delete()
125 .exec(&mut db)
126 .await?;
127
128 assert_err!(Todo::get_by_id(&mut db, &ids[1]).await);
130
131 assert_err!(user.todos().get_by_id(&mut db, &ids[1]).await);
133
134 user.todos()
136 .filter_by_id(ids[2])
137 .update()
138 .title("batch update 1")
139 .exec(&mut db)
140 .await?;
141
142 let todo = Todo::get_by_id(&mut db, &ids[2]).await?;
143 assert_eq!(todo.title, "batch update 1");
144
145 user2
147 .todos()
148 .filter_by_id(ids[2])
149 .update()
150 .title("batch update 2")
151 .exec(&mut db)
152 .await?;
153
154 let todo = Todo::get_by_id(&mut db, &ids[2]).await?;
155 assert_eq!(todo.title, "batch update 1");
156
157 let id = user.id;
158
159 user.delete().exec(&mut db).await?;
161 assert_err!(User::get_by_id(&mut db, &id).await);
162 assert_err!(Todo::get_by_id(&mut db, &ids[2]).await);
163 Ok(())
164}
165
166#[driver_test(id(ID), scenario(crate::scenarios::has_many_belongs_to))]
167pub async fn has_many_insert_on_update(test: &mut Test) -> Result<()> {
168 let mut db = setup(test).await;
169
170 let mut user = User::create().name("Alice").exec(&mut db).await?;
172 assert!(user.todos().exec(&mut db).await?.is_empty());
173
174 user.update()
176 .name("Bob")
177 .todos(toasty::stmt::insert(Todo::create().title("change name")))
178 .exec(&mut db)
179 .await?;
180
181 assert_eq!("Bob", user.name);
182 let todos: Vec<_> = user.todos().exec(&mut db).await?;
183 assert_eq!(1, todos.len());
184 assert_eq!(todos[0].title, "change name");
185 Ok(())
186}
187
188#[driver_test(id(ID), scenario(crate::scenarios::has_many_belongs_to))]
194pub async fn has_many_apply_empty_is_noop(test: &mut Test) -> Result<()> {
195 let mut db = setup(test).await;
196
197 let mut user = User::create().name("Alice").exec(&mut db).await?;
198 user.todos()
199 .create()
200 .title("existing")
201 .exec(&mut db)
202 .await?;
203
204 user.update()
205 .name("Bob")
206 .todos(toasty::stmt::apply::<toasty::stmt::List<Todo>>([]))
207 .exec(&mut db)
208 .await?;
209
210 assert_eq!(user.name, "Bob");
211 let todos: Vec<_> = user.todos().exec(&mut db).await?;
212 assert_eq!(todos.len(), 1);
213 assert_eq!(todos[0].title, "existing");
214 Ok(())
215}
216
217#[driver_test(id(ID), scenario(crate::scenarios::has_many_belongs_to))]
218pub async fn has_many_apply_multiple_inserts(test: &mut Test) -> Result<()> {
219 let mut db = setup(test).await;
220
221 let mut user = User::create().name("Alice").exec(&mut db).await?;
222
223 user.update()
224 .todos(toasty::stmt::apply([
225 toasty::stmt::insert(Todo::create().title("Buy groceries")),
226 toasty::stmt::insert(Todo::create().title("Walk the dog")),
227 ]))
228 .exec(&mut db)
229 .await?;
230
231 let mut titles: Vec<_> = user
232 .todos()
233 .exec(&mut db)
234 .await?
235 .into_iter()
236 .map(|t| t.title)
237 .collect();
238 titles.sort();
239 assert_eq!(titles, ["Buy groceries", "Walk the dog"]);
240 Ok(())
241}
242
243#[driver_test(id(ID), scenario(crate::scenarios::has_many_belongs_to))]
244pub async fn scoped_find_by_id(test: &mut Test) -> Result<()> {
245 let mut db = setup(test).await;
246
247 let user1 = User::create().name("User 1").exec(&mut db).await?;
249 let user2 = User::create().name("User 2").exec(&mut db).await?;
250
251 let todo = user1
253 .todos()
254 .create()
255 .title("hello world")
256 .exec(&mut db)
257 .await?;
258
259 let reloaded = user1.todos().get_by_id(&mut db, &todo.id).await?;
261 assert_eq!(reloaded.id, todo.id);
262 assert_eq!(reloaded.title, todo.title);
263
264 assert_none!(
266 user2
267 .todos()
268 .filter_by_id(todo.id)
269 .first()
270 .exec(&mut db)
271 .await?
272 );
273
274 let reloaded = User::filter_by_id(user1.id)
275 .todos()
276 .get_by_id(&mut db, &todo.id)
277 .await?;
278
279 assert_eq!(reloaded.id, todo.id);
280 assert_eq!(reloaded.title, todo.title);
281
282 user2
284 .todos()
285 .filter_by_id(todo.id)
286 .delete()
287 .exec(&mut db)
288 .await?;
289 let reloaded = user1.todos().get_by_id(&mut db, &todo.id).await?;
290 assert_eq!(reloaded.id, todo.id);
291 Ok(())
292}
293
294#[driver_test(id(ID))]
297pub async fn has_many_on_target_pk(_test: &mut Test) {}
298
299#[driver_test(id(ID))]
302pub async fn has_many_when_target_indexes_fk_and_pk(_test: &mut Test) {}
303
304#[driver_test(id(ID))]
306pub async fn has_many_when_fk_is_composite(test: &mut Test) -> Result<()> {
307 #[derive(Debug, toasty::Model)]
308 struct User {
309 #[key]
310 #[auto]
311 id: ID,
312
313 #[has_many]
314 todos: toasty::HasMany<Todo>,
315 }
316
317 #[derive(Debug, toasty::Model)]
318 #[key(partition = user_id, local = id)]
319 struct Todo {
320 #[auto]
321 id: uuid::Uuid,
322
323 user_id: ID,
324
325 #[belongs_to(key = user_id, references = id)]
326 user: toasty::BelongsTo<User>,
327
328 title: String,
329 }
330
331 let mut db = test.setup_db(models!(User, Todo)).await;
332
333 let user = User::create().exec(&mut db).await?;
335
336 assert_eq!(0, user.todos().exec(&mut db).await?.len());
338
339 let todo = user
341 .todos()
342 .create()
343 .title("hello world")
344 .exec(&mut db)
345 .await?;
346
347 let list = Todo::filter_by_user_id_and_id(user.id, todo.id)
349 .exec(&mut db)
350 .await?;
351
352 assert_eq!(1, list.len());
353 assert_eq!(todo.id, list[0].id);
354
355 let list = Todo::filter_by_user_id(user.id).exec(&mut db).await?;
357
358 assert_eq!(1, list.len());
359 assert_eq!(todo.id, list[0].id);
360
361 let mut created = HashMap::new();
362 let mut ids = vec![todo.id];
363 created.insert(todo.id, todo);
364
365 for i in 0..5 {
367 let title = format!("hello world {i}");
368
369 let todo = if i.is_even() {
370 user.todos().create().title(title).exec(&mut db).await?
372 } else {
373 Todo::create()
375 .user(&user)
376 .title(title)
377 .exec(&mut db)
378 .await?
379 };
380
381 ids.push(todo.id);
382 assert_none!(created.insert(todo.id, todo));
383 }
384
385 let list = user.todos().exec(&mut db).await?;
387
388 assert_eq!(6, list.len());
389
390 let loaded: HashMap<_, _> = list.into_iter().map(|todo| (todo.id, todo)).collect();
391 assert_eq!(6, loaded.len());
392
393 for (id, expect) in &created {
394 assert_eq!(expect.title, loaded[id].title);
395 }
396
397 let list = Todo::filter_by_user_id(user.id).exec(&mut db).await?;
399 assert_eq!(6, list.len());
400
401 let by_id: HashMap<_, _> = list.into_iter().map(|todo| (todo.id, todo)).collect();
402
403 assert_eq!(6, by_id.len());
404
405 for (id, expect) in by_id {
406 assert_eq!(expect.title, loaded[&id].title);
407 }
408
409 let user2 = User::create().exec(&mut db).await?;
411
412 assert_eq!(0, user2.todos().exec(&mut db).await?.len());
414
415 let u2_todo = user2
417 .todos()
418 .create()
419 .title("user 2 todo")
420 .exec(&mut db)
421 .await?;
422
423 let u1_todos = user.todos().exec(&mut db).await?;
424
425 for todo in u1_todos {
426 assert_ne!(u2_todo.id, todo.id);
427 }
428
429 let todo = Todo::get_by_user_id_and_id(&mut db, &user.id, &ids[0]).await?;
431 todo.delete().exec(&mut db).await?;
432
433 assert_err!(Todo::get_by_user_id_and_id(&mut db, &user.id, &ids[0]).await);
435
436 assert_err!(user.todos().get_by_id(&mut db, &ids[0]).await);
438
439 user.todos()
441 .filter_by_id(ids[1])
442 .delete()
443 .exec(&mut db)
444 .await?;
445
446 assert_err!(Todo::get_by_user_id_and_id(&mut db, &user.id, &ids[1]).await);
448
449 assert_err!(user.todos().get_by_id(&mut db, &ids[1]).await);
451
452 user.todos()
454 .filter_by_id(ids[2])
455 .update()
456 .title("batch update 1")
457 .exec(&mut db)
458 .await?;
459 let todo = Todo::get_by_user_id_and_id(&mut db, &user.id, &ids[2]).await?;
460 assert_eq!(todo.title, "batch update 1");
461
462 user2
464 .todos()
465 .filter_by_id(ids[2])
466 .update()
467 .title("batch update 2")
468 .exec(&mut db)
469 .await?;
470 let todo = Todo::get_by_user_id_and_id(&mut db, &user.id, &ids[2]).await?;
471 assert_eq!(todo.title, "batch update 1");
472 Ok(())
473}
474
475#[driver_test(id(ID))]
477pub async fn has_many_when_pk_is_composite(_test: &mut Test) {}
478
479#[driver_test(id(ID))]
481pub async fn has_many_when_fk_and_pk_are_composite(_test: &mut Test) {}
482
483#[driver_test(id(ID), scenario(crate::scenarios::has_many_belongs_to))]
484pub async fn belongs_to_required(test: &mut Test) {
485 let mut db = setup(test).await;
486
487 assert_err!(Todo::create().exec(&mut db).await);
488}
489
490#[driver_test(id(ID))]
491pub async fn delete_when_belongs_to_optional(test: &mut Test) -> Result<()> {
492 #[derive(Debug, toasty::Model)]
493 struct User {
494 #[key]
495 #[auto]
496 id: ID,
497
498 #[has_many]
499 todos: toasty::HasMany<Todo>,
500 }
501
502 #[derive(Debug, toasty::Model)]
503 struct Todo {
504 #[key]
505 #[auto]
506 id: ID,
507
508 #[index]
509 user_id: Option<ID>,
510
511 #[belongs_to(key = user_id, references = id)]
512 user: toasty::BelongsTo<Option<User>>,
513 }
514
515 let mut db = test.setup_db(models!(User, Todo)).await;
516
517 let user = User::create().exec(&mut db).await?;
518 let mut ids = vec![];
519
520 for _ in 0..3 {
521 let todo = user.todos().create().exec(&mut db).await?;
522 ids.push(todo.id);
523 }
524
525 user.delete().exec(&mut db).await?;
527
528 for id in ids {
530 let todo = Todo::get_by_id(&mut db, id).await?;
531 assert_none!(todo.user_id);
532 }
533
534 Ok(())
536}
537
538#[driver_test(id(ID), scenario(crate::scenarios::has_many_belongs_to))]
539pub async fn associate_new_user_with_todo_on_update_via_creation(test: &mut Test) -> Result<()> {
540 let mut db = setup(test).await;
541
542 let u1 = User::create()
544 .name("User 1")
545 .todo(Todo::create().title("hello world"))
546 .exec(&mut db)
547 .await?;
548
549 let todos: Vec<_> = u1.todos().exec(&mut db).await?;
551 assert_eq!(1, todos.len());
552 let mut todo = todos.into_iter().next().unwrap();
553
554 todo.update()
555 .user(User::create().name("User 2"))
556 .exec(&mut db)
557 .await?;
558 Ok(())
559}
560
561#[driver_test(id(ID), scenario(crate::scenarios::has_many_belongs_to))]
562pub async fn associate_new_user_with_todo_on_update_query_via_creation(
563 test: &mut Test,
564) -> Result<()> {
565 let mut db = setup(test).await;
566
567 let u1 = User::create()
569 .name("User 1")
570 .todo(Todo::create().title("a todo"))
571 .exec(&mut db)
572 .await?;
573
574 let todos: Vec<_> = u1.todos().exec(&mut db).await?;
576 assert_eq!(1, todos.len());
577 let todo = todos.into_iter().next().unwrap();
578
579 Todo::filter_by_id(todo.id)
580 .update()
581 .user(User::create().name("User 2"))
582 .exec(&mut db)
583 .await?;
584 Ok(())
585}
586
587#[driver_test(id(ID))]
588#[should_panic]
589pub async fn update_user_with_null_todo_is_err(test: &mut Test) -> Result<()> {
590 #[derive(Debug, toasty::Model)]
591 struct User {
592 #[key]
593 #[auto]
594 id: ID,
595
596 #[has_many]
597 todos: toasty::HasMany<Todo>,
598 }
599
600 #[derive(Debug, toasty::Model)]
601 struct Todo {
602 #[key]
603 #[auto]
604 id: ID,
605
606 #[index]
607 user_id: ID,
608
609 #[belongs_to(key = user_id, references = id)]
610 user: toasty::BelongsTo<User>,
611 }
612
613 use toasty::stmt::{self, IntoExpr};
614
615 let mut db = test.setup_db(models!(User, Todo)).await;
616
617 let u1 = User::create().todo(Todo::create()).exec(&mut db).await?;
619
620 let todos: Vec<_> = u1.todos().exec(&mut db).await?;
622 assert_eq!(1, todos.len());
623 let todo = todos.into_iter().next().unwrap();
624
625 let mut stmt: stmt::Update<Todo> =
627 stmt::Update::new(stmt::Query::from_expr((&todo).into_expr()));
628 stmt.set(2, toasty_core::stmt::Value::Null);
629 stmt.exec(&mut db).await?;
630
631 let u1_reloaded = User::get_by_id(&mut db, &u1.id).await?;
633 assert_eq!(u1_reloaded.id, u1.id);
634 Ok(())
635}
636
637#[driver_test(id(ID), scenario(crate::scenarios::has_many_belongs_to))]
638pub async fn assign_todo_that_already_has_user_on_create(test: &mut Test) -> Result<()> {
639 let mut db = setup(test).await;
640
641 let todo = Todo::create()
642 .title("a todo")
643 .user(User::create().name("User 1"))
644 .exec(&mut db)
645 .await?;
646
647 let u1 = todo.user().exec(&mut db).await?;
648
649 let u2 = User::create()
650 .name("User 2")
651 .todo(&todo)
652 .exec(&mut db)
653 .await?;
654
655 let todo_reload = Todo::get_by_id(&mut db, &todo.id).await?;
656
657 assert_eq!(u2.id, todo_reload.user_id);
658
659 let todos: Vec<_> = u1.todos().exec(&mut db).await?;
661 assert_eq!(0, todos.len());
662
663 let todos: Vec<_> = u2.todos().exec(&mut db).await?;
665 assert_eq!(1, todos.len());
666 assert_eq!(todo.id, todos[0].id);
667 Ok(())
668}
669
670#[driver_test(id(ID), scenario(crate::scenarios::has_many_belongs_to))]
671pub async fn assign_todo_that_already_has_user_on_update(test: &mut Test) -> Result<()> {
672 let mut db = setup(test).await;
673
674 let todo = Todo::create()
675 .title("a todo")
676 .user(User::create().name("User 1"))
677 .exec(&mut db)
678 .await?;
679
680 let u1 = todo.user().exec(&mut db).await?;
681
682 let mut u2 = User::create().name("User 2").exec(&mut db).await?;
683
684 u2.update()
686 .todos(toasty::stmt::insert(&todo))
687 .exec(&mut db)
688 .await?;
689
690 let todo_reload = Todo::get_by_id(&mut db, &todo.id).await?;
691
692 assert_eq!(u2.id, todo_reload.user_id);
693
694 let todos: Vec<_> = u1.todos().exec(&mut db).await?;
696 assert_eq!(0, todos.len());
697
698 let todos: Vec<_> = u2.todos().exec(&mut db).await?;
700 assert_eq!(1, todos.len());
701 assert_eq!(todo.id, todos[0].id);
702 Ok(())
703}
704
705#[driver_test(id(ID), scenario(crate::scenarios::has_many_belongs_to))]
706pub async fn assign_existing_user_to_todo(test: &mut Test) -> Result<()> {
707 let mut db = setup(test).await;
708
709 let mut todo = Todo::create()
710 .title("hello")
711 .user(User::create().name("User 1"))
712 .exec(&mut db)
713 .await?;
714
715 let u1 = todo.user().exec(&mut db).await?;
716
717 let u2 = User::create().name("User 2").exec(&mut db).await?;
718
719 todo.update().user(&u2).exec(&mut db).await?;
721
722 let todo_reload = Todo::get_by_id(&mut db, &todo.id).await?;
723
724 assert_eq!(u2.id, todo_reload.user_id);
725
726 let todos: Vec<_> = u1.todos().exec(&mut db).await?;
728 assert_eq!(0, todos.len());
729
730 let todos: Vec<_> = u2.todos().exec(&mut db).await?;
732 assert_eq!(1, todos.len());
733 assert_eq!(todo.id, todos[0].id);
734 Ok(())
735}
736
737#[driver_test(id(ID), scenario(crate::scenarios::has_many_belongs_to))]
738pub async fn assign_todo_to_user_on_update_query(test: &mut Test) -> Result<()> {
739 let mut db = setup(test).await;
740
741 let user = User::create().name("User 1").exec(&mut db).await?;
742
743 User::filter_by_id(user.id)
744 .update()
745 .todos(toasty::stmt::insert(Todo::create().title("hello")))
746 .exec(&mut db)
747 .await?;
748
749 let todos: Vec<_> = user.todos().exec(&mut db).await?;
750 assert_eq!(1, todos.len());
751 assert_eq!("hello", todos[0].title);
752 Ok(())
753}
754
755#[driver_test(id(ID))]
756pub async fn has_many_when_fk_is_composite_with_snippets(test: &mut Test) -> Result<()> {
757 #[derive(Debug, toasty::Model)]
758 struct User {
759 #[key]
760 #[auto]
761 id: ID,
762
763 #[has_many]
764 todos: toasty::HasMany<Todo>,
765 }
766
767 #[derive(Debug, toasty::Model)]
768 #[key(partition = user_id, local = id)]
769 struct Todo {
770 #[auto]
771 id: uuid::Uuid,
772
773 user_id: ID,
774
775 #[belongs_to(key = user_id, references = id)]
776 user: toasty::BelongsTo<User>,
777
778 title: String,
779 }
780
781 let mut db = test.setup_db(models!(User, Todo)).await;
782
783 let user1 = User::create().exec(&mut db).await?;
785 let user2 = User::create().exec(&mut db).await?;
786
787 user1
789 .todos()
790 .create()
791 .title("hello world")
792 .exec(&mut db)
793 .await?;
794
795 let todo2 = user2
796 .todos()
797 .create()
798 .title("hello world")
799 .exec(&mut db)
800 .await?;
801
802 Todo::update_by_user_id(user1.id)
804 .title("Title 2")
805 .exec(&mut db)
806 .await?;
807
808 let todo = Todo::get_by_user_id(&mut db, user1.id).await?;
809 assert!(todo.title == "Title 2");
810
811 Todo::update_by_user_id_and_id(user2.id, todo2.id)
812 .title("Title 3")
813 .exec(&mut db)
814 .await?;
815
816 let todo = Todo::get_by_user_id_and_id(&mut db, user2.id, todo2.id).await?;
817 assert!(todo.title == "Title 3");
818
819 Todo::delete_by_user_id(&mut db, user1.id).await?;
821 assert_err!(Todo::get_by_user_id(&mut db, user1.id).await);
822
823 Todo::delete_by_user_id_and_id(&mut db, user2.id, todo2.id).await?;
824 assert_err!(Todo::get_by_user_id_and_id(&mut db, user2.id, todo2.id).await);
825
826 Ok(())
827}