1use crate::prelude::*;
20use hashbrown::HashMap;
21
22#[driver_test]
26pub async fn composite_belongs_to_missing_index_is_error(test: &mut Test) -> Result<()> {
27 #[derive(Debug, toasty::Model)]
28 #[key(id, revision)]
29 struct Parent {
30 id: String,
31 revision: i64,
32
33 #[has_many]
34 children: toasty::HasMany<Child>,
35 }
36
37 #[derive(Debug, toasty::Model)]
38 struct Child {
39 #[key]
40 id: String,
41
42 #[index]
45 parent_id: String,
46 #[index]
47 parent_revision: i64,
48
49 #[belongs_to(key = [parent_id, parent_revision], references = [id, revision])]
50 parent: toasty::BelongsTo<Parent>,
51 }
52
53 let err = test
54 .try_setup_db(models!(Parent, Child))
55 .await
56 .expect_err("schema verification should reject this layout");
57
58 assert!(
59 err.is_invalid_schema(),
60 "expected invalid_schema error, got: {err}",
61 );
62
63 let msg = err.to_string();
64 assert!(
65 msg.contains("parent_id") && msg.contains("parent_revision"),
66 "error should mention the foreign-key fields, got: {msg}",
67 );
68 assert!(
69 msg.contains("#[index(parent_id, parent_revision)]"),
70 "error should suggest adding a composite index, got: {msg}",
71 );
72 assert!(
76 msg.contains("each foreign-key field already has its own `#[index]`"),
77 "error should call out the per-field `#[index]` annotations, got: {msg}",
78 );
79
80 Ok(())
81}
82
83#[driver_test(id(ID), scenario(crate::scenarios::composite_has_many_belongs_to))]
89pub async fn composite_crud_user_todos(test: &mut Test) -> Result<()> {
90 let mut db = setup(test).await;
91
92 let user = User::create().name("User 1").exec(&mut db).await?;
93
94 assert_eq!(0, user.todos().exec(&mut db).await?.len());
95
96 let todo = user
97 .todos()
98 .create()
99 .title("hello world")
100 .exec(&mut db)
101 .await?;
102
103 let list = Todo::filter_by_user_id_and_id(user.id, todo.id)
105 .exec(&mut db)
106 .await?;
107 assert_eq!(1, list.len());
108 assert_eq!(todo.id, list[0].id);
109
110 let list = Todo::filter_by_user_id(user.id).exec(&mut db).await?;
112 assert_eq!(1, list.len());
113 assert_eq!(todo.id, list[0].id);
114
115 let user_reload = User::get_by_id(&mut db, &todo.user_id).await?;
117 assert_eq!(user.id, user_reload.id);
118
119 let mut created = HashMap::new();
120 let mut ids = vec![todo.id];
121 created.insert(todo.id, todo);
122
123 for i in 0..5 {
124 let title = format!("hello world {i}");
125 let todo = if i.is_even() {
126 user.todos().create().title(title).exec(&mut db).await?
127 } else {
128 Todo::create()
129 .user(&user)
130 .title(title)
131 .exec(&mut db)
132 .await?
133 };
134 ids.push(todo.id);
135 assert_none!(created.insert(todo.id, todo));
136 }
137
138 let list = user.todos().exec(&mut db).await?;
139 assert_eq!(6, list.len());
140
141 let loaded: HashMap<_, _> = list.into_iter().map(|t| (t.id, t)).collect();
142 assert_eq!(6, loaded.len());
143 for (id, expect) in &created {
144 assert_eq!(expect.title, loaded[id].title);
145 }
146
147 let list = Todo::filter_by_user_id(user.id).exec(&mut db).await?;
148 assert_eq!(6, list.len());
149
150 let user2 = User::create().name("User 2").exec(&mut db).await?;
151 assert_eq!(0, user2.todos().exec(&mut db).await?.len());
152
153 let u2_todo = user2
154 .todos()
155 .create()
156 .title("user 2 todo")
157 .exec(&mut db)
158 .await?;
159
160 for todo in user.todos().exec(&mut db).await? {
161 assert_ne!(u2_todo.id, todo.id);
162 }
163
164 let todo = Todo::get_by_user_id_and_id(&mut db, &user.id, &ids[0]).await?;
166 todo.delete().exec(&mut db).await?;
167 assert_err!(Todo::get_by_user_id_and_id(&mut db, &user.id, &ids[0]).await);
168 assert_err!(user.todos().get_by_id(&mut db, &ids[0]).await);
169
170 user.todos()
172 .filter_by_id(ids[1])
173 .delete()
174 .exec(&mut db)
175 .await?;
176 assert_err!(Todo::get_by_user_id_and_id(&mut db, &user.id, &ids[1]).await);
177 assert_err!(user.todos().get_by_id(&mut db, &ids[1]).await);
178
179 user.todos()
181 .filter_by_id(ids[2])
182 .update()
183 .title("batch update 1")
184 .exec(&mut db)
185 .await?;
186 let todo = Todo::get_by_user_id_and_id(&mut db, &user.id, &ids[2]).await?;
187 assert_eq!(todo.title, "batch update 1");
188
189 user2
191 .todos()
192 .filter_by_id(ids[2])
193 .update()
194 .title("batch update 2")
195 .exec(&mut db)
196 .await?;
197 let todo = Todo::get_by_user_id_and_id(&mut db, &user.id, &ids[2]).await?;
198 assert_eq!(todo.title, "batch update 1");
199
200 let id = user.id;
202 user.delete().exec(&mut db).await?;
203 assert_err!(User::get_by_id(&mut db, &id).await);
204 assert_err!(Todo::get_by_user_id_and_id(&mut db, &id, &ids[2]).await);
205 Ok(())
206}
207
208#[driver_test(id(ID), scenario(crate::scenarios::composite_has_many_belongs_to))]
209pub async fn composite_has_many_insert_on_update(test: &mut Test) -> Result<()> {
210 let mut db = setup(test).await;
211
212 let mut user = User::create().name("Alice").exec(&mut db).await?;
213 assert!(user.todos().exec(&mut db).await?.is_empty());
214
215 user.update()
216 .name("Bob")
217 .todos(toasty::stmt::insert(Todo::create().title("change name")))
218 .exec(&mut db)
219 .await?;
220
221 assert_eq!("Bob", user.name);
222 let todos: Vec<_> = user.todos().exec(&mut db).await?;
223 assert_eq!(1, todos.len());
224 assert_eq!(todos[0].title, "change name");
225 Ok(())
226}
227
228#[driver_test(id(ID), scenario(crate::scenarios::composite_has_many_belongs_to))]
229pub async fn composite_scoped_find_by_id(test: &mut Test) -> Result<()> {
230 let mut db = setup(test).await;
231
232 let user1 = User::create().name("User 1").exec(&mut db).await?;
233 let user2 = User::create().name("User 2").exec(&mut db).await?;
234
235 let todo = user1
236 .todos()
237 .create()
238 .title("hello world")
239 .exec(&mut db)
240 .await?;
241
242 let reloaded = user1.todos().get_by_id(&mut db, &todo.id).await?;
243 assert_eq!(reloaded.id, todo.id);
244 assert_eq!(reloaded.title, todo.title);
245
246 assert_none!(
248 user2
249 .todos()
250 .filter_by_id(todo.id)
251 .first()
252 .exec(&mut db)
253 .await?
254 );
255
256 let reloaded = User::filter_by_id(user1.id)
257 .todos()
258 .get_by_id(&mut db, &todo.id)
259 .await?;
260 assert_eq!(reloaded.id, todo.id);
261
262 user2
264 .todos()
265 .filter_by_id(todo.id)
266 .delete()
267 .exec(&mut db)
268 .await?;
269 let reloaded = user1.todos().get_by_id(&mut db, &todo.id).await?;
270 assert_eq!(reloaded.id, todo.id);
271 Ok(())
272}
273
274#[driver_test(id(ID), scenario(crate::scenarios::composite_has_many_belongs_to))]
275pub async fn composite_belongs_to_required(test: &mut Test) {
276 let mut db = setup(test).await;
277 assert_err!(Todo::create().title("orphan").exec(&mut db).await);
278}
279
280#[driver_test(id(ID))]
281pub async fn composite_delete_when_belongs_to_optional(test: &mut Test) -> Result<()> {
282 #[derive(Debug, toasty::Model)]
283 struct User {
284 #[key]
285 #[auto]
286 id: ID,
287
288 #[has_many]
289 todos: toasty::HasMany<Todo>,
290 }
291
292 #[derive(Debug, toasty::Model)]
297 struct Todo {
298 #[key]
299 #[auto]
300 id: uuid::Uuid,
301
302 #[index]
303 user_id: Option<ID>,
304
305 #[belongs_to(key = user_id, references = id)]
306 user: toasty::BelongsTo<Option<User>>,
307 }
308
309 let mut db = test.setup_db(models!(User, Todo)).await;
310
311 let user = User::create().exec(&mut db).await?;
312 let mut ids = vec![];
313
314 for _ in 0..3 {
315 let todo = user.todos().create().exec(&mut db).await?;
316 ids.push(todo.id);
317 }
318
319 user.delete().exec(&mut db).await?;
320
321 for id in ids {
323 let todo = Todo::get_by_id(&mut db, id).await?;
324 assert_none!(todo.user_id);
325 }
326 Ok(())
327}
328
329#[driver_test(scenario(crate::scenarios::composite_fk_has_many_belongs_to))]
330pub async fn composite_associate_new_user_with_todo_on_update_via_creation(
331 test: &mut Test,
332) -> Result<()> {
333 let mut db = setup(test).await;
334
335 let u1 = User::create()
336 .revision(1)
337 .name("User 1")
338 .todo(Todo::create().title("hello world"))
339 .exec(&mut db)
340 .await?;
341
342 let todos: Vec<_> = u1.todos().exec(&mut db).await?;
343 assert_eq!(1, todos.len());
344 let mut todo = todos.into_iter().next().unwrap();
345
346 todo.update()
347 .user(User::create().revision(1).name("User 2"))
348 .exec(&mut db)
349 .await?;
350 Ok(())
351}
352
353#[driver_test(scenario(crate::scenarios::composite_fk_has_many_belongs_to))]
354pub async fn composite_associate_new_user_with_todo_on_update_query_via_creation(
355 test: &mut Test,
356) -> Result<()> {
357 let mut db = setup(test).await;
358
359 let u1 = User::create()
360 .revision(1)
361 .name("User 1")
362 .todo(Todo::create().title("a todo"))
363 .exec(&mut db)
364 .await?;
365
366 let todos: Vec<_> = u1.todos().exec(&mut db).await?;
367 assert_eq!(1, todos.len());
368 let todo = todos.into_iter().next().unwrap();
369
370 Todo::filter_by_id(todo.id)
371 .update()
372 .user(User::create().revision(1).name("User 2"))
373 .exec(&mut db)
374 .await?;
375 Ok(())
376}
377
378#[driver_test(scenario(crate::scenarios::composite_fk_has_many_belongs_to))]
379pub async fn composite_assign_todo_that_already_has_user_on_create(test: &mut Test) -> Result<()> {
380 let mut db = setup(test).await;
381
382 let todo = Todo::create()
383 .title("a todo")
384 .user(User::create().revision(1).name("User 1"))
385 .exec(&mut db)
386 .await?;
387
388 let u1 = todo.user().exec(&mut db).await?;
389
390 let u2 = User::create()
391 .revision(1)
392 .name("User 2")
393 .todo(&todo)
394 .exec(&mut db)
395 .await?;
396
397 let todo_reload = Todo::get_by_id(&mut db, &todo.id).await?;
398 assert_eq!(u2.id, todo_reload.user_id);
399 assert_eq!(u2.revision, todo_reload.user_revision);
400
401 let todos: Vec<_> = u1.todos().exec(&mut db).await?;
402 assert_eq!(0, todos.len());
403
404 let todos: Vec<_> = u2.todos().exec(&mut db).await?;
405 assert_eq!(1, todos.len());
406 assert_eq!(todo.id, todos[0].id);
407 Ok(())
408}
409
410#[driver_test(scenario(crate::scenarios::composite_fk_has_many_belongs_to))]
411pub async fn composite_assign_todo_that_already_has_user_on_update(test: &mut Test) -> Result<()> {
412 let mut db = setup(test).await;
413
414 let todo = Todo::create()
415 .title("a todo")
416 .user(User::create().revision(1).name("User 1"))
417 .exec(&mut db)
418 .await?;
419
420 let u1 = todo.user().exec(&mut db).await?;
421
422 let mut u2 = User::create()
423 .revision(1)
424 .name("User 2")
425 .exec(&mut db)
426 .await?;
427
428 u2.update()
429 .todos(toasty::stmt::insert(&todo))
430 .exec(&mut db)
431 .await?;
432
433 let todo_reload = Todo::get_by_id(&mut db, &todo.id).await?;
434 assert_eq!(u2.id, todo_reload.user_id);
435 assert_eq!(u2.revision, todo_reload.user_revision);
436
437 let todos: Vec<_> = u1.todos().exec(&mut db).await?;
438 assert_eq!(0, todos.len());
439
440 let todos: Vec<_> = u2.todos().exec(&mut db).await?;
441 assert_eq!(1, todos.len());
442 assert_eq!(todo.id, todos[0].id);
443 Ok(())
444}
445
446#[driver_test(scenario(crate::scenarios::composite_fk_has_many_belongs_to))]
447pub async fn composite_assign_existing_user_to_todo(test: &mut Test) -> Result<()> {
448 let mut db = setup(test).await;
449
450 let mut todo = Todo::create()
451 .title("hello")
452 .user(User::create().revision(1).name("User 1"))
453 .exec(&mut db)
454 .await?;
455
456 let u1 = todo.user().exec(&mut db).await?;
457 let u2 = User::create()
458 .revision(1)
459 .name("User 2")
460 .exec(&mut db)
461 .await?;
462
463 todo.update().user(&u2).exec(&mut db).await?;
464
465 let todo_reload = Todo::get_by_id(&mut db, &todo.id).await?;
466 assert_eq!(u2.id, todo_reload.user_id);
467 assert_eq!(u2.revision, todo_reload.user_revision);
468
469 let todos: Vec<_> = u1.todos().exec(&mut db).await?;
470 assert_eq!(0, todos.len());
471
472 let todos: Vec<_> = u2.todos().exec(&mut db).await?;
473 assert_eq!(1, todos.len());
474 assert_eq!(todo.id, todos[0].id);
475 Ok(())
476}
477
478#[driver_test(id(ID), scenario(crate::scenarios::composite_has_many_belongs_to))]
479pub async fn composite_assign_todo_to_user_on_update_query(test: &mut Test) -> Result<()> {
480 let mut db = setup(test).await;
481
482 let user = User::create().name("User 1").exec(&mut db).await?;
483
484 User::filter_by_id(user.id)
485 .update()
486 .todos(toasty::stmt::insert(Todo::create().title("hello")))
487 .exec(&mut db)
488 .await?;
489
490 let todos: Vec<_> = user.todos().exec(&mut db).await?;
491 assert_eq!(1, todos.len());
492 assert_eq!("hello", todos[0].title);
493 Ok(())
494}
495
496#[driver_test(id(ID), scenario(crate::scenarios::composite_has_many_belongs_to))]
502pub async fn composite_user_batch_create_todos_one_level(test: &mut Test) -> Result<()> {
503 let mut db = setup(test).await;
504
505 let user = User::create()
506 .name("Ann Chovey")
507 .todo(Todo::create().title("Make pizza"))
508 .exec(&mut db)
509 .await?;
510
511 assert_eq!(user.name, "Ann Chovey");
512
513 let todos: Vec<_> = user.todos().exec(&mut db).await?;
514 assert_eq!(1, todos.len());
515 assert_eq!("Make pizza", todos[0].title);
516
517 let todo = Todo::get_by_user_id_and_id(&mut db, &user.id, &todos[0].id).await?;
518 assert_eq!("Make pizza", todo.title);
519 Ok(())
520}
521
522#[driver_test(id(ID), scenario(crate::scenarios::composite_has_many_belongs_to))]
523pub async fn composite_user_batch_create_two_todos_simple(test: &mut Test) -> Result<()> {
524 let mut db = setup(test).await;
525
526 let user = User::create()
527 .name("Ann Chovey")
528 .todo(Todo::create().title("Make pizza"))
529 .todo(Todo::create().title("Sleep"))
530 .exec(&mut db)
531 .await?;
532
533 let todos: Vec<_> = user.todos().exec(&mut db).await?;
534 assert_eq!(2, todos.len());
535
536 let mut titles: Vec<_> = todos.iter().map(|t| &t.title[..]).collect();
537 titles.sort();
538 assert_eq!(titles, vec!["Make pizza", "Sleep"]);
539 Ok(())
540}
541
542#[driver_test(id(ID))]
543pub async fn composite_user_batch_create_todos_with_optional_field(test: &mut Test) -> Result<()> {
544 #[derive(Debug, toasty::Model)]
545 struct User {
546 #[key]
547 #[auto]
548 id: ID,
549
550 name: String,
551
552 #[has_many]
553 todos: toasty::HasMany<Todo>,
554
555 #[allow(dead_code)]
558 moto: Option<String>,
559 }
560
561 #[derive(Debug, toasty::Model)]
562 #[key(partition = user_id, local = id)]
563 struct Todo {
564 #[auto]
565 id: uuid::Uuid,
566
567 user_id: ID,
568
569 #[belongs_to(key = user_id, references = id)]
570 user: toasty::BelongsTo<User>,
571
572 title: String,
573 }
574
575 let mut db = test.setup_db(models!(User, Todo)).await;
576
577 let user = User::create()
578 .name("Ann Chovey")
579 .todo(Todo::create().title("Make pizza"))
580 .todo(Todo::create().title("Sleep"))
581 .exec(&mut db)
582 .await?;
583
584 let todos: Vec<_> = user.todos().exec(&mut db).await?;
585 assert_eq!(2, todos.len());
586
587 let mut titles: Vec<_> = todos.iter().map(|t| &t.title[..]).collect();
588 titles.sort();
589 assert_eq!(titles, vec!["Make pizza", "Sleep"]);
590 Ok(())
591}
592
593#[driver_test(id(ID))]
594pub async fn composite_remove_add_single_relation_option_belongs_to(test: &mut Test) -> Result<()> {
595 #[derive(Debug, toasty::Model)]
596 struct User {
597 #[key]
598 #[auto]
599 id: ID,
600
601 #[has_many]
602 todos: toasty::HasMany<Todo>,
603 }
604
605 #[derive(Debug, toasty::Model)]
606 struct Todo {
607 #[key]
608 #[auto]
609 id: uuid::Uuid,
610
611 #[index]
612 user_id: Option<ID>,
613
614 #[belongs_to(key = user_id, references = id)]
615 user: toasty::BelongsTo<Option<User>>,
616 }
617
618 let mut db = test.setup_db(models!(User, Todo)).await;
619
620 let user = User::create()
621 .todo(Todo::create())
622 .todo(Todo::create())
623 .exec(&mut db)
624 .await?;
625
626 let todos: Vec<_> = user.todos().exec(&mut db).await?;
627 assert_eq!(2, todos.len());
628
629 user.todos().remove(&mut db, &todos[0]).await?;
630
631 let todos_reloaded: Vec<_> = user.todos().exec(&mut db).await?;
632 assert_eq!(1, todos_reloaded.len());
633 assert_eq!(todos[1].id, todos_reloaded[0].id);
634
635 assert_err!(user.todos().get_by_id(&mut db, &todos[0].id).await);
636
637 let todo = Todo::get_by_id(&mut db, todos[0].id).await?;
638 assert_none!(todo.user_id);
639
640 user.todos().insert(&mut db, &todos[0]).await?;
641
642 let todos_reloaded: Vec<_> = user.todos().exec(&mut db).await?;
643 assert!(todos_reloaded.iter().any(|t| t.id == todos[0].id));
644 assert_ok!(user.todos().get_by_id(&mut db, todos[0].id).await);
645 Ok(())
646}
647
648#[driver_test(id(ID), scenario(crate::scenarios::composite_has_many_belongs_to))]
649pub async fn composite_add_remove_single_relation_required_belongs_to(
650 test: &mut Test,
651) -> Result<()> {
652 let mut db = setup(test).await;
653
654 let user = User::create().name("User 1").exec(&mut db).await?;
655
656 let t1 = user.todos().create().title("todo 1").exec(&mut db).await?;
657 let t2 = user.todos().create().title("todo 2").exec(&mut db).await?;
658 let t3 = user.todos().create().title("todo 3").exec(&mut db).await?;
659
660 let ids = vec![t1.id, t2.id, t3.id];
661
662 let todos_reloaded: Vec<_> = user.todos().exec(&mut db).await?;
663 assert_eq!(todos_reloaded.len(), 3);
664
665 for id in ids {
666 assert!(todos_reloaded.iter().any(|t| t.id == id));
667 }
668
669 user.todos().remove(&mut db, &todos_reloaded[0]).await?;
671
672 assert_err!(Todo::get_by_user_id_and_id(&mut db, &user.id, &todos_reloaded[0].id).await);
673
674 let todos_reloaded: Vec<_> = user.todos().exec(&mut db).await?;
675 assert_eq!(todos_reloaded.len(), 2);
676 Ok(())
677}
678
679#[driver_test(scenario(crate::scenarios::composite_fk_has_many_belongs_to))]
680pub async fn composite_reassign_relation_required_belongs_to(test: &mut Test) -> Result<()> {
681 let mut db = setup(test).await;
682
683 let u1 = User::create()
684 .revision(1)
685 .name("User 1")
686 .exec(&mut db)
687 .await?;
688 let u2 = User::create()
689 .revision(1)
690 .name("User 2")
691 .exec(&mut db)
692 .await?;
693
694 let t1 = u1.todos().create().title("a todo").exec(&mut db).await?;
695
696 u2.todos().insert(&mut db, &t1).await?;
697
698 assert!(u1.todos().exec(&mut db).await?.is_empty());
699
700 let todos = u2.todos().exec(&mut db).await?;
701 assert_eq!(1, todos.len());
702 assert_eq!(t1.id, todos[0].id);
703 Ok(())
704}
705
706#[driver_test(id(ID))]
707pub async fn composite_add_remove_multiple_relation_option_belongs_to(
708 test: &mut Test,
709) -> Result<()> {
710 #[derive(Debug, toasty::Model)]
711 struct User {
712 #[key]
713 #[auto]
714 id: ID,
715
716 #[has_many]
717 todos: toasty::HasMany<Todo>,
718 }
719
720 #[derive(Debug, toasty::Model)]
721 struct Todo {
722 #[key]
723 #[auto]
724 id: uuid::Uuid,
725
726 #[index]
727 user_id: Option<ID>,
728
729 #[belongs_to(key = user_id, references = id)]
730 user: toasty::BelongsTo<Option<User>>,
731 }
732
733 let mut db = test.setup_db(models!(User, Todo)).await;
734
735 let user = User::create().exec(&mut db).await?;
736
737 let t1 = Todo::create().exec(&mut db).await?;
738 let t2 = Todo::create().exec(&mut db).await?;
739 let t3 = Todo::create().exec(&mut db).await?;
740
741 let ids = vec![t1.id, t2.id, t3.id];
742
743 user.todos().insert(&mut db, &t1).await?;
744 user.todos().insert(&mut db, &t2).await?;
745 user.todos().insert(&mut db, &t3).await?;
746
747 let todos_reloaded: Vec<_> = user.todos().exec(&mut db).await?;
748 assert_eq!(todos_reloaded.len(), 3);
749
750 for id in ids {
751 assert!(todos_reloaded.iter().any(|t| t.id == id));
752 }
753 Ok(())
754}
755
756#[driver_test(id(ID), scenario(crate::scenarios::composite_has_many_belongs_to))]
762pub async fn composite_basic_has_many_and_belongs_to_preload(test: &mut Test) -> Result<()> {
763 let mut db = setup(test).await;
764
765 let user = User::create()
766 .name("Alice")
767 .todo(Todo::create().title("todo 1"))
768 .todo(Todo::create().title("todo 2"))
769 .todo(Todo::create().title("todo 3"))
770 .exec(&mut db)
771 .await?;
772
773 let user = User::filter_by_id(user.id)
774 .include(User::fields().todos())
775 .get(&mut db)
776 .await?;
777
778 assert_eq!(3, user.todos.get().len());
779
780 let id = user.todos.get()[0].id;
781 let user_id = user.todos.get()[0].user_id;
782
783 let todo = Todo::filter_by_user_id_and_id(user_id, id)
784 .include(Todo::fields().user())
785 .get(&mut db)
786 .await?;
787
788 assert_eq!(user.id, todo.user.get().id);
789 assert_eq!(user.id, todo.user_id);
790 Ok(())
791}
792
793#[driver_test(id(ID), scenario(crate::scenarios::composite_has_many_belongs_to))]
794pub async fn composite_preload_on_empty_query(test: &mut Test) -> Result<()> {
795 let mut db = setup(test).await;
796
797 User::create().name("Alice").exec(&mut db).await?;
798
799 let users: Vec<_> = User::filter(User::fields().name().eq("Nope"))
800 .include(User::fields().todos())
801 .exec(&mut db)
802 .await?;
803 assert!(users.is_empty());
804 Ok(())
805}
806
807#[driver_test(id(ID))]
808pub async fn composite_preload_has_many_with_optional_belongs_to(test: &mut Test) -> Result<()> {
809 #[derive(Debug, toasty::Model)]
810 struct User {
811 #[key]
812 #[auto]
813 id: ID,
814
815 name: String,
816
817 #[has_many]
818 todos: toasty::HasMany<Todo>,
819 }
820
821 #[derive(Debug, toasty::Model)]
822 struct Todo {
823 #[key]
824 #[auto]
825 id: uuid::Uuid,
826
827 #[index]
828 user_id: Option<ID>,
829
830 #[belongs_to(key = user_id, references = id)]
831 user: toasty::BelongsTo<Option<User>>,
832
833 title: String,
834 }
835
836 let mut db = test.setup_db(models!(User, Todo)).await;
837
838 let user = User::create()
839 .name("Alice")
840 .todo(Todo::create().title("alpha"))
841 .todo(Todo::create().title("beta"))
842 .exec(&mut db)
843 .await?;
844
845 let user = User::filter_by_id(user.id)
846 .include(User::fields().todos())
847 .get(&mut db)
848 .await?;
849
850 assert_eq!(2, user.todos.get().len());
851 Ok(())
852}
853
854#[driver_test(id(ID), scenario(crate::scenarios::composite_has_many_belongs_to))]
855pub async fn composite_nested_has_many_then_belongs_to_required(test: &mut Test) -> Result<()> {
856 let mut db = setup(test).await;
857
858 let user = User::create()
859 .name("Alice")
860 .todo(Todo::create().title("alpha"))
861 .todo(Todo::create().title("beta"))
862 .exec(&mut db)
863 .await?;
864
865 let user = User::filter_by_id(user.id)
866 .include(User::fields().todos().user())
867 .get(&mut db)
868 .await?;
869
870 let todos = user.todos.get();
871 assert_eq!(2, todos.len());
872 for todo in todos {
873 assert_eq!(user.id, todo.user.get().id);
874 assert_eq!("Alice", todo.user.get().name);
875 }
876 Ok(())
877}
878
879#[driver_test(id(ID))]
884pub async fn composite_crud_has_one_required(test: &mut Test) -> Result<()> {
885 #[derive(Debug, toasty::Model)]
889 struct User {
890 #[key]
891 #[auto]
892 id: ID,
893
894 #[has_one]
895 profile: toasty::HasOne<Option<Profile>>,
896 }
897
898 #[derive(Debug, toasty::Model)]
899 #[key(partition = user_id, local = id)]
900 struct Profile {
901 #[auto]
902 id: uuid::Uuid,
903
904 user_id: ID,
905
906 #[belongs_to(key = user_id, references = id)]
907 user: toasty::BelongsTo<User>,
908
909 bio: String,
910 }
911
912 let mut db = test.setup_db(models!(User, Profile)).await;
913
914 let user = User::create()
915 .profile(Profile::create().bio("an apple a day"))
916 .exec(&mut db)
917 .await?;
918
919 let profile = user.profile().exec(&mut db).await?.unwrap();
920 assert_eq!(profile.bio, "an apple a day");
921
922 assert_eq!(user.id, profile.user().exec(&mut db).await?.id);
923
924 user.delete().exec(&mut db).await?;
926 assert_err!(Profile::get_by_user_id_and_id(&mut db, &profile.user_id, &profile.id).await);
927 Ok(())
928}
929
930#[driver_test(id(ID), requires(sql))]
935pub async fn composite_filter_by_belongs_to_field(test: &mut Test) -> Result<()> {
936 #[derive(Debug, toasty::Model)]
937 struct User {
938 #[key]
939 #[auto]
940 id: ID,
941
942 name: String,
943 }
944
945 #[derive(Debug, toasty::Model)]
946 #[key(partition = user_id, local = id)]
947 struct Profile {
948 #[auto]
949 id: uuid::Uuid,
950
951 user_id: ID,
952
953 #[belongs_to(key = user_id, references = id)]
954 user: toasty::BelongsTo<User>,
955
956 bio: String,
957 }
958
959 let mut db = test.setup_db(models!(User, Profile)).await;
960
961 let alice = User::create().name("alice").exec(&mut db).await?;
962 let bob = User::create().name("bob").exec(&mut db).await?;
963
964 Profile::create()
965 .user(&alice)
966 .bio("alice's bio")
967 .exec(&mut db)
968 .await?;
969 Profile::create()
970 .user(&bob)
971 .bio("bob's bio")
972 .exec(&mut db)
973 .await?;
974
975 let profiles: Vec<Profile> = Profile::filter(Profile::fields().user().name().eq("alice"))
976 .exec(&mut db)
977 .await?;
978 assert_eq!(profiles.len(), 1);
979 assert_eq!(profiles[0].bio, "alice's bio");
980 Ok(())
981}
982
983#[driver_test(
984 id(ID),
985 requires(scan),
986 scenario(crate::scenarios::composite_has_many_belongs_to)
987)]
988pub async fn composite_filter_parent_by_child_field(test: &mut Test) -> Result<()> {
989 let mut db = setup(test).await;
990
991 let alice = User::create().name("Alice").exec(&mut db).await?;
992 let bob = User::create().name("Bob").exec(&mut db).await?;
993 let carol = User::create().name("Carol").exec(&mut db).await?;
994
995 alice.todos().create().title("urgent").exec(&mut db).await?;
996 bob.todos().create().title("later").exec(&mut db).await?;
997 carol.todos().create().title("urgent").exec(&mut db).await?;
998 carol.todos().create().title("later").exec(&mut db).await?;
999
1000 let users: Vec<_> = User::filter(
1001 User::fields()
1002 .todos()
1003 .any(Todo::fields().title().eq("urgent")),
1004 )
1005 .exec(&mut db)
1006 .await?;
1007
1008 assert_eq_unordered!(users.iter().map(|u| &u.name[..]), ["Alice", "Carol"]);
1009 Ok(())
1010}
1011
1012#[driver_test(id(ID), requires(scan))]
1013pub async fn composite_select_belongs_to_basic(test: &mut Test) -> Result<()> {
1014 #[derive(Debug, toasty::Model)]
1015 struct User {
1016 #[key]
1017 #[auto]
1018 id: ID,
1019 name: String,
1020 }
1021
1022 #[derive(Debug, toasty::Model)]
1023 #[key(partition = user_id, local = id)]
1024 struct Post {
1025 #[auto]
1026 id: uuid::Uuid,
1027 title: String,
1028
1029 user_id: ID,
1030
1031 #[belongs_to(key = user_id, references = id)]
1032 author: toasty::BelongsTo<User>,
1033 }
1034
1035 let mut db = test.setup_db(models!(User, Post)).await;
1036
1037 let alice = User::create().name("Alice").exec(&mut db).await?;
1038 Post::create()
1039 .title("Hello")
1040 .author(&alice)
1041 .exec(&mut db)
1042 .await?;
1043
1044 let users: Vec<User> = Post::all()
1045 .select(Post::fields().author())
1046 .exec(&mut db)
1047 .await?;
1048
1049 assert_eq!(users.len(), 1);
1050 assert_eq!(users[0].name, "Alice");
1051 Ok(())
1052}