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::HasOne<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::BelongsTo<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::HasOne<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::BelongsTo<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::HasOne<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::BelongsTo<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))]
299pub async fn set_has_one_by_value_in_update_query(test: &mut Test) -> Result<()> {
300    #[derive(Debug, toasty::Model)]
301    struct User {
302        #[key]
303        #[auto]
304        id: ID,
305
306        #[has_one]
307        profile: toasty::HasOne<Option<Profile>>,
308    }
309
310    #[derive(Debug, toasty::Model)]
311    struct Profile {
312        #[key]
313        #[auto]
314        id: ID,
315
316        #[unique]
317        user_id: Option<ID>,
318
319        #[belongs_to(key = user_id, references = id)]
320        user: toasty::BelongsTo<Option<User>>,
321    }
322
323    let mut db = test.setup_db(models!(User, Profile)).await;
324
325    let user = User::create().exec(&mut db).await?;
326    let profile = Profile::create().exec(&mut db).await?;
327
328    User::filter_by_id(user.id)
329        .update()
330        .profile(&profile)
331        .exec(&mut db)
332        .await?;
333
334    let profile_reload = user.profile().exec(&mut db).await?.unwrap();
335    assert_eq!(profile_reload.id, profile.id);
336
337    assert_eq!(profile_reload.user_id.as_ref().unwrap(), &user.id);
338    Ok(())
339}
340
341#[driver_test(id(ID))]
342pub async fn unset_has_one_in_batch_update(test: &mut Test) -> Result<()> {
343    #[derive(Debug, toasty::Model)]
344    struct User {
345        #[key]
346        #[auto]
347        id: ID,
348
349        #[index]
350        name: String,
351
352        #[has_one]
353        profile: toasty::HasOne<Option<Profile>>,
354    }
355
356    #[derive(Debug, toasty::Model)]
357    struct Profile {
358        #[key]
359        #[auto]
360        id: ID,
361
362        #[unique]
363        user_id: ID,
364
365        #[belongs_to(key = user_id, references = id)]
366        user: toasty::BelongsTo<User>,
367    }
368
369    let mut db = test.setup_db(models!(User, Profile)).await;
370
371    // Create two users with the same name, each with a profile
372    let u1 = User::create()
373        .name("alice")
374        .profile(Profile::create())
375        .exec(&mut db)
376        .await?;
377    let p1 = u1.profile().exec(&mut db).await?.unwrap();
378
379    let u2 = User::create()
380        .name("alice")
381        .profile(Profile::create())
382        .exec(&mut db)
383        .await?;
384    let p2 = u2.profile().exec(&mut db).await?.unwrap();
385
386    // A third user with a different name (should not be affected)
387    let u3 = User::create()
388        .name("bob")
389        .profile(Profile::create())
390        .exec(&mut db)
391        .await?;
392
393    // Batch update: unset profiles for all users named "alice"
394    User::filter_by_name("alice")
395        .update()
396        .profile(None)
397        .exec(&mut db)
398        .await?;
399
400    // Both profiles should be deleted (required belongs_to)
401    assert_err!(Profile::get_by_id(&mut db, &p1.id).await);
402    assert_err!(Profile::get_by_id(&mut db, &p2.id).await);
403
404    // Bob's profile should still exist
405    let p3 = u3.profile().exec(&mut db).await?.unwrap();
406    assert_eq!(p3.user_id, u3.id);
407
408    Ok(())
409}
410
411#[driver_test(id(ID))]
412pub async fn unset_has_one_with_required_pair_in_pk_query_update(test: &mut Test) -> Result<()> {
413    #[derive(Debug, toasty::Model)]
414    struct User {
415        #[key]
416        #[auto]
417        id: ID,
418
419        #[has_one]
420        profile: toasty::HasOne<Option<Profile>>,
421    }
422
423    #[derive(Debug, toasty::Model)]
424    struct Profile {
425        #[key]
426        #[auto]
427        id: ID,
428
429        #[unique]
430        user_id: ID,
431
432        #[belongs_to(key = user_id, references = id)]
433        user: toasty::BelongsTo<User>,
434    }
435
436    let mut db = test.setup_db(models!(User, Profile)).await;
437
438    let user = User::create()
439        .profile(Profile::create())
440        .exec(&mut db)
441        .await?;
442    let profile = user.profile().exec(&mut db).await?.unwrap();
443
444    assert_eq!(user.id, profile.user_id);
445
446    User::filter_by_id(user.id)
447        .update()
448        .profile(None)
449        .exec(&mut db)
450        .await?;
451
452    // Profile is deleted
453    assert_err!(Profile::get_by_id(&mut db, &profile.id).await);
454    Ok(())
455}
456
457#[driver_test(id(ID))]
458pub async fn unset_has_one_with_required_pair_in_non_pk_query_update(
459    test: &mut Test,
460) -> Result<()> {
461    #[derive(Debug, toasty::Model)]
462    struct User {
463        #[key]
464        #[auto]
465        id: ID,
466
467        #[unique]
468        email: String,
469
470        #[has_one]
471        profile: toasty::HasOne<Option<Profile>>,
472    }
473
474    #[derive(Debug, toasty::Model)]
475    struct Profile {
476        #[key]
477        #[auto]
478        id: ID,
479
480        #[unique]
481        user_id: ID,
482
483        #[belongs_to(key = user_id, references = id)]
484        user: toasty::BelongsTo<User>,
485    }
486
487    let mut db = test.setup_db(models!(User, Profile)).await;
488
489    let user = User::create()
490        .email("foo@example.com")
491        .profile(Profile::create())
492        .exec(&mut db)
493        .await?;
494    let profile = user.profile().exec(&mut db).await?.unwrap();
495    assert_eq!(profile.user_id, user.id);
496
497    User::filter_by_email(&user.email)
498        .update()
499        .profile(None)
500        .exec(&mut db)
501        .await?;
502
503    // Profile is deleted
504    assert_err!(Profile::get_by_id(&mut db, &profile.id).await);
505    Ok(())
506}
507
508#[driver_test(id(ID))]
509pub async fn associate_has_one_by_val_on_insert(test: &mut Test) -> Result<()> {
510    #[derive(Debug, toasty::Model)]
511    struct User {
512        #[key]
513        #[auto]
514        id: ID,
515
516        #[has_one]
517        profile: toasty::HasOne<Profile>,
518    }
519
520    #[derive(Debug, toasty::Model)]
521    struct Profile {
522        #[key]
523        #[auto]
524        id: ID,
525
526        #[unique]
527        user_id: Option<ID>,
528
529        #[belongs_to(key = user_id, references = id)]
530        user: toasty::BelongsTo<Option<User>>,
531
532        bio: String,
533    }
534
535    let mut db = test.setup_db(models!(User, Profile)).await;
536
537    // Create a profile
538    let profile = Profile::create().bio("hello world").exec(&mut db).await?;
539
540    // Create a user and associate the profile with it, by value
541    let u1 = User::create().profile(&profile).exec(&mut db).await?;
542
543    let profile_reloaded = u1.profile().exec(&mut db).await?;
544    assert_eq!(profile.id, profile_reloaded.id);
545    assert_eq!(Some(&u1.id), profile_reloaded.user_id.as_ref());
546    assert_eq!(profile.bio, profile_reloaded.bio);
547    Ok(())
548}
549
550#[driver_test(id(ID), scenario(crate::scenarios::has_one_optional_belongs_to))]
551pub async fn associate_has_one_by_val_on_update_query_with_filter_1(test: &mut Test) -> Result<()> {
552    let mut db = setup(test).await;
553
554    let u1 = User::create().name("user 1").exec(&mut db).await?;
555    let p1 = Profile::create().bio("hello world").exec(&mut db).await?;
556
557    // Associate profile via filter_by_id update — should work
558    User::filter_by_id(u1.id)
559        .update()
560        .profile(&p1)
561        .exec(&mut db)
562        .await?;
563
564    let u1_reloaded = User::get_by_id(&mut db, &u1.id).await?;
565    let p1_reloaded = u1_reloaded.profile().exec(&mut db).await?.unwrap();
566    assert_eq!(p1.id, p1_reloaded.id);
567    assert_eq!(p1.bio, p1_reloaded.bio);
568    assert_eq!(p1_reloaded.user_id.as_ref(), Some(&u1.id));
569
570    // Unset
571    User::filter_by_id(u1.id)
572        .update()
573        .profile(None)
574        .exec(&mut db)
575        .await?;
576
577    User::filter_by_id(u1.id)
578        .filter(User::fields().name().eq("anon"))
579        .update()
580        .profile(&p1)
581        .exec(&mut db)
582        .await?;
583
584    // Verify profile's user_id is still None (update was a no-op)
585    let p1_reloaded = Profile::get_by_id(&mut db, &p1.id).await?;
586    assert!(p1_reloaded.user_id.is_none());
587
588    Ok(())
589}
590
591#[driver_test(id(ID), scenario(crate::scenarios::has_one_optional_belongs_to))]
592pub async fn associate_has_one_by_val_on_update_query_with_filter_2(test: &mut Test) -> Result<()> {
593    let mut db = setup(test).await;
594
595    let u1 = toasty::create!(User {
596        name: "User 1",
597        profile: {
598            bio: "hello world"
599        }
600    })
601    .exec(&mut db)
602    .await?;
603
604    let u2 = toasty::create!(User { name: "User 2" })
605        .exec(&mut db)
606        .await?;
607
608    let p1 = u1.profile().exec(&mut db).await?.unwrap();
609    assert_eq!(p1.user_id.as_ref(), Some(&u1.id));
610
611    User::filter_by_id(u2.id)
612        .filter(User::fields().name().eq("anon"))
613        .update()
614        .profile(&p1)
615        .exec(&mut db)
616        .await?;
617
618    // Verify profile's user_id still points to u1 (not changed)
619    let p1_reloaded = Profile::get_by_id(&mut db, &p1.id).await?;
620    assert_eq!(p1_reloaded.user_id.as_ref(), Some(&u1.id));
621
622    Ok(())
623}