Skip to main content

toasty_driver_integration_suite/tests/
crud_query_macro.rs

1use crate::prelude::*;
2
3#[driver_test(id(ID), scenario(crate::scenarios::two_models), requires(scan))]
4pub async fn query_macro_all(test: &mut Test) -> Result<()> {
5    let mut db = setup(test).await;
6
7    toasty::create!(User::[{ name: "Alice" }, { name: "Bob" }])
8        .exec(&mut db)
9        .await?;
10
11    // query!(User) expands to User::all()
12    let users = toasty::query!(User).exec(&mut db).await?;
13    assert_struct!(users, #({ name: "Alice" }, { name: "Bob" }));
14
15    Ok(())
16}
17
18#[driver_test(id(ID), scenario(crate::scenarios::two_models), requires(scan))]
19pub async fn query_macro_filter_eq(test: &mut Test) -> Result<()> {
20    let mut db = setup(test).await;
21
22    toasty::create!(User::[{ name: "Alice" }, { name: "Bob" }])
23        .exec(&mut db)
24        .await?;
25
26    // query!(User filter .name == "Alice") expands to User::filter(User::fields().name().eq("Alice"))
27    let users = toasty::query!(User filter .name == "Alice")
28        .exec(&mut db)
29        .await?;
30
31    assert_struct!(users, [{ name: "Alice" }]);
32
33    Ok(())
34}
35
36#[driver_test(id(ID), scenario(crate::scenarios::two_models), requires(scan))]
37pub async fn query_macro_filter_ne(test: &mut Test) -> Result<()> {
38    let mut db = setup(test).await;
39
40    toasty::create!(User::[{ name: "Alice" }, { name: "Bob" }])
41        .exec(&mut db)
42        .await?;
43
44    let users = toasty::query!(User filter .name != "Alice")
45        .exec(&mut db)
46        .await?;
47
48    assert_struct!(users, [{ name: "Bob" }]);
49
50    Ok(())
51}
52
53#[driver_test(id(ID), scenario(crate::scenarios::user_with_age), requires(scan))]
54pub async fn query_macro_filter_numeric_comparisons(test: &mut Test) -> Result<()> {
55    let mut db = setup(test).await;
56
57    toasty::create!(User::[
58        { name: "Young", age: 15 },
59        { name: "Adult", age: 25 },
60        { name: "Senior", age: 65 },
61    ])
62    .exec(&mut db)
63    .await?;
64
65    // Greater than
66    let users = toasty::query!(User filter .age > 20).exec(&mut db).await?;
67    assert_struct!(users, #({ name: "Adult" }, { name: "Senior" }));
68
69    // Greater than or equal
70    let users = toasty::query!(User filter .age >= 25).exec(&mut db).await?;
71    assert_struct!(users, #({ name: "Adult" }, { name: "Senior" }));
72
73    // Less than
74    let users = toasty::query!(User filter .age < 25).exec(&mut db).await?;
75    assert_struct!(users, [{ name: "Young" }]);
76
77    // Less than or equal
78    let users = toasty::query!(User filter .age <= 25).exec(&mut db).await?;
79    assert_struct!(users, #({ name: "Young" }, { name: "Adult" }));
80
81    Ok(())
82}
83
84#[driver_test(id(ID), scenario(crate::scenarios::user_with_age), requires(scan))]
85pub async fn query_macro_filter_and(test: &mut Test) -> Result<()> {
86    let mut db = setup(test).await;
87
88    toasty::create!(User::[
89        { name: "Alice", age: 30 },
90        { name: "Bob", age: 30 },
91        { name: "Alice", age: 20 },
92    ])
93    .exec(&mut db)
94    .await?;
95
96    let users = toasty::query!(User filter .name == "Alice" and .age == 30)
97        .exec(&mut db)
98        .await?;
99
100    assert_struct!(users, [{ name: "Alice", age: 30 }]);
101
102    Ok(())
103}
104
105#[driver_test(id(ID), scenario(crate::scenarios::two_models), requires(scan))]
106pub async fn query_macro_filter_or(test: &mut Test) -> Result<()> {
107    let mut db = setup(test).await;
108
109    toasty::create!(User::[{ name: "Alice" }, { name: "Bob" }, { name: "Carl" }])
110        .exec(&mut db)
111        .await?;
112
113    let users = toasty::query!(User filter .name == "Alice" or .name == "Bob")
114        .exec(&mut db)
115        .await?;
116
117    assert_struct!(users, #({ name: "Alice" }, { name: "Bob" }));
118
119    Ok(())
120}
121
122#[driver_test(id(ID), scenario(crate::scenarios::two_models), requires(scan))]
123pub async fn query_macro_filter_not(test: &mut Test) -> Result<()> {
124    let mut db = setup(test).await;
125
126    toasty::create!(User::[{ name: "Alice" }, { name: "Bob" }])
127        .exec(&mut db)
128        .await?;
129
130    let users = toasty::query!(User filter not .name == "Alice")
131        .exec(&mut db)
132        .await?;
133
134    assert_struct!(users, [{ name: "Bob" }]);
135
136    Ok(())
137}
138
139// Gated on `requires(sql)` until [#857] is fixed — `OR` of comparison ops
140// nested inside an `AND` panics in eval on the DynamoDB scan path.
141//
142// [#857]: https://github.com/tokio-rs/toasty/issues/857
143#[driver_test(id(ID), scenario(crate::scenarios::user_with_age), requires(sql))]
144pub async fn query_macro_filter_parens(test: &mut Test) -> Result<()> {
145    let mut db = setup(test).await;
146
147    toasty::create!(User::[
148        { name: "Alice", age: 30 },
149        { name: "Bob", age: 20 },
150        { name: "Carl", age: 40 },
151    ])
152    .exec(&mut db)
153    .await?;
154
155    // AND binds tighter than OR, so parentheses change the grouping:
156    // .name == "Alice" AND (.age > 25 OR .age < 15)
157    let users = toasty::query!(User filter .name == "Alice" and (.age > 25 or .age < 15))
158        .exec(&mut db)
159        .await?;
160
161    assert_struct!(users, [{ name: "Alice" }]);
162
163    Ok(())
164}
165
166#[driver_test(id(ID), scenario(crate::scenarios::two_models), requires(scan))]
167pub async fn query_macro_filter_external_ref(test: &mut Test) -> Result<()> {
168    let mut db = setup(test).await;
169
170    toasty::create!(User::[{ name: "Alice" }, { name: "Bob" }])
171        .exec(&mut db)
172        .await?;
173
174    let target_name = "Alice";
175    let users = toasty::query!(User filter .name == #target_name)
176        .exec(&mut db)
177        .await?;
178
179    assert_struct!(users, [{ name: "Alice" }]);
180
181    Ok(())
182}
183
184#[driver_test(id(ID), scenario(crate::scenarios::two_models), requires(scan))]
185pub async fn query_macro_filter_external_expr(test: &mut Test) -> Result<()> {
186    let mut db = setup(test).await;
187
188    toasty::create!(User::[{ name: "Alice" }, { name: "Bob" }])
189        .exec(&mut db)
190        .await?;
191
192    fn get_name() -> &'static str {
193        "Bob"
194    }
195
196    let users = toasty::query!(User filter .name == #(get_name()))
197        .exec(&mut db)
198        .await?;
199
200    assert_struct!(users, [{ name: "Bob" }]);
201
202    Ok(())
203}
204
205#[driver_test(id(ID), scenario(crate::scenarios::two_models), requires(scan))]
206pub async fn query_macro_case_insensitive_keywords(test: &mut Test) -> Result<()> {
207    let mut db = setup(test).await;
208
209    toasty::create!(User::[{ name: "Alice" }, { name: "Bob" }])
210        .exec(&mut db)
211        .await?;
212
213    // FILTER (uppercase)
214    let users = toasty::query!(User FILTER .name == "Alice")
215        .exec(&mut db)
216        .await?;
217    assert_struct!(users, [{ name: "Alice" }]);
218
219    // Filter (mixed case), AND, OR
220    let users = toasty::query!(User Filter .name == "Alice" AND .name == "Alice")
221        .exec(&mut db)
222        .await?;
223    assert_struct!(users, [{ name: "Alice" }]);
224
225    Ok(())
226}
227
228#[driver_test(id(ID), scenario(crate::scenarios::user_with_age), requires(scan))]
229pub async fn query_macro_complex_boolean(test: &mut Test) -> Result<()> {
230    let mut db = setup(test).await;
231
232    toasty::create!(User::[
233        { name: "Alice", age: 30 },
234        { name: "Bob", age: 20 },
235        { name: "Carl", age: 40 },
236        { name: "Diana", age: 10 },
237    ])
238    .exec(&mut db)
239    .await?;
240
241    // Complex: NOT (.age < 18) AND (.name == "Alice" OR .name == "Carl")
242    let users =
243        toasty::query!(User filter not (.age < 18) and (.name == "Alice" or .name == "Carl"))
244            .exec(&mut db)
245            .await?;
246
247    assert_struct!(users, #({ name: "Alice" }, { name: "Carl" }));
248
249    Ok(())
250}
251
252// Gated on `requires(sql)` until [#856] is fixed — DynamoDB rejects the bool
253// attribute value type when binding `bool` literals in filter predicates.
254//
255// [#856]: https://github.com/tokio-rs/toasty/issues/856
256#[driver_test(id(ID), requires(sql))]
257pub async fn query_macro_filter_bool_literal(test: &mut Test) -> Result<()> {
258    #[derive(Debug, toasty::Model)]
259    struct Item {
260        #[key]
261        #[auto]
262        id: ID,
263
264        name: String,
265
266        #[index]
267        active: bool,
268    }
269
270    let mut db = test.setup_db(models!(Item)).await;
271
272    toasty::create!(Item::[
273        { name: "on", active: true },
274        { name: "off", active: false },
275    ])
276    .exec(&mut db)
277    .await?;
278
279    let items = toasty::query!(Item filter .active == true)
280        .exec(&mut db)
281        .await?;
282
283    assert_struct!(items, [{ name: "on" }]);
284
285    Ok(())
286}
287
288// --- ORDER BY, LIMIT, OFFSET tests ---
289
290#[driver_test(id(ID), scenario(crate::scenarios::two_models), requires(sql))]
291pub async fn query_macro_order_by_asc(test: &mut Test) -> Result<()> {
292    let mut db = setup(test).await;
293
294    toasty::create!(User::[{ name: "Carl" }, { name: "Alice" }, { name: "Bob" }])
295        .exec(&mut db)
296        .await?;
297
298    let users = toasty::query!(User ORDER BY .name ASC)
299        .exec(&mut db)
300        .await?;
301    assert_struct!(users, [{ name: "Alice" }, { name: "Bob" }, { name: "Carl" }]);
302
303    Ok(())
304}
305
306#[driver_test(id(ID), scenario(crate::scenarios::two_models), requires(sql))]
307pub async fn query_macro_order_by_desc(test: &mut Test) -> Result<()> {
308    let mut db = setup(test).await;
309
310    toasty::create!(User::[{ name: "Carl" }, { name: "Alice" }, { name: "Bob" }])
311        .exec(&mut db)
312        .await?;
313
314    let users = toasty::query!(User ORDER BY .name DESC)
315        .exec(&mut db)
316        .await?;
317    assert_struct!(users, [{ name: "Carl" }, { name: "Bob" }, { name: "Alice" }]);
318
319    Ok(())
320}
321
322#[driver_test(id(ID), scenario(crate::scenarios::two_models), requires(sql))]
323pub async fn query_macro_limit(test: &mut Test) -> Result<()> {
324    let mut db = setup(test).await;
325
326    toasty::create!(User::[{ name: "Alice" }, { name: "Bob" }, { name: "Carl" }])
327        .exec(&mut db)
328        .await?;
329
330    let users = toasty::query!(User ORDER BY .name ASC LIMIT 2)
331        .exec(&mut db)
332        .await?;
333    assert_eq!(users.len(), 2);
334    assert_struct!(users, [{ name: "Alice" }, { name: "Bob" }]);
335
336    Ok(())
337}
338
339#[driver_test(id(ID), scenario(crate::scenarios::two_models), requires(sql))]
340pub async fn query_macro_offset_and_limit(test: &mut Test) -> Result<()> {
341    let mut db = setup(test).await;
342
343    toasty::create!(User::[{ name: "Alice" }, { name: "Bob" }, { name: "Carl" }, { name: "Diana" }])
344        .exec(&mut db)
345        .await?;
346
347    // Skip 1, take 2 (ordered by name ascending)
348    let users = toasty::query!(User ORDER BY .name ASC OFFSET 1 LIMIT 2)
349        .exec(&mut db)
350        .await?;
351    assert_struct!(users, [{ name: "Bob" }, { name: "Carl" }]);
352
353    Ok(())
354}
355
356#[driver_test(id(ID), scenario(crate::scenarios::user_with_age), requires(sql))]
357pub async fn query_macro_filter_with_order_by_and_limit(test: &mut Test) -> Result<()> {
358    let mut db = setup(test).await;
359
360    toasty::create!(User::[
361        { name: "Alice", age: 30 },
362        { name: "Bob", age: 25 },
363        { name: "Carl", age: 35 },
364        { name: "Diana", age: 20 },
365    ])
366    .exec(&mut db)
367    .await?;
368
369    // Filter age > 20, order by name desc, limit 2
370    let users = toasty::query!(User FILTER .age > 20 ORDER BY .name DESC LIMIT 2)
371        .exec(&mut db)
372        .await?;
373    assert_struct!(users, [{ name: "Carl" }, { name: "Bob" }]);
374
375    Ok(())
376}
377
378#[driver_test(id(ID), scenario(crate::scenarios::two_models), requires(sql))]
379pub async fn query_macro_limit_external_ref(test: &mut Test) -> Result<()> {
380    let mut db = setup(test).await;
381
382    toasty::create!(User::[{ name: "Alice" }, { name: "Bob" }, { name: "Carl" }])
383        .exec(&mut db)
384        .await?;
385
386    let n = 1;
387    let users = toasty::query!(User ORDER BY .name ASC LIMIT #n)
388        .exec(&mut db)
389        .await?;
390    assert_struct!(users, [{ name: "Alice" }]);
391
392    Ok(())
393}
394
395#[driver_test(id(ID), scenario(crate::scenarios::two_models), requires(sql))]
396pub async fn query_macro_case_insensitive_order_limit(test: &mut Test) -> Result<()> {
397    let mut db = setup(test).await;
398
399    toasty::create!(User::[{ name: "Alice" }, { name: "Bob" }, { name: "Carl" }])
400        .exec(&mut db)
401        .await?;
402
403    // Case-insensitive: order, by, asc, limit
404    let users = toasty::query!(User order by .name asc limit 2)
405        .exec(&mut db)
406        .await?;
407    assert_struct!(users, [{ name: "Alice" }, { name: "Bob" }]);
408
409    Ok(())
410}
411
412// --- DynamoDB-compatible query macro tests ---
413// These use composite primary keys (partition + local) so queries can be served
414// by DynamoDB's key condition expressions.
415
416#[driver_test(id(ID))]
417pub async fn query_macro_partition_key_eq(test: &mut Test) -> Result<()> {
418    #[derive(Debug, toasty::Model)]
419    #[key(partition = league, local = name)]
420    struct Team {
421        league: String,
422
423        name: String,
424
425        founded: i64,
426    }
427
428    let mut db = test.setup_db(models!(Team)).await;
429
430    for (league, name, founded) in [
431        ("MLS", "Portland Timbers", 2009),
432        ("MLS", "Seattle Sounders FC", 2007),
433        ("EPL", "Arsenal", 1886),
434        ("EPL", "Chelsea", 1905),
435    ] {
436        toasty::create!(Team {
437            league: league,
438            name: name,
439            founded: founded
440        })
441        .exec(&mut db)
442        .await?;
443    }
444
445    // Filter on partition key only
446    let teams = toasty::query!(Team filter .league == "EPL")
447        .exec(&mut db)
448        .await?;
449
450    assert_struct!(teams, #({ name: "Arsenal" }, { name: "Chelsea" }));
451
452    Ok(())
453}
454
455#[driver_test(id(ID))]
456pub async fn query_macro_partition_and_local_key(test: &mut Test) -> Result<()> {
457    #[derive(Debug, toasty::Model)]
458    #[key(partition = league, local = name)]
459    struct Team {
460        league: String,
461
462        name: String,
463
464        founded: i64,
465    }
466
467    let mut db = test.setup_db(models!(Team)).await;
468
469    for (league, name, founded) in [
470        ("MLS", "Portland Timbers", 2009),
471        ("MLS", "Seattle Sounders FC", 2007),
472        ("EPL", "Arsenal", 1886),
473        ("EPL", "Chelsea", 1905),
474    ] {
475        toasty::create!(Team {
476            league: league,
477            name: name,
478            founded: founded
479        })
480        .exec(&mut db)
481        .await?;
482    }
483
484    // Filter on partition key + local key
485    let teams = toasty::query!(Team filter .league == "MLS" and .name == "Portland Timbers")
486        .exec(&mut db)
487        .await?;
488
489    assert_struct!(teams, [{ name: "Portland Timbers", founded: 2009 }]);
490
491    Ok(())
492}
493
494#[driver_test(id(ID))]
495pub async fn query_macro_local_key_comparison(test: &mut Test) -> Result<()> {
496    #[derive(Debug, toasty::Model)]
497    #[key(partition = kind, local = timestamp)]
498    struct Event {
499        kind: String,
500
501        timestamp: i64,
502    }
503
504    let mut db = test.setup_db(models!(Event)).await;
505
506    for (kind, ts) in [
507        ("info", 0),
508        ("info", 2),
509        ("info", 4),
510        ("info", 6),
511        ("info", 8),
512        ("info", 10),
513        ("warn", 1),
514        ("warn", 3),
515        ("warn", 5),
516    ] {
517        toasty::create!(Event {
518            kind: kind,
519            timestamp: ts
520        })
521        .exec(&mut db)
522        .await?;
523    }
524
525    // Partition key + greater-than on local key
526    let events = toasty::query!(Event filter .kind == "info" and .timestamp > 6)
527        .exec(&mut db)
528        .await?;
529
530    assert_struct!(events, #({ timestamp: 8 }, { timestamp: 10 }));
531
532    // Partition key + less-than-or-equal on local key
533    let events = toasty::query!(Event filter .kind == "info" and .timestamp <= 4)
534        .exec(&mut db)
535        .await?;
536
537    assert_struct!(events, #({ timestamp: 0 }, { timestamp: 2 }, { timestamp: 4 }));
538
539    Ok(())
540}
541
542#[driver_test(id(ID))]
543pub async fn query_macro_partition_key_external_ref(test: &mut Test) -> Result<()> {
544    #[derive(Debug, toasty::Model)]
545    #[key(partition = league, local = name)]
546    struct Team {
547        league: String,
548
549        name: String,
550
551        founded: i64,
552    }
553
554    let mut db = test.setup_db(models!(Team)).await;
555
556    for (league, name, founded) in [
557        ("MLS", "Portland Timbers", 2009),
558        ("MLS", "Seattle Sounders FC", 2007),
559        ("EPL", "Arsenal", 1886),
560    ] {
561        toasty::create!(Team {
562            league: league,
563            name: name,
564            founded: founded
565        })
566        .exec(&mut db)
567        .await?;
568    }
569
570    // Use external variable reference with partition key query
571    let target_league = "MLS";
572    let teams = toasty::query!(Team filter .league == #target_league)
573        .exec(&mut db)
574        .await?;
575
576    assert_struct!(teams, #({ name: "Portland Timbers" }, { name: "Seattle Sounders FC" }));
577
578    Ok(())
579}
580
581#[driver_test(id(ID))]
582pub async fn query_macro_partition_key_with_not(test: &mut Test) -> Result<()> {
583    #[derive(Debug, toasty::Model)]
584    #[key(partition = team, local = name)]
585    struct Player {
586        team: String,
587
588        name: String,
589
590        position: String,
591    }
592
593    let mut db = test.setup_db(models!(Player)).await;
594
595    for (team, name, position) in [
596        ("Timbers", "Diego Valeri", "Midfielder"),
597        ("Timbers", "Fanendo Adi", "Forward"),
598        ("Timbers", "Adam Kwarasey", "Goalkeeper"),
599        ("Sounders", "Clint Dempsey", "Forward"),
600    ] {
601        toasty::create!(Player {
602            team: team,
603            name: name,
604            position: position
605        })
606        .exec(&mut db)
607        .await?;
608    }
609
610    // Partition key + NOT on non-key field
611    let players =
612        toasty::query!(Player filter .team == "Timbers" and not .position == "Midfielder")
613            .exec(&mut db)
614            .await?;
615
616    assert_struct!(players, #({ name: "Adam Kwarasey" }, { name: "Fanendo Adi" }));
617
618    Ok(())
619}
620
621#[driver_test(id(ID))]
622pub async fn query_macro_partition_key_with_or(test: &mut Test) -> Result<()> {
623    #[derive(Debug, toasty::Model)]
624    #[key(partition = team, local = name)]
625    struct Player {
626        team: String,
627
628        name: String,
629
630        position: String,
631
632        number: i64,
633    }
634
635    let mut db = test.setup_db(models!(Player)).await;
636
637    for (team, name, position, number) in [
638        ("Timbers", "Diego Valeri", "Midfielder", 8),
639        ("Timbers", "Darlington Nagbe", "Midfielder", 6),
640        ("Timbers", "Fanendo Adi", "Forward", 9),
641        ("Timbers", "Adam Kwarasey", "Goalkeeper", 1),
642        ("Sounders", "Clint Dempsey", "Forward", 2),
643    ] {
644        toasty::create!(Player {
645            team: team,
646            name: name,
647            position: position,
648            number: number
649        })
650        .exec(&mut db)
651        .await?;
652    }
653
654    // Partition key + OR on non-key fields
655    let players = toasty::query!(Player filter .team == "Timbers" and (.position == "Forward" or .position == "Goalkeeper"))
656        .exec(&mut db)
657        .await?;
658
659    assert_struct!(players, #({ name: "Adam Kwarasey" }, { name: "Fanendo Adi" }));
660
661    Ok(())
662}
663
664#[driver_test(id(ID), scenario(crate::scenarios::two_models), requires(scan))]
665pub async fn query_macro_filter_in_list(test: &mut Test) -> Result<()> {
666    let mut db = setup(test).await;
667
668    toasty::create!(User::[{ name: "Alice" }, { name: "Bob" }, { name: "Carl" }])
669        .exec(&mut db)
670        .await?;
671
672    // Literal list
673    let users = toasty::query!(User filter .name IN ["Alice", "Carl"])
674        .exec(&mut db)
675        .await?;
676
677    assert_struct!(users, #({ name: "Alice" }, { name: "Carl" }));
678
679    Ok(())
680}
681
682#[driver_test(id(ID), scenario(crate::scenarios::two_models), requires(scan))]
683pub async fn query_macro_filter_in_list_external_ref(test: &mut Test) -> Result<()> {
684    let mut db = setup(test).await;
685
686    toasty::create!(User::[{ name: "Alice" }, { name: "Bob" }, { name: "Carl" }])
687        .exec(&mut db)
688        .await?;
689
690    // External variable reference
691    let names = vec!["Alice", "Bob"];
692    let users = toasty::query!(User filter .name IN #names)
693        .exec(&mut db)
694        .await?;
695
696    assert_struct!(users, #({ name: "Alice" }, { name: "Bob" }));
697
698    Ok(())
699}
700
701#[driver_test(id(ID), scenario(crate::scenarios::two_models), requires(scan))]
702pub async fn query_macro_filter_in_list_with_and(test: &mut Test) -> Result<()> {
703    let mut db = setup(test).await;
704
705    toasty::create!(User::[{ name: "Alice" }, { name: "Bob" }, { name: "Carl" }])
706        .exec(&mut db)
707        .await?;
708
709    // IN combined with AND
710    let users = toasty::query!(User filter .name IN ["Alice", "Bob", "Carl"] and .name != "Bob")
711        .exec(&mut db)
712        .await?;
713
714    assert_struct!(users, #({ name: "Alice" }, { name: "Carl" }));
715
716    Ok(())
717}
718
719#[driver_test(id(ID))]
720pub async fn query_macro_filter_in_list_by_pk(test: &mut Test) -> Result<()> {
721    #[derive(Debug, toasty::Model)]
722    struct Item {
723        #[key]
724        #[auto]
725        id: ID,
726
727        name: String,
728    }
729
730    let mut db = test.setup_db(models!(Item)).await;
731
732    // Create several items and collect their IDs
733    let mut ids = Vec::new();
734    for name in ["Alice", "Bob", "Carl", "Diana"] {
735        let item = Item::create().name(name).exec(&mut db).await?;
736        ids.push(item.id);
737    }
738
739    // Batch fetch a subset by primary key using IN
740    let target_ids = vec![ids[0], ids[2]]; // Alice and Carl
741    let items = toasty::query!(Item filter .id IN #target_ids)
742        .exec(&mut db)
743        .await?;
744
745    assert_eq!(items.len(), 2);
746    assert_struct!(items, #({ name: "Alice" }, { name: "Carl" }));
747
748    Ok(())
749}