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