toasty_driver_integration_suite/tests/
tys.rs

1use crate::prelude::*;
2
3use std::{rc::Rc, sync::Arc};
4use toasty::schema::db;
5use toasty_core::{
6    driver::Operation,
7    stmt::{ExprSet, InsertTarget, Statement},
8};
9
10/// Macro to generate the common test body for numeric types
11macro_rules! num_ty_test_body {
12    ($test:expr, $ty:ty, $test_values:expr) => {{
13        #[derive(Debug, toasty::Model)]
14        #[allow(dead_code)]
15        struct Item {
16            #[key]
17            #[auto]
18            id: uuid::Uuid,
19            val: $ty,
20        }
21
22        let test = $test;
23        let mut db = test.setup_db(models!(Item)).await;
24        let mut test_values: Vec<$ty> = (*$test_values).to_vec();
25
26        // Filter test values based on database capabilities for unsigned integers
27        // Unsigned types have MIN == 0, signed types have MIN < 0
28        if <$ty>::MIN == 0 {
29            if let Some(max_unsigned) = test.capability().storage_types.max_unsigned_integer {
30                test_values.retain(|&val| {
31                    let val_as_u64 = val as u64;
32                    val_as_u64 <= max_unsigned
33                });
34            }
35        }
36
37        // Clear setup operations
38        test.log().clear();
39
40        // Test 1: All test values round-trip
41        for &val in &test_values {
42            let created = Item::create().val(val).exec(&mut db).await?;
43
44            // Verify the INSERT operation stored the correct value
45            let (op, _resp) = test.log().pop();
46            assert_struct!(op, Operation::QuerySql(_ {
47                stmt: Statement::Insert(_ {
48                    target: InsertTarget::Table(_ {
49                        table: == table_id(&mut db, "items"),
50                        columns: == columns(&mut db, "items", &["id", "val"]),
51                        ..
52                    }),
53                    source.body: ExprSet::Values(_ {
54                        rows: [=~ (Any, val)],
55                        ..
56                    }),
57                    ..
58                }),
59                ..
60            }));
61
62            let read = Item::get_by_id(&mut db, &created.id).await?;
63            assert_eq!(read.val, val, "Round-trip failed for: {}", val);
64
65            // Clear the read operation
66            test.log().clear();
67        }
68
69        // Test 2: Multiple records with different values
70        let mut created_records = Vec::new();
71        for &val in &test_values {
72            let created = Item::create().val(val).exec(&mut db).await?;
73            created_records.push((created.id, val));
74            test.log().clear();
75        }
76
77        for (id, expected_val) in created_records {
78            let read = Item::get_by_id(&mut db, &id).await?;
79            assert_eq!(
80                read.val, expected_val,
81                "Multiple records test failed for: {}",
82                expected_val
83            );
84            test.log().clear();
85        }
86
87        // Test 3: Update chain
88        if !test_values.is_empty() {
89            let mut record = Item::create().val(test_values[0]).exec(&mut db).await?;
90            test.log().clear();
91
92            for &val in &test_values {
93                record.update().val(val).exec(&mut db).await?;
94
95                // Verify the UPDATE operation sent the correct value
96                let (op, _resp) = test.log().pop();
97                if test.capability().sql {
98                    assert_struct!(op, Operation::QuerySql(_ {
99                        stmt: Statement::Update(_ {
100                            assignments: #{ 1: _ { expr: _, .. }},
101                            ..
102                        }),
103                        ..
104                    }));
105                } else {
106                    assert_struct!(op, Operation::UpdateByKey(_ {
107                        assignments: #{ 1: _ { expr: _, .. }},
108                        ..
109                    }));
110                }
111
112                let read = Item::get_by_id(&mut db, &record.id).await?;
113                assert_eq!(read.val, val, "Update chain failed for: {}", val);
114                record.val = val;
115
116                test.log().clear();
117            }
118        }
119        Ok(())
120    }};
121}
122
123#[driver_test]
124pub async fn ty_i8(test: &mut Test) -> Result<()> {
125    num_ty_test_body!(test, i8, &[i8::MIN, -100, -1, 0, 1, 63, 100, i8::MAX])
126}
127
128#[driver_test]
129pub async fn ty_i16(test: &mut Test) -> Result<()> {
130    num_ty_test_body!(
131        test,
132        i16,
133        &[i16::MIN, -10000, -1, 0, 1, 10000, 16383, i16::MAX]
134    )
135}
136
137#[driver_test]
138pub async fn ty_i32(test: &mut Test) -> Result<()> {
139    num_ty_test_body!(
140        test,
141        i32,
142        &[i32::MIN, -1000000, -1, 0, 1, 1000000, 1073741823, i32::MAX]
143    )
144}
145
146#[driver_test]
147pub async fn ty_i64(test: &mut Test) -> Result<()> {
148    num_ty_test_body!(
149        test,
150        i64,
151        &[
152            i64::MIN,
153            -1000000000000,
154            -1,
155            0,
156            1,
157            1000000000000,
158            4611686018427387903,
159            i64::MAX
160        ]
161    )
162}
163
164#[driver_test]
165pub async fn ty_isize(test: &mut Test) -> Result<()> {
166    num_ty_test_body!(
167        test,
168        isize,
169        &[
170            isize::MIN,
171            -1000000000000,
172            -1,
173            0,
174            1,
175            1000000000000,
176            4611686018427387903,
177            isize::MAX
178        ]
179    )
180}
181
182#[driver_test]
183pub async fn ty_u8(test: &mut Test) -> Result<()> {
184    num_ty_test_body!(test, u8, &[u8::MIN, 0, 1, 100, 127, 200, u8::MAX])
185}
186
187#[driver_test]
188pub async fn ty_u16(test: &mut Test) -> Result<()> {
189    num_ty_test_body!(test, u16, &[u16::MIN, 0, 1, 10000, 32767, 50000, u16::MAX])
190}
191
192#[driver_test]
193pub async fn ty_u32(test: &mut Test) -> Result<()> {
194    num_ty_test_body!(
195        test,
196        u32,
197        &[u32::MIN, 0, 1, 1000000, 2147483647, 2000000000, u32::MAX]
198    )
199}
200
201#[driver_test]
202pub async fn ty_u64(test: &mut Test) -> Result<()> {
203    num_ty_test_body!(
204        test,
205        u64,
206        &[
207            u64::MIN,
208            0,
209            1,
210            1000000000000,
211            9223372036854775807,
212            10000000000000000000,
213            u64::MAX
214        ]
215    )
216}
217
218#[driver_test]
219pub async fn ty_usize(test: &mut Test) -> Result<()> {
220    num_ty_test_body!(
221        test,
222        usize,
223        &[
224            usize::MIN,
225            0,
226            1,
227            1000000000000,
228            9223372036854775807,
229            10000000000000000000,
230            usize::MAX
231        ]
232    )
233}
234
235#[driver_test]
236pub async fn ty_str(test: &mut Test) -> Result<()> {
237    #[derive(Debug, toasty::Model)]
238    struct Item {
239        #[key]
240        #[auto]
241        id: uuid::Uuid,
242        val: String,
243    }
244
245    let mut db = test.setup_db(models!(Item)).await;
246
247    let test_values: Vec<_> = [
248        gen_string(0, "empty"),
249        gen_string(10, "ascii"),
250        // 20 * 4 bytes = 80 bytes (well under MySQL's 191-byte limit)
251        gen_string(20, "unicode"),
252        gen_string(100, "mixed"),
253        gen_string(1_000, "ascii"),
254        gen_string(10_000, "mixed"),
255        // ~100KB - well under DynamoDB's 400KB limit
256        gen_string(100_000, "ascii"),
257        gen_string(20, "newlines"),
258        gen_string(100, "spaces"),
259    ]
260    .into_iter()
261    .filter(
262        |value| match test.capability().default_string_max_length() {
263            Some(max_len) => max_len >= value.len() as _,
264            None => true,
265        },
266    )
267    .collect();
268
269    // Clear setup operations
270    test.log().clear();
271
272    // Test 1: All test values round-trip
273    for val in &test_values {
274        let created = Item::create().val((*val).clone()).exec(&mut db).await?;
275
276        // Verify the INSERT operation stored the string value
277        let (op, _resp) = test.log().pop();
278        assert_struct!(op, Operation::QuerySql(_ {
279            stmt: Statement::Insert(_ {
280                target: InsertTarget::Table(_ {
281                    table: == table_id(&db, "items"),
282                    columns: == columns(&db, "items", &["id", "val"]),
283                    ..
284                }),
285                source.body: ExprSet::Values(_ {
286                    rows: [=~ (Any, val)],
287                    ..
288                }),
289                ..
290            }),
291            ..
292        }));
293
294        let read = Item::get_by_id(&mut db, &created.id).await?;
295        assert_eq!(read.val, *val);
296
297        test.log().clear();
298    }
299
300    // Test 2: Update chain
301    let mut record = Item::create().val(&test_values[0]).exec(&mut db).await?;
302    test.log().clear();
303
304    for val in &test_values {
305        record.update().val(val).exec(&mut db).await?;
306
307        // Verify the UPDATE operation sent the string value
308        let (op, _resp) = test.log().pop();
309        if test.capability().sql {
310            assert_struct!(op, Operation::QuerySql(_ {
311                stmt: Statement::Update(_ {
312                    assignments: #{ 1: _ { expr: _, .. }},
313                    ..
314                }),
315                ..
316            }));
317        } else {
318            assert_struct!(op, Operation::UpdateByKey(_ {
319                assignments: #{ 1: _ { expr: _, .. }},
320                ..
321            }));
322        }
323
324        let read = Item::get_by_id(&mut db, &record.id).await?;
325        assert_eq!(read.val, *val);
326
327        test.log().clear();
328    }
329    Ok(())
330}
331
332// Helper function to generate a test string with specific characteristics
333fn gen_string(length: usize, pattern: &str) -> String {
334    match pattern {
335        "empty" => String::new(),
336        "ascii" => "a".repeat(length),
337        "unicode" => "🦀".repeat(length),
338        "mixed" => "test ".repeat(length / 5), // ~5 chars per repeat
339        "newlines" => "line\n".repeat(length / 5),
340        "spaces" => " ".repeat(length),
341        _ => pattern.repeat(length / pattern.len().max(1)),
342    }
343}
344
345#[driver_test]
346pub async fn ty_bytes(test: &mut Test) -> Result<()> {
347    #[derive(Debug, toasty::Model)]
348    struct Item {
349        #[key]
350        #[auto]
351        id: uuid::Uuid,
352        val: Vec<u8>,
353    }
354
355    let mut db = test.setup_db(models!(Item)).await;
356
357    let test_values: Vec<Vec<u8>> = vec![
358        vec![],
359        vec![0],
360        vec![1, 2, 3],
361        vec![0xFF; 100],
362        (0..=255).collect(),
363        vec![0; 1000],
364    ];
365
366    // Clear setup operations
367    test.log().clear();
368
369    // Test 1: All test values round-trip
370    for val in &test_values {
371        let created = Item::create().val(val.clone()).exec(&mut db).await?;
372
373        // Verify the INSERT operation stored the bytes value
374        let (op, _resp) = test.log().pop();
375        assert_struct!(op, Operation::QuerySql(_ {
376            stmt: Statement::Insert(_ {
377                target: InsertTarget::Table(_ {
378                    table: == table_id(&db, "items"),
379                    columns: == columns(&db, "items", &["id", "val"]),
380                    ..
381                }),
382                source.body: ExprSet::Values(_ {
383                    rows: [=~ (Any, val.as_slice())],
384                    ..
385                }),
386                ..
387            }),
388            ..
389        }));
390
391        let read = Item::get_by_id(&mut db, &created.id).await?;
392        assert_eq!(read.val, *val);
393
394        test.log().clear();
395    }
396
397    // Test 2: Update chain
398    let mut record = Item::create()
399        .val(test_values[0].clone())
400        .exec(&mut db)
401        .await?;
402    test.log().clear();
403
404    for val in &test_values {
405        record.update().val(val.clone()).exec(&mut db).await?;
406
407        // Verify the UPDATE operation sent the bytes value
408        let (op, _resp) = test.log().pop();
409        if test.capability().sql {
410            assert_struct!(op, Operation::QuerySql(_ {
411                stmt: Statement::Update(_ {
412                    assignments: #{ 1: _ { expr: _, .. }},
413                    ..
414                }),
415                ..
416            }));
417        } else {
418            assert_struct!(op, Operation::UpdateByKey(_ {
419                assignments: #{ 1: _ { expr: _, .. }},
420                ..
421            }));
422        }
423
424        let read = Item::get_by_id(&mut db, &record.id).await?;
425        assert_eq!(read.val, *val);
426
427        test.log().clear();
428    }
429    Ok(())
430}
431
432#[driver_test]
433pub async fn ty_uuid(test: &mut Test) -> Result<()> {
434    #[derive(Debug, toasty::Model)]
435    struct Item {
436        #[key]
437        #[auto]
438        id: uuid::Uuid,
439        val: uuid::Uuid,
440    }
441
442    let mut db = test.setup_db(models!(Item)).await;
443
444    // Clear setup operations
445    test.log().clear();
446
447    for _ in 0..16 {
448        let val = uuid::Uuid::new_v4();
449        let created = Item::create().val(val).exec(&mut db).await?;
450
451        // Verify the INSERT operation - UUID should be stored in its native format
452        let (op, _resp) = test.log().pop();
453        assert_struct!(op, Operation::QuerySql(_ {
454            stmt: Statement::Insert(_ {
455                target: InsertTarget::Table(_ {
456                    table: == table_id(&db, "items"),
457                    columns: == columns(&db, "items", &["id", "val"]),
458                    ..
459                }),
460                ..
461            }),
462            ..
463        }));
464
465        match &test.capability().storage_types.default_uuid_type {
466            db::Type::Uuid => {
467                assert_struct!(op, Operation::QuerySql(_ {
468                    stmt: Statement::Insert(_ {
469                        source.body: ExprSet::Values(_ {
470                            rows: [=~ (Any, val)],
471                            ..
472                        }),
473                        ..
474                    }),
475                    ..
476                }));
477            }
478            db::Type::Blob => {
479                assert_struct!(op, Operation::QuerySql(_ {
480                    stmt: Statement::Insert(_ {
481                        source.body: ExprSet::Values(_ {
482                            rows: [=~ (Any, val.as_bytes())],
483                            ..
484                        }),
485                        ..
486                    }),
487                    ..
488                }));
489            }
490            db::Type::Text | db::Type::VarChar(..) => {
491                assert_struct!(op, Operation::QuerySql(_ {
492                    stmt: Statement::Insert(_ {
493                        source.body: ExprSet::Values(_ {
494                            rows: [=~ (Any, val.to_string())],
495                            ..
496                        }),
497                        ..
498                    }),
499                    ..
500                }));
501            }
502            ty => todo!("ty={ty:#?}"),
503        }
504
505        let read = Item::get_by_id(&mut db, &created.id).await?;
506        assert_eq!(read.val, val);
507
508        test.log().clear();
509    }
510    Ok(())
511}
512
513#[driver_test]
514pub async fn ty_smart_ptrs(test: &mut Test) -> Result<()> {
515    #[derive(Debug, toasty::Model)]
516    struct Item {
517        #[key]
518        #[auto]
519        id: uuid::Uuid,
520        arced: Arc<i32>,
521        rced: Rc<i32>,
522        boxed: Box<i32>,
523    }
524
525    let mut db = test.setup_db(models!(Item)).await;
526
527    // Clear setup operations
528    test.log().clear();
529
530    let created = Item::create()
531        .arced(1i32)
532        .rced(2i32)
533        .boxed(3i32)
534        .exec(&mut db)
535        .await?;
536
537    // Verify the INSERT operation stored the unwrapped values
538    let (op, _resp) = test.log().pop();
539    assert_struct!(op, Operation::QuerySql(_ {
540        stmt: Statement::Insert(_ {
541            target: InsertTarget::Table(_ {
542                table: == table_id(&db, "items"),
543                columns: == columns(&db, "items", &["id", "arced", "rced", "boxed"]),
544                ..
545            }),
546            source.body: ExprSet::Values(_ {
547                rows: [=~ (Any, Any, Any, Any)],
548                ..
549            }),
550            ..
551        }),
552        ..
553    }));
554
555    let read = Item::get_by_id(&mut db, &created.id).await?;
556    assert_eq!(created.id, read.id);
557    assert_eq!(created.arced, read.arced);
558    assert_eq!(created.rced, read.rced);
559    assert_eq!(created.boxed, read.boxed);
560
561    test.log().clear();
562    Ok(())
563}