toasty_driver_integration_suite/tests/
crud_basic.rs

1use crate::prelude::*;
2
3use toasty_core::{
4    driver::{Operation, Rows},
5    stmt::{Assignment, 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(== "updated!")},
137            }),
138            ret: None,
139        }));
140    } else {
141        assert_struct!(op, Operation::UpdateByKey({
142            table: == item_table_id,
143            keys.len(): 1,
144            assignments: #{ [1]: Assignment::Set(== "updated!")},
145            filter: None,
146            returning: false,
147        }));
148    }
149    assert_struct!(resp, { values: Rows::Count(1) });
150    assert!(test.log().is_empty());
151
152    test.log().clear();
153    let reload = Item::get_by_id(&mut db, &created.id).await?;
154    assert_eq!(reload.val, created.val);
155
156    // Update by ID
157    Item::filter_by_id(created.id)
158        .update()
159        .val("updated again!")
160        .exec(&mut db)
161        .await?;
162    let reload = Item::get_by_id(&mut db, &created.id).await?;
163    assert_eq!(reload.val, "updated again!");
164
165    // Delete the record (instance method — generates full-key filter).
166    test.log().clear();
167    reload.delete().exec(&mut db).await?;
168
169    let (op, resp) = test.log().pop();
170    if is_sql {
171        assert_struct!(op, Operation::QuerySql({
172            stmt: Statement::Delete({
173                from: Source::Table({
174                    tables: [== item_table_id, ..],
175                }),
176            }),
177        }));
178    } else {
179        assert_struct!(op, Operation::DeleteByKey({
180            table: == item_table_id,
181            keys.len(): 1,
182            filter: None,
183        }));
184    }
185    assert_struct!(resp, { values: Rows::Count(1) });
186    assert!(test.log().is_empty());
187
188    // It is gone
189    assert_err!(Item::get_by_id(&mut db, &created.id).await);
190
191    // Delete by ID
192    Item::filter_by_id(ids[0]).delete().exec(&mut db).await?;
193
194    // It is gone
195    assert_err!(Item::get_by_id(&mut db, &ids[0]).await);
196    Ok(())
197}
198
199#[driver_test(id(ID))]
200pub async fn required_field_create_without_setting(test: &mut Test) {
201    #[derive(Debug, toasty::Model)]
202    struct User {
203        #[key]
204        #[auto]
205        id: ID,
206
207        #[allow(dead_code)]
208        name: String,
209    }
210
211    let mut db = test.setup_db(models!(User)).await;
212
213    // Try creating a user without setting the name field results in an error
214    assert_err!(User::create().exec(&mut db).await);
215}
216
217#[driver_test(id(ID), scenario(crate::scenarios::user_unique_email))]
218pub async fn unique_index_required_field_update(test: &mut Test) -> Result<()> {
219    let mut db = setup(test).await;
220
221    let email = "user1@example.com";
222
223    let user = User::create().email(email).exec(&mut db).await?;
224
225    assert_eq!("user1@example.com", user.email);
226
227    // Trying to create a user with the same email address fails
228    assert_err!(User::create().email(email).exec(&mut db).await);
229
230    // Loading the user by email
231    let user_reloaded = User::get_by_email(&mut db, email).await?;
232    assert_eq!(user.id, user_reloaded.id);
233    assert_eq!(user_reloaded.email, email);
234
235    // Creating a user with a different email works
236    let user_alt_email = User::create()
237        .email("alt-email@example.com")
238        .exec(&mut db)
239        .await?;
240
241    assert_ne!(user.id, user_alt_email.id);
242
243    // Deleting the user then reuse the email address
244    user.delete().exec(&mut db).await?;
245
246    // Finding by the email returns None
247    assert_none!(User::filter_by_email(email).first().exec(&mut db).await?);
248
249    let mut user2 = User::create().email(email).exec(&mut db).await?;
250    assert_ne!(user2.id, user_reloaded.id);
251
252    // Trying to create a third user with that email address fails.
253    assert_err!(User::create().email(email).exec(&mut db).await);
254
255    // Updating the email address by object
256    user2
257        .update()
258        .email("user2@example.com")
259        .exec(&mut db)
260        .await?;
261
262    // Reload the user by ID
263    let user_reloaded = User::filter_by_id(user2.id).get(&mut db).await?;
264    assert_eq!(user2.id, user_reloaded.id);
265    assert_eq!(user_reloaded.email, "user2@example.com");
266
267    // Finding by the email returns None
268    assert_none!(User::filter_by_email(email).first().exec(&mut db).await?);
269
270    // Trying to create a user with the updated email address fails
271    assert_err!(
272        User::create()
273            .email("user2@example.com")
274            .exec(&mut db)
275            .await
276    );
277
278    // Creating a user with the **old** email address succeeds
279    let user3 = User::create().email(email).exec(&mut db).await?;
280    assert_eq!(user3.email, email);
281    assert_ne!(user3.id, user2.id);
282
283    // Updating the email address by ID
284    User::filter_by_id(user2.id)
285        .update()
286        .email("user3@example.com")
287        .exec(&mut db)
288        .await?;
289
290    // Finding by the email returns None
291    assert_none!(
292        User::filter_by_email(&user2.email)
293            .first()
294            .exec(&mut db)
295            .await?
296    );
297
298    // Find the user by the new address.
299    let u = User::filter_by_email("user3@example.com")
300        .get(&mut db)
301        .await?;
302
303    assert_eq!(u.id, user2.id);
304
305    assert_err!(
306        User::create()
307            .email("user3@example.com")
308            .exec(&mut db)
309            .await
310    );
311
312    // But we *can* create a user w/ the old email
313    assert_ok!(
314        User::create()
315            .email("user2@example.com")
316            .exec(&mut db)
317            .await
318    );
319    Ok(())
320}
321
322#[driver_test(id(ID))]
323pub async fn unique_index_nullable_field_update(test: &mut Test) -> Result<()> {
324    #[derive(Debug, toasty::Model)]
325    struct User {
326        #[key]
327        #[auto]
328        id: ID,
329
330        #[unique]
331        email: Option<String>,
332    }
333
334    let mut db = test.setup_db(models!(User)).await;
335
336    // Create a user without an email address
337    let mut u1 = User::create().exec(&mut db).await?;
338    assert!(u1.email.is_none());
339
340    // Create a second user without an email address
341    let mut u2 = User::create().exec(&mut db).await?;
342    assert!(u2.email.is_none());
343
344    // Reload u1 and make sure email is still set.
345    let u1_reload = User::get_by_id(&mut db, &u1.id).await?;
346    assert!(u1_reload.email.is_none());
347
348    // Finding by a bogus email finds nothing
349    assert_none!(
350        User::filter_by_email("nobody@example.com")
351            .first()
352            .exec(&mut db)
353            .await?
354    );
355
356    // Create a user **with** an email
357    let mut u3 = User::create()
358        .email("three@example.com")
359        .exec(&mut db)
360        .await?;
361    assert_eq!(u3.email, Some("three@example.com".to_string()));
362
363    let u3_reload = User::get_by_email(&mut db, "three@example.com").await?;
364    assert_eq!(u3_reload.id, u3.id);
365
366    // Now, set u1's email to something
367    u1.update().email("one@example.com").exec(&mut db).await?;
368    assert_eq!(u1.email, Some("one@example.com".to_string()));
369
370    // Find it
371    let u1_reload = User::get_by_email(&mut db, "one@example.com").await?;
372    assert_eq!(u1.id, u1_reload.id);
373
374    // Try updating user 2 to an already taken email address
375    assert_err!(u2.update().email("three@example.com").exec(&mut db).await);
376
377    // Can still fetch user 3
378    let u3_reload = User::get_by_email(&mut db, "three@example.com").await?;
379    assert_eq!(u3_reload.id, u3.id);
380
381    // Update user 2 to set an actual email now.
382    u2.update().email("two@example.com").exec(&mut db).await?;
383    let u2_reload = User::get_by_email(&mut db, "two@example.com").await?;
384    assert_eq!(u2_reload.id, u2.id);
385
386    // Update a user to **remove** the email attribute
387    let mut update = u3.update();
388    update.set_email(None);
389    update.exec(&mut db).await?;
390    assert!(u3.email.is_none());
391
392    // We can create a new user using the freed email
393    let u4 = User::create()
394        .email("three@example.com")
395        .exec(&mut db)
396        .await?;
397    let u4_reload = User::filter_by_email("three@example.com")
398        .get(&mut db)
399        .await?;
400    assert_eq!(u4_reload.id, u4.id);
401    Ok(())
402}
403
404#[driver_test(id(ID))]
405pub async fn unique_index_no_update(test: &mut Test) -> Result<()> {
406    #[derive(Debug, toasty::Model)]
407    struct User {
408        #[key]
409        #[auto]
410        id: ID,
411
412        #[unique]
413        email: String,
414
415        name: String,
416    }
417
418    let mut db = test.setup_db(models!(User)).await;
419
420    let mut user = User::create()
421        .email("user@example.com")
422        .name("John Doe")
423        .exec(&mut db)
424        .await?;
425
426    let u = User::filter_by_id(user.id).get(&mut db).await?;
427    assert_eq!(user.name, u.name);
428
429    // Update the name by value
430    user.update().name("Jane Doe").exec(&mut db).await?;
431
432    assert_eq!("Jane Doe", user.name);
433
434    let u = User::get_by_id(&mut db, &user.id).await?;
435    assert_eq!(user.name, u.name);
436
437    // Find by email still works
438    let u = User::get_by_email(&mut db, &user.email).await?;
439    assert_eq!(user.name, u.name);
440    Ok(())
441}
442
443#[driver_test(id(ID))]
444pub async fn update_multiple_fields(test: &mut Test) -> Result<()> {
445    #[derive(Debug, toasty::Model)]
446    struct User {
447        #[key]
448        #[auto]
449        id: ID,
450
451        name: String,
452        email: String,
453    }
454
455    let mut db = test.setup_db(models!(User)).await;
456
457    let mut user = User::create()
458        .name("John Doe")
459        .email("john@example.com")
460        .exec(&mut db)
461        .await?;
462
463    // Update by object
464    user.update()
465        .name("Jane Doe")
466        .email("jane@example.com")
467        .exec(&mut db)
468        .await?;
469
470    assert_eq!("Jane Doe", user.name);
471    assert_eq!("jane@example.com", user.email);
472
473    let user = User::get_by_id(&mut db, &user.id).await?;
474    assert_eq!("Jane Doe", user.name);
475    assert_eq!("jane@example.com", user.email);
476
477    // Update by query
478    User::filter_by_id(user.id)
479        .update()
480        .name("John2 Doe")
481        .email("john2@example.com")
482        .exec(&mut db)
483        .await?;
484
485    let user = User::get_by_id(&mut db, &user.id).await?;
486    assert_eq!("John2 Doe", user.name);
487    assert_eq!("john2@example.com", user.email);
488    Ok(())
489}
490
491#[driver_test(id(ID))]
492pub async fn update_and_delete_snippets(test: &mut Test) -> Result<()> {
493    #[derive(Debug, toasty::Model)]
494    struct User {
495        #[key]
496        #[auto]
497        id: ID,
498
499        #[allow(dead_code)]
500        name: String,
501    }
502
503    let mut db = test.setup_db(models!(User)).await;
504
505    let user = User::create().name("John Doe").exec(&mut db).await?;
506
507    User::update_by_id(user.id)
508        .name("John Doe2")
509        .exec(&mut db)
510        .await?;
511
512    let new_user = User::get_by_id(&mut db, user.id).await?;
513    assert!(new_user.name == "John Doe2");
514
515    User::delete_by_id(&mut db, user.id).await?;
516
517    assert_err!(User::get_by_id(&mut db, user.id).await);
518    Ok(())
519}