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