Skip to main content

toasty_driver_integration_suite/tests/
crud_basic.rs

1use crate::prelude::*;
2
3use toasty_core::{
4    driver::{Operation, Rows},
5    stmt::{Assignment, Expr, Source, Statement, UpdateTarget},
6};
7
8#[driver_test(id(ID))]
9pub async fn crud_no_fields(t: &mut Test) -> Result<()> {
10    const MORE: i32 = 10;
11
12    #[derive(Debug, toasty::Model)]
13    struct Item {
14        #[key]
15        #[auto]
16        id: ID,
17    }
18
19    let mut db = t.setup_db(models!(Item)).await;
20
21    let created = Item::create().exec(&mut db).await?;
22
23    // Find Item
24    let read = Item::filter_by_id(created.id).exec(&mut db).await?;
25
26    assert_eq!(1, read.len());
27    assert_eq!(created.id, read[0].id);
28
29    // Generate a few instances, IDs should be different
30
31    let mut ids = vec![];
32
33    for _ in 0..MORE {
34        let item = Item::create().exec(&mut db).await?;
35        assert_ne!(item.id, created.id);
36        ids.push(item.id);
37    }
38
39    assert_unique!(ids);
40
41    for id in &ids {
42        let read = Item::filter_by_id(id).exec(&mut db).await?;
43
44        assert_eq!(1, read.len());
45        assert_eq!(*id, read[0].id);
46    }
47
48    // Randomize the IDs
49    ids.shuffle();
50
51    // Delete the IDs
52    for i in 0..MORE {
53        let id = ids.pop().unwrap();
54
55        if i.is_even() {
56            // Delete by object
57            let val = Item::get_by_id(&mut db, &id).await?;
58            val.delete().exec(&mut db).await?;
59        } else {
60            // Delete by ID
61            Item::filter_by_id(id).delete().exec(&mut db).await?;
62        }
63
64        // Assert deleted
65        assert_err!(Item::get_by_id(&mut db, id).await);
66
67        // Assert other items remain
68        for id in &ids {
69            let item = Item::get_by_id(&mut db, id).await?;
70            assert_eq!(*id, item.id);
71        }
72    }
73    Ok(())
74}
75
76#[driver_test(id(ID))]
77pub async fn crud_one_string(test: &mut Test) -> Result<()> {
78    #[derive(Debug, toasty::Model)]
79    struct Item {
80        #[key]
81        #[auto]
82        id: ID,
83
84        val: String,
85    }
86
87    let mut db = test.setup_db(models!(Item)).await;
88
89    let item_table_id = table_id(&db, "items");
90    let is_sql = test.capability().sql;
91
92    let mut created = Item::create().val("hello world").exec(&mut db).await?;
93
94    assert_eq!(created.val, "hello world");
95
96    // Find Item
97    let read = Item::filter_by_id(created.id).exec(&mut db).await?;
98
99    assert_eq!(1, read.len());
100    assert_eq!(created.id, read[0].id);
101    assert_eq!(created.val, "hello world");
102
103    let mut ids = vec![];
104
105    for i in 0..10 {
106        let item = Item::create()
107            .val(format!("hello {i}"))
108            .exec(&mut db)
109            .await?;
110
111        assert_ne!(item.id, created.id);
112        ids.push(item.id);
113    }
114
115    assert_unique!(ids);
116
117    for (i, id) in ids.iter().enumerate() {
118        let read = Item::filter_by_id(id).exec(&mut db).await?;
119
120        assert_eq!(1, read.len());
121        assert_eq!(*id, read[0].id);
122        assert_eq!(format!("hello {i}"), read[0].val);
123    }
124
125    // Update by val (instance method — generates full-key filter).
126    test.log().clear();
127    created.update().val("updated!").exec(&mut db).await?;
128    assert_eq!(created.val, "updated!");
129
130    let (op, resp) = test.log().pop();
131    // Column index 1 = val (id=0, val=1).
132    if is_sql {
133        assert_struct!(op, Operation::QuerySql({
134            stmt: Statement::Update({
135                target: UpdateTarget::Table(== item_table_id),
136                assignments: #{ [1]: Assignment::Set(Expr::Arg({ position: 0 }))},
137            }),
138            params: [{ value: == "updated!" }, ..],
139            ret: None,
140        }));
141    } else {
142        assert_struct!(op, Operation::UpdateByKey({
143            table: == item_table_id,
144            keys.len(): 1,
145            assignments: #{ [1]: Assignment::Set(== "updated!")},
146            filter: None,
147            returning: None,
148        }));
149    }
150    assert_struct!(resp, { values: Rows::Count(1) });
151    assert!(test.log().is_empty());
152
153    test.log().clear();
154    let reload = Item::get_by_id(&mut db, &created.id).await?;
155    assert_eq!(reload.val, created.val);
156
157    // Update by ID
158    Item::filter_by_id(created.id)
159        .update()
160        .val("updated again!")
161        .exec(&mut db)
162        .await?;
163    let reload = Item::get_by_id(&mut db, &created.id).await?;
164    assert_eq!(reload.val, "updated again!");
165
166    // Delete the record (instance method — generates full-key filter).
167    test.log().clear();
168    reload.delete().exec(&mut db).await?;
169
170    let (op, resp) = test.log().pop();
171    if is_sql {
172        assert_struct!(op, Operation::QuerySql({
173            stmt: Statement::Delete({
174                from: Source::Table({
175                    tables: [== item_table_id, ..],
176                }),
177            }),
178        }));
179    } else {
180        assert_struct!(op, Operation::DeleteByKey({
181            table: == item_table_id,
182            keys.len(): 1,
183            filter: None,
184        }));
185    }
186    assert_struct!(resp, { values: Rows::Count(1) });
187    assert!(test.log().is_empty());
188
189    // It is gone
190    assert_err!(Item::get_by_id(&mut db, &created.id).await);
191
192    // Delete by ID
193    Item::filter_by_id(ids[0]).delete().exec(&mut db).await?;
194
195    // It is gone
196    assert_err!(Item::get_by_id(&mut db, &ids[0]).await);
197    Ok(())
198}
199
200#[driver_test(id(ID), scenario(crate::scenarios::two_models))]
201pub async fn required_field_create_without_setting(test: &mut Test) {
202    let mut db = setup(test).await;
203
204    // Try creating a user without setting the name field results in an error
205    assert_err!(User::create().exec(&mut db).await);
206}
207
208#[driver_test(id(ID), scenario(crate::scenarios::user_unique_email))]
209pub async fn unique_index_required_field_update(test: &mut Test) -> Result<()> {
210    let mut db = setup(test).await;
211
212    let email = "user1@example.com";
213
214    let user = User::create().email(email).exec(&mut db).await?;
215
216    assert_eq!("user1@example.com", user.email);
217
218    // Trying to create a user with the same email address fails
219    assert_err!(User::create().email(email).exec(&mut db).await);
220
221    // Loading the user by email
222    let user_reloaded = User::get_by_email(&mut db, email).await?;
223    assert_eq!(user.id, user_reloaded.id);
224    assert_eq!(user_reloaded.email, email);
225
226    // Creating a user with a different email works
227    let user_alt_email = User::create()
228        .email("alt-email@example.com")
229        .exec(&mut db)
230        .await?;
231
232    assert_ne!(user.id, user_alt_email.id);
233
234    // Deleting the user then reuse the email address
235    user.delete().exec(&mut db).await?;
236
237    // Finding by the email returns None
238    assert_none!(User::filter_by_email(email).first().exec(&mut db).await?);
239
240    let mut user2 = User::create().email(email).exec(&mut db).await?;
241    assert_ne!(user2.id, user_reloaded.id);
242
243    // Trying to create a third user with that email address fails.
244    assert_err!(User::create().email(email).exec(&mut db).await);
245
246    // Updating the email address by object
247    user2
248        .update()
249        .email("user2@example.com")
250        .exec(&mut db)
251        .await?;
252
253    // Reload the user by ID
254    let user_reloaded = User::filter_by_id(user2.id).get(&mut db).await?;
255    assert_eq!(user2.id, user_reloaded.id);
256    assert_eq!(user_reloaded.email, "user2@example.com");
257
258    // Finding by the email returns None
259    assert_none!(User::filter_by_email(email).first().exec(&mut db).await?);
260
261    // Trying to create a user with the updated email address fails
262    assert_err!(
263        User::create()
264            .email("user2@example.com")
265            .exec(&mut db)
266            .await
267    );
268
269    // Creating a user with the **old** email address succeeds
270    let user3 = User::create().email(email).exec(&mut db).await?;
271    assert_eq!(user3.email, email);
272    assert_ne!(user3.id, user2.id);
273
274    // Updating the email address by ID
275    User::filter_by_id(user2.id)
276        .update()
277        .email("user3@example.com")
278        .exec(&mut db)
279        .await?;
280
281    // Finding by the email returns None
282    assert_none!(
283        User::filter_by_email(&user2.email)
284            .first()
285            .exec(&mut db)
286            .await?
287    );
288
289    // Find the user by the new address.
290    let u = User::filter_by_email("user3@example.com")
291        .get(&mut db)
292        .await?;
293
294    assert_eq!(u.id, user2.id);
295
296    assert_err!(
297        User::create()
298            .email("user3@example.com")
299            .exec(&mut db)
300            .await
301    );
302
303    // But we *can* create a user w/ the old email
304    assert_ok!(
305        User::create()
306            .email("user2@example.com")
307            .exec(&mut db)
308            .await
309    );
310    Ok(())
311}
312
313#[driver_test(id(ID))]
314pub async fn unique_index_nullable_field_update(test: &mut Test) -> Result<()> {
315    #[derive(Debug, toasty::Model)]
316    struct User {
317        #[key]
318        #[auto]
319        id: ID,
320
321        #[unique]
322        email: Option<String>,
323    }
324
325    let mut db = test.setup_db(models!(User)).await;
326
327    // Create a user without an email address
328    let mut u1 = User::create().exec(&mut db).await?;
329    assert!(u1.email.is_none());
330
331    // Create a second user without an email address
332    let mut u2 = User::create().exec(&mut db).await?;
333    assert!(u2.email.is_none());
334
335    // Reload u1 and make sure email is still set.
336    let u1_reload = User::get_by_id(&mut db, &u1.id).await?;
337    assert!(u1_reload.email.is_none());
338
339    // Finding by a bogus email finds nothing
340    assert_none!(
341        User::filter_by_email("nobody@example.com")
342            .first()
343            .exec(&mut db)
344            .await?
345    );
346
347    // Create a user **with** an email
348    let mut u3 = User::create()
349        .email("three@example.com")
350        .exec(&mut db)
351        .await?;
352    assert_eq!(u3.email, Some("three@example.com".to_string()));
353
354    let u3_reload = User::get_by_email(&mut db, "three@example.com").await?;
355    assert_eq!(u3_reload.id, u3.id);
356
357    // Now, set u1's email to something
358    u1.update().email("one@example.com").exec(&mut db).await?;
359    assert_eq!(u1.email, Some("one@example.com".to_string()));
360
361    // Find it
362    let u1_reload = User::get_by_email(&mut db, "one@example.com").await?;
363    assert_eq!(u1.id, u1_reload.id);
364
365    // Try updating user 2 to an already taken email address
366    assert_err!(u2.update().email("three@example.com").exec(&mut db).await);
367
368    // Can still fetch user 3
369    let u3_reload = User::get_by_email(&mut db, "three@example.com").await?;
370    assert_eq!(u3_reload.id, u3.id);
371
372    // Update user 2 to set an actual email now.
373    u2.update().email("two@example.com").exec(&mut db).await?;
374    let u2_reload = User::get_by_email(&mut db, "two@example.com").await?;
375    assert_eq!(u2_reload.id, u2.id);
376
377    // Update a user to **remove** the email attribute
378    let mut update = u3.update();
379    update.set_email(None);
380    update.exec(&mut db).await?;
381    assert!(u3.email.is_none());
382
383    // We can create a new user using the freed email
384    let u4 = User::create()
385        .email("three@example.com")
386        .exec(&mut db)
387        .await?;
388    let u4_reload = User::filter_by_email("three@example.com")
389        .get(&mut db)
390        .await?;
391    assert_eq!(u4_reload.id, u4.id);
392    Ok(())
393}
394
395#[driver_test(id(ID), scenario(crate::scenarios::user_unique_email_with_name))]
396pub async fn unique_index_no_update(test: &mut Test) -> Result<()> {
397    let mut db = setup(test).await;
398
399    let mut user = User::create()
400        .email("user@example.com")
401        .name("John Doe")
402        .exec(&mut db)
403        .await?;
404
405    let u = User::filter_by_id(user.id).get(&mut db).await?;
406    assert_eq!(user.name, u.name);
407
408    // Update the name by value
409    user.update().name("Jane Doe").exec(&mut db).await?;
410
411    assert_eq!("Jane Doe", user.name);
412
413    let u = User::get_by_id(&mut db, &user.id).await?;
414    assert_eq!(user.name, u.name);
415
416    // Find by email still works
417    let u = User::get_by_email(&mut db, &user.email).await?;
418    assert_eq!(user.name, u.name);
419    Ok(())
420}
421
422#[driver_test(id(ID), scenario(crate::scenarios::user_unique_email_with_name))]
423pub async fn unique_index_set_same_value(test: &mut Test) -> Result<()> {
424    let mut db = setup(test).await;
425
426    let mut user = toasty::create!(User {
427        email: "user@example.com",
428        name: "John Doe",
429    })
430    .exec(&mut db)
431    .await?;
432
433    // Update both fields, but set email to the same value it already has.
434    // This exercises the path where the unique column appears in op.assignments
435    // but its new value equals the current stored value.
436    user.update()
437        .email("user@example.com")
438        .name("Jane Doe")
439        .exec(&mut db)
440        .await?;
441
442    assert_eq!("user@example.com", user.email);
443    assert_eq!("Jane Doe", user.name);
444
445    let u = User::get_by_id(&mut db, &user.id).await?;
446    assert_eq!(user.email, u.email);
447    assert_eq!(user.name, u.name);
448
449    // Lookup by email still works
450    let u = User::get_by_email(&mut db, &user.email).await?;
451    assert_eq!(user.name, u.name);
452
453    Ok(())
454}
455
456#[driver_test(id(ID), scenario(crate::scenarios::user_name_email))]
457pub async fn update_multiple_fields(test: &mut Test) -> Result<()> {
458    let mut db = setup(test).await;
459
460    let mut user = User::create()
461        .name("John Doe")
462        .email("john@example.com")
463        .exec(&mut db)
464        .await?;
465
466    // Update by object
467    user.update()
468        .name("Jane Doe")
469        .email("jane@example.com")
470        .exec(&mut db)
471        .await?;
472
473    assert_eq!("Jane Doe", user.name);
474    assert_eq!("jane@example.com", user.email);
475
476    let user = User::get_by_id(&mut db, &user.id).await?;
477    assert_eq!("Jane Doe", user.name);
478    assert_eq!("jane@example.com", user.email);
479
480    // Update by query
481    User::filter_by_id(user.id)
482        .update()
483        .name("John2 Doe")
484        .email("john2@example.com")
485        .exec(&mut db)
486        .await?;
487
488    let user = User::get_by_id(&mut db, &user.id).await?;
489    assert_eq!("John2 Doe", user.name);
490    assert_eq!("john2@example.com", user.email);
491    Ok(())
492}
493
494#[driver_test(id(ID), scenario(crate::scenarios::two_models))]
495pub async fn update_and_delete_snippets(test: &mut Test) -> Result<()> {
496    let mut db = setup(test).await;
497
498    let user = User::create().name("John Doe").exec(&mut db).await?;
499
500    User::update_by_id(user.id)
501        .name("John Doe2")
502        .exec(&mut db)
503        .await?;
504
505    let new_user = User::get_by_id(&mut db, user.id).await?;
506    assert!(new_user.name == "John Doe2");
507
508    User::delete_by_id(&mut db, user.id).await?;
509
510    assert_err!(User::get_by_id(&mut db, user.id).await);
511    Ok(())
512}