Skip to main content

toasty_driver_integration_suite/tests/
batch_create_statements.rs

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