toasty_driver_integration_suite/tests/
batch_create_statements.rs

1use crate::prelude::*;
2
3use toasty_core::{
4    driver::{operation::Transaction, Operation},
5    stmt::Statement,
6};
7
8/// Batch two creates of the same model.
9#[driver_test(id(ID), requires(sql))]
10pub async fn batch_two_creates_same_model(t: &mut Test) -> Result<()> {
11    #[derive(Debug, toasty::Model)]
12    struct User {
13        #[key]
14        #[auto]
15        id: ID,
16        name: String,
17    }
18
19    let mut db = t.setup_db(models!(User)).await;
20
21    t.log().clear();
22    let (alice, bob): (User, User) =
23        toasty::batch((User::create().name("Alice"), User::create().name("Bob")))
24            .exec(&mut db)
25            .await?;
26
27    assert_eq!(alice.name, "Alice");
28    assert_eq!(bob.name, "Bob");
29
30    // Two independent creates → transaction-wrapped
31    assert_struct!(
32        t.log().pop_op(),
33        Operation::Transaction(Transaction::Start {
34            isolation: None,
35            read_only: false
36        })
37    );
38    assert_struct!(t.log().pop_op(), Operation::QuerySql(_ {
39        stmt: Statement::Insert(_),
40        ..
41    })); // INSERT alice
42    assert_struct!(t.log().pop_op(), Operation::QuerySql(_ {
43        stmt: Statement::Insert(_),
44        ..
45    })); // INSERT bob
46    assert!(t.log().pop_op().is_transaction_commit());
47    assert!(t.log().is_empty());
48
49    // Verify both were persisted
50    let all: Vec<_> = User::filter_by_id(alice.id).exec(&mut db).await?;
51    assert_eq!(all.len(), 1);
52    let all: Vec<_> = User::filter_by_id(bob.id).exec(&mut db).await?;
53    assert_eq!(all.len(), 1);
54
55    Ok(())
56}
57
58/// Batch creates of two different models.
59#[driver_test(id(ID), requires(sql))]
60pub async fn batch_two_creates_different_models(t: &mut Test) -> Result<()> {
61    #[derive(Debug, toasty::Model)]
62    struct User {
63        #[key]
64        #[auto]
65        id: ID,
66        name: String,
67    }
68
69    #[derive(Debug, toasty::Model)]
70    struct Post {
71        #[key]
72        #[auto]
73        id: ID,
74        title: String,
75    }
76
77    let mut db = t.setup_db(models!(User, Post)).await;
78
79    t.log().clear();
80    let (user, post): (User, Post) = toasty::batch((
81        User::create().name("Alice"),
82        Post::create().title("Hello World"),
83    ))
84    .exec(&mut db)
85    .await?;
86
87    assert_eq!(user.name, "Alice");
88    assert_eq!(post.title, "Hello World");
89
90    // Two independent creates → transaction-wrapped
91    assert_struct!(
92        t.log().pop_op(),
93        Operation::Transaction(Transaction::Start {
94            isolation: None,
95            read_only: false
96        })
97    );
98    assert_struct!(t.log().pop_op(), Operation::QuerySql(_ {
99        stmt: Statement::Insert(_),
100        ..
101    })); // INSERT user
102    assert_struct!(t.log().pop_op(), Operation::QuerySql(_ {
103        stmt: Statement::Insert(_),
104        ..
105    })); // INSERT post
106    assert!(t.log().pop_op().is_transaction_commit());
107    assert!(t.log().is_empty());
108
109    // Verify persistence
110    let found = User::get_by_id(&mut db, user.id).await?;
111    assert_eq!(found.name, "Alice");
112    let found = Post::get_by_id(&mut db, post.id).await?;
113    assert_eq!(found.title, "Hello World");
114
115    Ok(())
116}
117
118/// Batch mixing a create and a query, with the create coming second.
119#[driver_test(id(ID), requires(sql))]
120pub async fn batch_query_then_create(t: &mut Test) -> Result<()> {
121    #[derive(Debug, toasty::Model)]
122    struct User {
123        #[key]
124        #[auto]
125        id: ID,
126        #[index]
127        name: String,
128    }
129
130    let mut db = t.setup_db(models!(User)).await;
131    User::create().name("Alice").exec(&mut db).await?;
132
133    t.log().clear();
134    let (existing, created): (Vec<User>, User) =
135        toasty::batch((User::filter_by_name("Alice"), User::create().name("Bob")))
136            .exec(&mut db)
137            .await?;
138
139    assert_struct!(existing, [_ { name: "Alice" }]);
140    assert_eq!(created.name, "Bob");
141
142    // Two operations (query + create) → transaction-wrapped
143    assert_struct!(
144        t.log().pop_op(),
145        Operation::Transaction(Transaction::Start {
146            isolation: None,
147            read_only: false
148        })
149    );
150    assert_struct!(t.log().pop_op(), Operation::QuerySql(_ {
151        stmt: Statement::Query(_),
152        ..
153    })); // SELECT
154    assert_struct!(t.log().pop_op(), Operation::QuerySql(_ {
155        stmt: Statement::Insert(_),
156        ..
157    })); // INSERT
158    assert!(t.log().pop_op().is_transaction_commit());
159    assert!(t.log().is_empty());
160
161    Ok(())
162}
163
164/// Batch mixing a create first and a query second.
165#[driver_test(id(ID), requires(sql))]
166pub async fn batch_create_then_query(t: &mut Test) -> Result<()> {
167    #[derive(Debug, toasty::Model)]
168    struct User {
169        #[key]
170        #[auto]
171        id: ID,
172        #[index]
173        name: String,
174    }
175
176    let mut db = t.setup_db(models!(User)).await;
177    User::create().name("Alice").exec(&mut db).await?;
178
179    t.log().clear();
180    let (created, existing): (User, Vec<User>) =
181        toasty::batch((User::create().name("Bob"), User::filter_by_name("Alice")))
182            .exec(&mut db)
183            .await?;
184
185    assert_eq!(created.name, "Bob");
186    assert_struct!(existing, [_ { name: "Alice" }]);
187
188    // Two operations (create + query) → transaction-wrapped
189    assert_struct!(
190        t.log().pop_op(),
191        Operation::Transaction(Transaction::Start {
192            isolation: None,
193            read_only: false
194        })
195    );
196    assert_struct!(t.log().pop_op(), Operation::QuerySql(_ {
197        stmt: Statement::Insert(_),
198        ..
199    })); // INSERT
200    assert_struct!(t.log().pop_op(), Operation::QuerySql(_ {
201        stmt: Statement::Query(_),
202        ..
203    })); // SELECT
204    assert!(t.log().pop_op().is_transaction_commit());
205    assert!(t.log().is_empty());
206
207    Ok(())
208}
209
210/// Three-element batch: create, query, create.
211#[driver_test(id(ID), requires(sql))]
212pub async fn batch_create_query_create(t: &mut Test) -> Result<()> {
213    #[derive(Debug, toasty::Model)]
214    struct User {
215        #[key]
216        #[auto]
217        id: ID,
218        #[index]
219        name: String,
220    }
221
222    let mut db = t.setup_db(models!(User)).await;
223    User::create().name("Alice").exec(&mut db).await?;
224
225    t.log().clear();
226    let (bob, existing, carol): (User, Vec<User>, User) = toasty::batch((
227        User::create().name("Bob"),
228        User::filter_by_name("Alice"),
229        User::create().name("Carol"),
230    ))
231    .exec(&mut db)
232    .await?;
233
234    assert_eq!(bob.name, "Bob");
235    assert_struct!(existing, [_ { name: "Alice" }]);
236    assert_eq!(carol.name, "Carol");
237
238    // Three operations → transaction-wrapped
239    assert_struct!(
240        t.log().pop_op(),
241        Operation::Transaction(Transaction::Start {
242            isolation: None,
243            read_only: false
244        })
245    );
246    assert_struct!(t.log().pop_op(), Operation::QuerySql(_ {
247        stmt: Statement::Insert(_),
248        ..
249    })); // INSERT bob
250    assert_struct!(t.log().pop_op(), Operation::QuerySql(_ {
251        stmt: Statement::Query(_),
252        ..
253    })); // SELECT alice
254    assert_struct!(t.log().pop_op(), Operation::QuerySql(_ {
255        stmt: Statement::Insert(_),
256        ..
257    })); // INSERT carol
258    assert!(t.log().pop_op().is_transaction_commit());
259    assert!(t.log().is_empty());
260
261    Ok(())
262}
263
264/// Batch creates via an array of create builders.
265#[driver_test(id(ID), requires(sql))]
266pub async fn batch_creates_from_array(t: &mut Test) -> Result<()> {
267    #[derive(Debug, toasty::Model)]
268    struct User {
269        #[key]
270        #[auto]
271        id: ID,
272        name: String,
273    }
274
275    let mut db = t.setup_db(models!(User)).await;
276
277    t.log().clear();
278    let users = toasty::batch([
279        User::create().name("Alice"),
280        User::create().name("Bob"),
281        User::create().name("Carol"),
282    ])
283    .exec(&mut db)
284    .await?;
285
286    assert_struct!(users, [{ name: "Alice" }, { name: "Bob" }, { name: "Carol" }]);
287
288    // Three independent creates → transaction-wrapped
289    assert_struct!(
290        t.log().pop_op(),
291        Operation::Transaction(Transaction::Start {
292            isolation: None,
293            read_only: false
294        })
295    );
296    for _ in 0..3 {
297        assert_struct!(t.log().pop_op(), Operation::QuerySql(_ {
298            stmt: Statement::Insert(_),
299            ..
300        }));
301    }
302    assert!(t.log().pop_op().is_transaction_commit());
303    assert!(t.log().is_empty());
304
305    // Verify all were persisted
306    for user in &users {
307        let found = User::get_by_id(&mut db, user.id).await?;
308        assert_eq!(found.name, user.name);
309    }
310
311    Ok(())
312}
313
314/// Batch creates via a Vec of create builders.
315#[driver_test(id(ID), requires(sql))]
316pub async fn batch_creates_from_vec(t: &mut Test) -> Result<()> {
317    #[derive(Debug, toasty::Model)]
318    struct User {
319        #[key]
320        #[auto]
321        id: ID,
322        name: String,
323    }
324
325    let mut db = t.setup_db(models!(User)).await;
326
327    let names = ["Alice", "Bob", "Carol"];
328    let builders: Vec<_> = names.iter().map(|n| User::create().name(*n)).collect();
329
330    t.log().clear();
331    let users = toasty::batch(builders).exec(&mut db).await?;
332
333    assert_struct!(users, [{ name: "Alice" }, { name: "Bob" }, { name: "Carol" }]);
334
335    // Three independent creates → transaction-wrapped
336    assert_struct!(
337        t.log().pop_op(),
338        Operation::Transaction(Transaction::Start {
339            isolation: None,
340            read_only: false
341        })
342    );
343    for _ in 0..3 {
344        assert_struct!(t.log().pop_op(), Operation::QuerySql(_ {
345            stmt: Statement::Insert(_),
346            ..
347        }));
348    }
349    assert!(t.log().pop_op().is_transaction_commit());
350    assert!(t.log().is_empty());
351
352    // Verify all were persisted
353    for user in &users {
354        let found = User::get_by_id(&mut db, user.id).await?;
355        assert_eq!(found.name, user.name);
356    }
357
358    Ok(())
359}