Skip to main content

toasty_driver_integration_suite/tests/
relation_has_one_crud.rs

1use crate::prelude::*;
2
3#[driver_test(id(ID), scenario(crate::scenarios::has_one_optional_belongs_to))]
4pub async fn crud_has_one_bi_direction_optional(test: &mut Test) -> Result<()> {
5    let mut db = setup(test).await;
6
7    // Create a user without a profile
8    let user = User::create().name("Jane Doe").exec(&mut db).await?;
9
10    // No profile
11    assert_none!(user.profile().exec(&mut db).await?);
12
13    // Create a profile for the user
14    let profile = user
15        .profile()
16        .create()
17        .bio("a person")
18        .exec(&mut db)
19        .await?;
20
21    // Load the profile
22    let profile_reload = user.profile().exec(&mut db).await?.unwrap();
23    assert_eq!(profile.id, profile_reload.id);
24
25    // Load the user via the profile
26    let user_reload = profile.user().exec(&mut db).await?.unwrap();
27    assert_eq!(user.id, user_reload.id);
28
29    // Create a new user with a profile
30    let mut user = User::create()
31        .name("Tim Apple")
32        .profile(Profile::create().bio("an apple a day"))
33        .exec(&mut db)
34        .await?;
35
36    let profile = user.profile().exec(&mut db).await?.unwrap();
37    assert_eq!(profile.bio, "an apple a day");
38
39    // The new profile is associated with the user
40    assert_eq!(user.id, profile.user().exec(&mut db).await?.unwrap().id);
41
42    // Update a user, creating a new profile.
43    user.update()
44        .profile(Profile::create().bio("keeps the doctor away"))
45        .exec(&mut db)
46        .await?;
47
48    // The user's profile is updated
49    let profile = user.profile().exec(&mut db).await?.unwrap();
50    assert_eq!(profile.bio, "keeps the doctor away");
51    assert_eq!(user.id, profile.user().exec(&mut db).await?.unwrap().id);
52
53    // Unset the profile via an update. This will nullify user on the profile.
54    user.update().profile(None).exec(&mut db).await?;
55
56    // The profile is none
57    assert!(user.profile().exec(&mut db).await?.is_none());
58
59    let profile_reloaded = Profile::filter_by_id(profile.id).get(&mut db).await?;
60    assert_none!(profile_reloaded.user_id);
61
62    user.update()
63        .profile(&profile_reloaded)
64        .exec(&mut db)
65        .await?;
66
67    let profile_reloaded = Profile::get_by_id(&mut db, &profile.id).await?;
68    assert_eq!(&user.id, profile_reloaded.user_id.as_ref().unwrap());
69
70    // Deleting the profile will nullify the profile field for the user
71    profile_reloaded.delete().exec(&mut db).await?;
72
73    let mut user_reloaded = User::get_by_id(&mut db, &user.id).await?;
74    assert_none!(user_reloaded.profile().exec(&mut db).await?);
75
76    // Create a new profile for the user
77    user_reloaded
78        .update()
79        .profile(Profile::create().bio("hello"))
80        .exec(&mut db)
81        .await?;
82
83    let profile_id = user_reloaded.profile().exec(&mut db).await?.unwrap().id;
84
85    // Delete the user
86    user_reloaded.delete().exec(&mut db).await?;
87
88    let profile_reloaded = Profile::get_by_id(&mut db, &profile_id).await?;
89    assert_none!(profile_reloaded.user_id);
90    Ok(())
91}
92
93#[driver_test(id(ID))]
94#[should_panic]
95pub async fn crud_has_one_required_belongs_to_optional(test: &mut Test) -> Result<()> {
96    #[derive(Debug, toasty::Model)]
97    struct User {
98        #[key]
99        #[auto]
100        id: ID,
101
102        #[has_one]
103        profile: toasty::Deferred<Profile>,
104    }
105
106    #[derive(Debug, toasty::Model)]
107    struct Profile {
108        #[key]
109        #[auto]
110        id: ID,
111
112        #[unique]
113        user_id: Option<ID>,
114
115        #[belongs_to(key = user_id, references = id)]
116        user: toasty::Deferred<Option<User>>,
117
118        bio: String,
119    }
120
121    let mut db = test.setup_db(models!(User, Profile)).await;
122
123    // Create a new user with a profile
124    let user = User::create()
125        .profile(Profile::create().bio("an apple a day"))
126        .exec(&mut db)
127        .await?;
128
129    let profile = user.profile().exec(&mut db).await?;
130    assert_eq!(profile.bio, "an apple a day");
131
132    // The new profile is associated with the user
133    assert_eq!(user.id, profile.user().exec(&mut db).await?.unwrap().id);
134
135    // Deleting the user leaves the profile in place.
136    user.delete().exec(&mut db).await?;
137    let profile_reloaded = Profile::get_by_id(&mut db, &profile.id).await?;
138    assert_none!(profile_reloaded.user_id);
139
140    // Try creating a user **without** a user: error
141    assert_err!(User::create().exec(&mut db).await);
142    Ok(())
143}
144
145#[driver_test(id(ID))]
146pub async fn update_belongs_to_with_required_has_one_pair(test: &mut Test) -> Result<()> {
147    #[derive(Debug, toasty::Model)]
148    struct User {
149        #[key]
150        #[auto]
151        id: ID,
152
153        #[has_one]
154        profile: toasty::Deferred<Profile>,
155    }
156
157    #[derive(Debug, toasty::Model)]
158    struct Profile {
159        #[key]
160        #[auto]
161        id: ID,
162
163        #[unique]
164        user_id: Option<ID>,
165
166        #[belongs_to(key = user_id, references = id)]
167        user: toasty::Deferred<Option<User>>,
168
169        bio: String,
170    }
171
172    let mut db = test.setup_db(models!(User, Profile)).await;
173
174    // Create a user with a profile
175    let u1 = User::create()
176        .profile(Profile::create().bio("an apple a day"))
177        .exec(&mut db)
178        .await?;
179
180    let mut p1 = u1.profile().exec(&mut db).await?;
181    assert_eq!(p1.bio, "an apple a day");
182
183    // Associate the profile with a new user by value
184    let u2 = User::create()
185        .profile(Profile::create().bio("I plant trees"))
186        .exec(&mut db)
187        .await?;
188
189    let p2 = u2.profile().exec(&mut db).await?;
190    assert_eq!(p2.bio, "I plant trees");
191
192    // Associate the original profile w/ the new user by value
193    p1.update().user(&u2).exec(&mut db).await?;
194
195    // assert_eq!(u2.id, p1.user().find(&mut db).await.unwrap().unwrap().id);
196    // u1 is deleted
197    assert_err!(User::get_by_id(&mut db, &u1.id).await);
198    // p2 ID is null
199    let p2_reloaded = Profile::get_by_id(&mut db, &p2.id).await?;
200    assert_none!(p2_reloaded.user_id);
201
202    /*
203    // Associate the profile with a new user by statement
204    let u1 = db::User::create()
205        .name("Tim Apple")
206        .profile(db::Profile::create().bio("an apple a day"))
207        .exec(&mut db)
208        .await
209        .unwrap();
210
211    let mut p1 = u1.profile().exec(&mut db).await.unwrap();
212    assert_eq!(p1.bio, "an apple a day");
213
214    /*
215    // Associate the profile with a new user by value
216    let u2 = db::User::create()
217        .name("Johnny Appleseed")
218        .profile(db::Profile::create().bio("I plant trees"))
219        .exec(&mut db)
220        .await
221        .unwrap();
222
223    let p2 = u2.profile().exec(&mut db).await.unwrap();
224    assert_eq!(p2.bio, "I plant trees");
225    */
226
227    // Associate the original profile w/ the new user by value
228    p1.update()
229        .user(db::User::create().name("Johnny Appleseed"))
230        .exec(&mut db)
231        .await
232        .unwrap();
233
234    assert_eq!(
235        u2.id,
236        p1.user
237            .as_ref()
238            .unwrap()
239            .get(&mut db)
240            .await
241            .unwrap()
242            .unwrap()
243            .id
244    );
245    // u1 is deleted
246    assert_err!(db::User::find_by_id(&u1.id).get(&mut db).await);
247    */
248    Ok(())
249}
250
251#[driver_test(id(ID))]
252pub async fn crud_has_one_optional_belongs_to_required(test: &mut Test) -> Result<()> {
253    #[derive(Debug, toasty::Model)]
254    struct User {
255        #[key]
256        #[auto]
257        id: ID,
258
259        #[has_one]
260        profile: toasty::Deferred<Option<Profile>>,
261    }
262
263    #[derive(Debug, toasty::Model)]
264    struct Profile {
265        #[key]
266        #[auto]
267        id: ID,
268
269        #[unique]
270        user_id: ID,
271
272        #[belongs_to(key = user_id, references = id)]
273        user: toasty::Deferred<User>,
274
275        bio: String,
276    }
277
278    let mut db = test.setup_db(models!(User, Profile)).await;
279
280    // Create a new user with a profile
281    let user = User::create()
282        .profile(Profile::create().bio("an apple a day"))
283        .exec(&mut db)
284        .await?;
285
286    let profile = user.profile().exec(&mut db).await?.unwrap();
287    assert_eq!(profile.bio, "an apple a day");
288
289    // The new profile is associated with the user
290    assert_eq!(user.id, profile.user().exec(&mut db).await?.id);
291
292    // Deleting the user also deletes the profile
293    user.delete().exec(&mut db).await?;
294    assert_err!(Profile::get_by_id(&mut db, &profile.id).await);
295    Ok(())
296}
297
298#[driver_test(id(ID), scenario(crate::scenarios::has_one_optional_belongs_to))]
299pub async fn set_has_one_by_value_in_update_query(test: &mut Test) -> Result<()> {
300    let mut db = setup(test).await;
301
302    let user = User::create().name("Jane Doe").exec(&mut db).await?;
303    let profile = Profile::create().bio("a person").exec(&mut db).await?;
304
305    User::filter_by_id(user.id)
306        .update()
307        .profile(&profile)
308        .exec(&mut db)
309        .await?;
310
311    let profile_reload = user.profile().exec(&mut db).await?.unwrap();
312    assert_eq!(profile_reload.id, profile.id);
313
314    assert_eq!(profile_reload.user_id.as_ref().unwrap(), &user.id);
315    Ok(())
316}
317
318#[driver_test(id(ID))]
319pub async fn unset_has_one_in_batch_update(test: &mut Test) -> Result<()> {
320    #[derive(Debug, toasty::Model)]
321    struct User {
322        #[key]
323        #[auto]
324        id: ID,
325
326        #[index]
327        name: String,
328
329        #[has_one]
330        profile: toasty::Deferred<Option<Profile>>,
331    }
332
333    #[derive(Debug, toasty::Model)]
334    struct Profile {
335        #[key]
336        #[auto]
337        id: ID,
338
339        #[unique]
340        user_id: ID,
341
342        #[belongs_to(key = user_id, references = id)]
343        user: toasty::Deferred<User>,
344    }
345
346    let mut db = test.setup_db(models!(User, Profile)).await;
347
348    // Create two users with the same name, each with a profile
349    let u1 = User::create()
350        .name("alice")
351        .profile(Profile::create())
352        .exec(&mut db)
353        .await?;
354    let p1 = u1.profile().exec(&mut db).await?.unwrap();
355
356    let u2 = User::create()
357        .name("alice")
358        .profile(Profile::create())
359        .exec(&mut db)
360        .await?;
361    let p2 = u2.profile().exec(&mut db).await?.unwrap();
362
363    // A third user with a different name (should not be affected)
364    let u3 = User::create()
365        .name("bob")
366        .profile(Profile::create())
367        .exec(&mut db)
368        .await?;
369
370    // Batch update: unset profiles for all users named "alice"
371    User::filter_by_name("alice")
372        .update()
373        .profile(None)
374        .exec(&mut db)
375        .await?;
376
377    // Both profiles should be deleted (required belongs_to)
378    assert_err!(Profile::get_by_id(&mut db, &p1.id).await);
379    assert_err!(Profile::get_by_id(&mut db, &p2.id).await);
380
381    // Bob's profile should still exist
382    let p3 = u3.profile().exec(&mut db).await?.unwrap();
383    assert_eq!(p3.user_id, u3.id);
384
385    Ok(())
386}
387
388#[driver_test(id(ID))]
389pub async fn unset_has_one_with_required_pair_in_pk_query_update(test: &mut Test) -> Result<()> {
390    #[derive(Debug, toasty::Model)]
391    struct User {
392        #[key]
393        #[auto]
394        id: ID,
395
396        #[has_one]
397        profile: toasty::Deferred<Option<Profile>>,
398    }
399
400    #[derive(Debug, toasty::Model)]
401    struct Profile {
402        #[key]
403        #[auto]
404        id: ID,
405
406        #[unique]
407        user_id: ID,
408
409        #[belongs_to(key = user_id, references = id)]
410        user: toasty::Deferred<User>,
411    }
412
413    let mut db = test.setup_db(models!(User, Profile)).await;
414
415    let user = User::create()
416        .profile(Profile::create())
417        .exec(&mut db)
418        .await?;
419    let profile = user.profile().exec(&mut db).await?.unwrap();
420
421    assert_eq!(user.id, profile.user_id);
422
423    User::filter_by_id(user.id)
424        .update()
425        .profile(None)
426        .exec(&mut db)
427        .await?;
428
429    // Profile is deleted
430    assert_err!(Profile::get_by_id(&mut db, &profile.id).await);
431    Ok(())
432}
433
434#[driver_test(id(ID))]
435pub async fn unset_has_one_with_required_pair_in_non_pk_query_update(
436    test: &mut Test,
437) -> Result<()> {
438    #[derive(Debug, toasty::Model)]
439    struct User {
440        #[key]
441        #[auto]
442        id: ID,
443
444        #[unique]
445        email: String,
446
447        #[has_one]
448        profile: toasty::Deferred<Option<Profile>>,
449    }
450
451    #[derive(Debug, toasty::Model)]
452    struct Profile {
453        #[key]
454        #[auto]
455        id: ID,
456
457        #[unique]
458        user_id: ID,
459
460        #[belongs_to(key = user_id, references = id)]
461        user: toasty::Deferred<User>,
462    }
463
464    let mut db = test.setup_db(models!(User, Profile)).await;
465
466    let user = User::create()
467        .email("foo@example.com")
468        .profile(Profile::create())
469        .exec(&mut db)
470        .await?;
471    let profile = user.profile().exec(&mut db).await?.unwrap();
472    assert_eq!(profile.user_id, user.id);
473
474    User::filter_by_email(&user.email)
475        .update()
476        .profile(None)
477        .exec(&mut db)
478        .await?;
479
480    // Profile is deleted
481    assert_err!(Profile::get_by_id(&mut db, &profile.id).await);
482    Ok(())
483}
484
485#[driver_test(id(ID))]
486pub async fn associate_has_one_by_val_on_insert(test: &mut Test) -> Result<()> {
487    #[derive(Debug, toasty::Model)]
488    struct User {
489        #[key]
490        #[auto]
491        id: ID,
492
493        #[has_one]
494        profile: toasty::Deferred<Profile>,
495    }
496
497    #[derive(Debug, toasty::Model)]
498    struct Profile {
499        #[key]
500        #[auto]
501        id: ID,
502
503        #[unique]
504        user_id: Option<ID>,
505
506        #[belongs_to(key = user_id, references = id)]
507        user: toasty::Deferred<Option<User>>,
508
509        bio: String,
510    }
511
512    let mut db = test.setup_db(models!(User, Profile)).await;
513
514    // Create a profile
515    let profile = Profile::create().bio("hello world").exec(&mut db).await?;
516
517    // Create a user and associate the profile with it, by value
518    let u1 = User::create().profile(&profile).exec(&mut db).await?;
519
520    let profile_reloaded = u1.profile().exec(&mut db).await?;
521    assert_eq!(profile.id, profile_reloaded.id);
522    assert_eq!(Some(&u1.id), profile_reloaded.user_id.as_ref());
523    assert_eq!(profile.bio, profile_reloaded.bio);
524    Ok(())
525}
526
527#[driver_test(id(ID), scenario(crate::scenarios::has_one_optional_belongs_to))]
528pub async fn associate_has_one_by_val_on_update_query_with_filter_1(test: &mut Test) -> Result<()> {
529    let mut db = setup(test).await;
530
531    let u1 = User::create().name("user 1").exec(&mut db).await?;
532    let p1 = Profile::create().bio("hello world").exec(&mut db).await?;
533
534    // Associate profile via filter_by_id update — should work
535    User::filter_by_id(u1.id)
536        .update()
537        .profile(&p1)
538        .exec(&mut db)
539        .await?;
540
541    let u1_reloaded = User::get_by_id(&mut db, &u1.id).await?;
542    let p1_reloaded = u1_reloaded.profile().exec(&mut db).await?.unwrap();
543    assert_eq!(p1.id, p1_reloaded.id);
544    assert_eq!(p1.bio, p1_reloaded.bio);
545    assert_eq!(p1_reloaded.user_id.as_ref(), Some(&u1.id));
546
547    // Unset
548    User::filter_by_id(u1.id)
549        .update()
550        .profile(None)
551        .exec(&mut db)
552        .await?;
553
554    User::filter_by_id(u1.id)
555        .filter(User::fields().name().eq("anon"))
556        .update()
557        .profile(&p1)
558        .exec(&mut db)
559        .await?;
560
561    // Verify profile's user_id is still None (update was a no-op)
562    let p1_reloaded = Profile::get_by_id(&mut db, &p1.id).await?;
563    assert!(p1_reloaded.user_id.is_none());
564
565    Ok(())
566}
567
568#[driver_test(id(ID), scenario(crate::scenarios::has_one_optional_belongs_to))]
569pub async fn associate_has_one_by_val_on_update_query_with_filter_2(test: &mut Test) -> Result<()> {
570    let mut db = setup(test).await;
571
572    let u1 = toasty::create!(User {
573        name: "User 1",
574        profile: {
575            bio: "hello world"
576        }
577    })
578    .exec(&mut db)
579    .await?;
580
581    let u2 = toasty::create!(User { name: "User 2" })
582        .exec(&mut db)
583        .await?;
584
585    let p1 = u1.profile().exec(&mut db).await?.unwrap();
586    assert_eq!(p1.user_id.as_ref(), Some(&u1.id));
587
588    User::filter_by_id(u2.id)
589        .filter(User::fields().name().eq("anon"))
590        .update()
591        .profile(&p1)
592        .exec(&mut db)
593        .await?;
594
595    // Verify profile's user_id still points to u1 (not changed)
596    let p1_reloaded = Profile::get_by_id(&mut db, &p1.id).await?;
597    assert_eq!(p1_reloaded.user_id.as_ref(), Some(&u1.id));
598
599    Ok(())
600}