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
10macro_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 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 test.log().clear();
39
40 for &val in &test_values {
42 let created = Item::create().val(val).exec(&mut db).await?;
43
44 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 test.log().clear();
67 }
68
69 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 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 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 gen_string(20, "unicode"),
252 gen_string(100, "mixed"),
253 gen_string(1_000, "ascii"),
254 gen_string(10_000, "mixed"),
255 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 test.log().clear();
271
272 for val in &test_values {
274 let created = Item::create().val((*val).clone()).exec(&mut db).await?;
275
276 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 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 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
332fn 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), "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 test.log().clear();
368
369 for val in &test_values {
371 let created = Item::create().val(val.clone()).exec(&mut db).await?;
372
373 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 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 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 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 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 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 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}