Skip to main content

toasty_driver_integration_suite/tests/
relation_has_many_crud.rs

1//! Test basic has_many associations without any preloading of associations
2//! during query time. All associations are accessed via queries on demand.
3
4use 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    // Create a user
12    let user = User::create().name("User 1").exec(&mut db).await?;
13
14    // No TODOs
15    assert_eq!(0, user.todos().exec(&mut db).await?.len());
16
17    // Create a Todo associated with the user
18    let todo = user
19        .todos()
20        .create()
21        .title("hello world")
22        .exec(&mut db)
23        .await?;
24
25    // Find the todo by ID
26    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    // Find the TODO by user ID
32    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    // Find the User using the Todo
38    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    // Create a few more TODOs
46    for i in 0..5 {
47        let title = format!("hello world {i}");
48
49        let todo = if i.is_even() {
50            // Create via user
51            user.todos().create().title(title).exec(&mut db).await?
52        } else {
53            // Create via todo builder
54            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    // Load all TODOs
66    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    // Find all TODOs by user (using the belongs_to queries)
78    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    // Create a second user
90    let user2 = User::create().name("User 2").exec(&mut db).await?;
91
92    // No TODOs associated with `user2`
93    assert_eq!(0, user2.todos().exec(&mut db).await?.len());
94
95    // Create a TODO for user2
96    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    // Delete a TODO by value
112    let todo = Todo::get_by_id(&mut db, &ids[0]).await?;
113    todo.delete().exec(&mut db).await?;
114
115    // Can no longer get the todo via id
116    assert_err!(Todo::get_by_id(&mut db, &ids[0]).await);
117
118    // Can no longer get the todo scoped
119    assert_err!(user.todos().get_by_id(&mut db, &ids[0]).await);
120
121    // Delete a TODO by scope
122    user.todos()
123        .filter_by_id(ids[1])
124        .delete()
125        .exec(&mut db)
126        .await?;
127
128    // Can no longer get the todo via id
129    assert_err!(Todo::get_by_id(&mut db, &ids[1]).await);
130
131    // Can no longer get the todo scoped
132    assert_err!(user.todos().get_by_id(&mut db, &ids[1]).await);
133
134    // Successfuly a todo by scope
135    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    // Now fail to update it by scoping by other user
146    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    // Delete the user and associated TODOs are deleted
160    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    // Create a user, no TODOs
171    let mut user = User::create().name("Alice").exec(&mut db).await?;
172    assert!(user.todos().exec(&mut db).await?.is_empty());
173
174    // Update the user and create a todo in a batch
175    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/// `stmt::apply([])` on a has-many is a no-op: the surface API's empty
189/// Apply loop adds no entry to the assignments map, so the relation
190/// field is treated as unchanged. Run alongside a separate scalar
191/// change because the engine verifier rejects updates with no
192/// assignments at all.
193#[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    // Create a couple of users
248    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    // Create a todo
252    let todo = user1
253        .todos()
254        .create()
255        .title("hello world")
256        .exec(&mut db)
257        .await?;
258
259    // Find it scoped by user1
260    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    // Trying to find the same todo scoped by user2 is missing
265    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    // Deleting the TODO from the user 2 scope fails
283    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// The has_many association uses the target's primary key as the association's
295// foreign key. In this case, the relation's query should not be duplicated.
296#[driver_test(id(ID))]
297pub async fn has_many_on_target_pk(_test: &mut Test) {}
298
299// The target model has an explicit index on (FK, PK). In this case, the query
300// generated by the (FK, PK) pair should not be duplicated by the relation.
301#[driver_test(id(ID))]
302pub async fn has_many_when_target_indexes_fk_and_pk(_test: &mut Test) {}
303
304// When the FK is composite, things should still work
305#[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    // Create a user
334    let user = User::create().exec(&mut db).await?;
335
336    // No TODOs
337    assert_eq!(0, user.todos().exec(&mut db).await?.len());
338
339    // Create a Todo associated with the user
340    let todo = user
341        .todos()
342        .create()
343        .title("hello world")
344        .exec(&mut db)
345        .await?;
346
347    // Find the todo by ID
348    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    // Find the TODO by user ID
356    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    // Create a few more TODOs
366    for i in 0..5 {
367        let title = format!("hello world {i}");
368
369        let todo = if i.is_even() {
370            // Create via user
371            user.todos().create().title(title).exec(&mut db).await?
372        } else {
373            // Create via todo builder
374            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    // Load all TODOs
386    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    // Find all TODOs by user (using the belongs_to queries)
398    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    // Create a second user
410    let user2 = User::create().exec(&mut db).await?;
411
412    // No TODOs associated with `user2`
413    assert_eq!(0, user2.todos().exec(&mut db).await?.len());
414
415    // Create a TODO for user2
416    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    // Delete a TODO by value
430    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    // Can no longer get the todo via id
434    assert_err!(Todo::get_by_user_id_and_id(&mut db, &user.id, &ids[0]).await);
435
436    // Can no longer get the todo scoped
437    assert_err!(user.todos().get_by_id(&mut db, &ids[0]).await);
438
439    // Delete a TODO by scope
440    user.todos()
441        .filter_by_id(ids[1])
442        .delete()
443        .exec(&mut db)
444        .await?;
445
446    // Can no longer get the todo via id
447    assert_err!(Todo::get_by_user_id_and_id(&mut db, &user.id, &ids[1]).await);
448
449    // Can no longer get the todo scoped
450    assert_err!(user.todos().get_by_id(&mut db, &ids[1]).await);
451
452    // Successfuly a todo by scope
453    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    // Now fail to update it by scoping by other user
463    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// When the PK is composite, things should still work
476#[driver_test(id(ID))]
477pub async fn has_many_when_pk_is_composite(_test: &mut Test) {}
478
479// When both the FK and PK are composite, things should still work
480#[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    // Delete the user
526    user.delete().exec(&mut db).await?;
527
528    // All the todos still exist and `user` is set to `None`.
529    for id in ids {
530        let todo = Todo::get_by_id(&mut db, id).await?;
531        assert_none!(todo.user_id);
532    }
533
534    // Deleting a user leaves the todo in place.
535    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    // Create a user with a todo
543    let u1 = User::create()
544        .name("User 1")
545        .todo(Todo::create().title("hello world"))
546        .exec(&mut db)
547        .await?;
548
549    // Get the todo
550    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    // Create a user with a todo
568    let u1 = User::create()
569        .name("User 1")
570        .todo(Todo::create().title("a todo"))
571        .exec(&mut db)
572        .await?;
573
574    // Get the todo
575    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    // Create a user with a todo
618    let u1 = User::create().todo(Todo::create()).exec(&mut db).await?;
619
620    // Get the todo
621    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    // Updating the todo w/ null is an error. Thus requires a bit of a hack to make work
626    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    // User is not deleted
632    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    // First user has no todos
660    let todos: Vec<_> = u1.todos().exec(&mut db).await?;
661    assert_eq!(0, todos.len());
662
663    // Second user has the todo
664    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    // Update the user
685    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    // First user has no todos
695    let todos: Vec<_> = u1.todos().exec(&mut db).await?;
696    assert_eq!(0, todos.len());
697
698    // Second user has the todo
699    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    // Update the todo
720    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    // First user has no todos
727    let todos: Vec<_> = u1.todos().exec(&mut db).await?;
728    assert_eq!(0, todos.len());
729
730    // Second user has the todo
731    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    // Create users
784    let user1 = User::create().exec(&mut db).await?;
785    let user2 = User::create().exec(&mut db).await?;
786
787    // Create a Todo associated with the user
788    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    // Update the Todos with the snippets
803    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    // Delete the Todos with the snippets
820    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}