toasty_driver_integration_suite/tests/
composite_key_pagination.rs

1//! Test pagination on composite-key models.
2//!
3//! These tests exercise `paginate()`, `limit()`, and `order_by()` on models
4//! with a partition + local key, which is the pattern DynamoDB uses for
5//! `QueryPk`. They intentionally have **no** `requires(sql)` gate so they run
6//! on every driver, including DynamoDB.
7
8use crate::prelude::*;
9use toasty::Page;
10use toasty_core::driver::{Operation, Rows};
11
12#[driver_test(requires(sql))]
13pub async fn paginate_composite_key(test: &mut Test) -> Result<()> {
14    #[derive(Debug, toasty::Model)]
15    #[key(partition = kind, local = seq)]
16    struct Event {
17        kind: String,
18        seq: i64,
19    }
20
21    let mut db = test.setup_db(models!(Event)).await;
22
23    // Seed 20 events under the same partition key so we can paginate over them.
24    for i in 0..20 {
25        Event::create().kind("info").seq(i).exec(&mut db).await?;
26    }
27
28    test.log().clear();
29
30    // First page (descending): should return seq 19..10
31    let page: Page<_> = Event::filter_by_kind("info")
32        .order_by(Event::fields().seq().desc())
33        .paginate(10)
34        .exec(&mut db)
35        .await?;
36
37    assert_eq!(page.len(), 10);
38    for (i, expected) in (10..20).rev().enumerate() {
39        assert_eq!(page[i].seq, expected);
40    }
41
42    // Verify the driver operation type
43    let (op, resp) = test.log().pop();
44    if test.capability().sql {
45        assert_struct!(op, Operation::QuerySql(_));
46    } else {
47        assert_struct!(op, Operation::QueryPk(_));
48    }
49    assert_struct!(resp.rows, Rows::Stream(_));
50
51    // Second page via .next()
52    let page: Page<_> = page.next(&mut db).await?.unwrap();
53    assert_eq!(page.len(), 10);
54    for (i, expected) in (0..10).rev().enumerate() {
55        assert_eq!(page[i].seq, expected);
56    }
57
58    // Go back to the first page via .prev()
59    let page: Page<_> = page.prev(&mut db).await?.unwrap();
60    assert_eq!(page.len(), 10);
61    for (i, expected) in (10..20).rev().enumerate() {
62        assert_eq!(page[i].seq, expected);
63    }
64
65    Ok(())
66}
67
68#[driver_test(requires(sql))]
69pub async fn paginate_composite_key_asc(test: &mut Test) -> Result<()> {
70    #[derive(Debug, toasty::Model)]
71    #[key(partition = kind, local = seq)]
72    struct Event {
73        kind: String,
74        seq: i64,
75    }
76
77    let mut db = test.setup_db(models!(Event)).await;
78
79    for i in 0..20 {
80        Event::create().kind("info").seq(i).exec(&mut db).await?;
81    }
82
83    test.log().clear();
84
85    // First page (ascending): should return seq 0..9
86    let page: Page<_> = Event::filter_by_kind("info")
87        .order_by(Event::fields().seq().asc())
88        .paginate(5)
89        .exec(&mut db)
90        .await?;
91
92    assert_eq!(page.len(), 5);
93    for (i, expected) in (0..5).enumerate() {
94        assert_eq!(page[i].seq, expected);
95    }
96
97    let (op, _) = test.log().pop();
98    if test.capability().sql {
99        assert_struct!(op, Operation::QuerySql(_));
100    } else {
101        assert_struct!(op, Operation::QueryPk(_));
102    }
103
104    // Walk forward through all pages and collect every seq value.
105    let mut all_seqs: Vec<i64> = page.iter().map(|e| e.seq).collect();
106    let mut current = page;
107    while let Some(next) = current.next(&mut db).await? {
108        all_seqs.extend(next.iter().map(|e| e.seq));
109        current = next;
110    }
111
112    assert_eq!(all_seqs, (0..20).collect::<Vec<_>>());
113
114    Ok(())
115}
116
117#[driver_test]
118pub async fn limit_composite_key(test: &mut Test) -> Result<()> {
119    #[derive(Debug, toasty::Model)]
120    #[key(partition = kind, local = seq)]
121    struct Event {
122        kind: String,
123        seq: i64,
124    }
125
126    let mut db = test.setup_db(models!(Event)).await;
127
128    for i in 0..20 {
129        Event::create().kind("info").seq(i).exec(&mut db).await?;
130    }
131
132    test.log().clear();
133
134    // Limit without explicit ordering
135    let events: Vec<_> = Event::filter_by_kind("info").limit(7).exec(&mut db).await?;
136    assert_eq!(events.len(), 7);
137
138    let (op, _) = test.log().pop();
139    if test.capability().sql {
140        assert_struct!(op, Operation::QuerySql(_));
141    } else {
142        assert_struct!(op, Operation::QueryPk(_));
143    }
144
145    test.log().clear();
146
147    // Limit combined with descending order
148    let events: Vec<_> = Event::filter_by_kind("info")
149        .order_by(Event::fields().seq().desc())
150        .limit(5)
151        .exec(&mut db)
152        .await?;
153    assert_eq!(events.len(), 5);
154    for i in 0..4 {
155        assert!(events[i].seq > events[i + 1].seq);
156    }
157    // The first item should be the highest seq
158    assert_eq!(events[0].seq, 19);
159
160    test.log().clear();
161
162    // Limit larger than result set returns all results
163    let events: Vec<_> = Event::filter_by_kind("info")
164        .limit(100)
165        .exec(&mut db)
166        .await?;
167    assert_eq!(events.len(), 20);
168
169    Ok(())
170}
171
172#[driver_test]
173pub async fn sort_composite_key(test: &mut Test) -> Result<()> {
174    #[derive(Debug, toasty::Model)]
175    #[key(partition = kind, local = seq)]
176    struct Event {
177        kind: String,
178        seq: i64,
179    }
180
181    let mut db = test.setup_db(models!(Event)).await;
182
183    for i in 0..20 {
184        Event::create().kind("info").seq(i).exec(&mut db).await?;
185    }
186
187    test.log().clear();
188
189    // Ascending sort
190    let events: Vec<_> = Event::filter_by_kind("info")
191        .order_by(Event::fields().seq().asc())
192        .exec(&mut db)
193        .await?;
194
195    assert_eq!(events.len(), 20);
196    for i in 0..19 {
197        assert!(events[i].seq < events[i + 1].seq);
198    }
199
200    let (op, resp) = test.log().pop();
201    if test.capability().sql {
202        assert_struct!(op, Operation::QuerySql(_));
203    } else {
204        assert_struct!(op, Operation::QueryPk(_));
205    }
206    assert_struct!(resp.rows, Rows::Stream(_));
207
208    test.log().clear();
209
210    // Descending sort
211    let events: Vec<_> = Event::filter_by_kind("info")
212        .order_by(Event::fields().seq().desc())
213        .exec(&mut db)
214        .await?;
215
216    assert_eq!(events.len(), 20);
217    for i in 0..19 {
218        assert!(events[i].seq > events[i + 1].seq);
219    }
220
221    let (op, resp) = test.log().pop();
222    if test.capability().sql {
223        assert_struct!(op, Operation::QuerySql(_));
224    } else {
225        assert_struct!(op, Operation::QueryPk(_));
226    }
227    assert_struct!(resp.rows, Rows::Stream(_));
228
229    Ok(())
230}