toasty_driver_integration_suite/tests/
tx_atomic_stmt.rs1use crate::prelude::*;
2
3use toasty_core::driver::{Operation, operation::Transaction};
4
5#[driver_test(id(ID), requires(sql), scenario(crate::scenarios::has_many_belongs_to))]
10pub async fn multi_op_create_wraps_in_transaction(t: &mut Test) -> Result<()> {
11 let mut db = setup(t).await;
12
13 t.log().clear();
14 let user = User::create()
15 .name("Alice")
16 .todos([Todo::create().title("task")])
17 .exec(&mut db)
18 .await?;
19
20 assert_struct!(
21 t.log().pop_op(),
22 Operation::Transaction(Transaction::Start {
23 isolation: None,
24 read_only: false,
25 ..
26 })
27 );
28 assert_struct!(t.log().pop_op(), Operation::QuerySql(_)); assert_struct!(t.log().pop_op(), Operation::QuerySql(_)); assert_struct!(
31 t.log().pop_op(),
32 Operation::Transaction(Transaction::Commit)
33 );
34 assert!(t.log().is_empty());
35
36 let todos = user.todos().exec(&mut db).await?;
37 assert_eq!(1, todos.len());
38
39 Ok(())
40}
41
42#[driver_test(id(ID), requires(scan), scenario(crate::scenarios::two_models))]
45pub async fn single_op_skips_transaction(t: &mut Test) -> Result<()> {
46 let mut db = setup(t).await;
47
48 t.log().clear();
49 User::create().name("x").exec(&mut db).await?;
50
51 assert_struct!(t.log().pop_op(), Operation::QuerySql(_));
53 assert!(t.log().is_empty());
54
55 Ok(())
56}
57
58#[driver_test(requires(and(sql, auto_increment)))]
70pub async fn create_with_has_many_rolls_back_on_failure(t: &mut Test) -> Result<()> {
71 #[derive(Debug, toasty::Model)]
72 struct User {
73 #[key]
74 #[auto]
75 id: u64,
76
77 #[has_many]
78 todos: toasty::Deferred<Vec<Todo>>,
79 }
80
81 #[derive(Debug, toasty::Model)]
82 struct Todo {
83 #[key]
84 #[auto]
85 id: u64,
86
87 #[index]
88 user_id: u64,
89
90 #[belongs_to(key = user_id, references = id)]
91 user: toasty::Deferred<User>,
92
93 #[unique]
94 title: String,
95 }
96
97 let mut db = t.setup_db(models!(User, Todo)).await;
98
99 User::create()
101 .todos([Todo::create().title("taken")])
102 .exec(&mut db)
103 .await?;
104
105 t.log().clear();
106 assert_err!(
107 User::create()
108 .todos([Todo::create().title("taken")])
109 .exec(&mut db)
110 .await
111 );
112
113 assert_struct!(
116 t.log().pop_op(),
117 Operation::Transaction(Transaction::Start {
118 isolation: None,
119 read_only: false,
120 ..
121 })
122 );
123 assert_struct!(t.log().pop_op(), Operation::QuerySql(_)); assert_struct!(
125 t.log().pop_op(),
126 Operation::Transaction(Transaction::Rollback)
127 );
128 assert!(t.log().is_empty());
129
130 let users = User::all().exec(&mut db).await?;
132 assert_eq!(1, users.len());
133
134 Ok(())
135}
136
137#[driver_test(requires(and(sql, auto_increment)))]
145pub async fn create_with_has_one_rolls_back_on_failure(t: &mut Test) -> Result<()> {
146 #[derive(Debug, toasty::Model)]
147 struct User {
148 #[key]
149 #[auto]
150 id: u64,
151
152 #[has_one]
153 profile: toasty::Deferred<Option<Profile>>,
154 }
155
156 #[derive(Debug, toasty::Model)]
157 struct Profile {
158 #[key]
159 #[auto]
160 id: u64,
161
162 #[unique]
163 bio: String,
164
165 #[unique]
166 user_id: u64,
167
168 #[belongs_to(key = user_id, references = id)]
169 user: toasty::Deferred<User>,
170 }
171
172 let mut db = t.setup_db(models!(User, Profile)).await;
173
174 User::create()
176 .profile(Profile::create().bio("taken"))
177 .exec(&mut db)
178 .await?;
179
180 t.log().clear();
181 assert_err!(
182 User::create()
183 .profile(Profile::create().bio("taken"))
184 .exec(&mut db)
185 .await
186 );
187
188 assert_struct!(
189 t.log().pop_op(),
190 Operation::Transaction(Transaction::Start {
191 isolation: None,
192 read_only: false,
193 ..
194 })
195 );
196 assert_struct!(t.log().pop_op(), Operation::QuerySql(_)); assert_struct!(
198 t.log().pop_op(),
199 Operation::Transaction(Transaction::Rollback)
200 );
201 assert!(t.log().is_empty());
202
203 let users = User::all().exec(&mut db).await?;
205 assert_eq!(1, users.len());
206
207 Ok(())
208}
209
210#[driver_test(id(ID), requires(sql))]
218pub async fn update_with_new_association_rolls_back_on_failure(t: &mut Test) -> Result<()> {
219 #[derive(Debug, toasty::Model)]
220 struct User {
221 #[key]
222 #[auto]
223 id: ID,
224
225 #[unique]
226 name: String,
227
228 #[has_many]
229 todos: toasty::Deferred<Vec<Todo>>,
230 }
231
232 #[derive(Debug, toasty::Model)]
233 struct Todo {
234 #[key]
235 #[auto]
236 id: ID,
237
238 #[index]
239 user_id: ID,
240
241 #[belongs_to(key = user_id, references = id)]
242 user: toasty::Deferred<User>,
243
244 title: String,
245 }
246
247 let mut db = t.setup_db(models!(User, Todo)).await;
248
249 let mut user = User::create().name("original").exec(&mut db).await?;
250 User::create().name("taken").exec(&mut db).await?;
252
253 t.log().clear();
254 assert_err!(
255 user.update()
256 .name("taken") .todos(toasty::stmt::insert(Todo::create().title("new-todo"))) .exec(&mut db)
259 .await
260 );
261
262 assert_struct!(
265 t.log().pop_op(),
266 Operation::Transaction(Transaction::Start {
267 isolation: None,
268 read_only: false,
269 ..
270 })
271 );
272 assert_struct!(t.log().pop_op(), Operation::QuerySql(_)); assert_struct!(
274 t.log().pop_op(),
275 Operation::Transaction(Transaction::Rollback)
276 );
277 assert!(t.log().is_empty());
278
279 let todos = user.todos().exec(&mut db).await?;
281 assert!(todos.is_empty());
282
283 Ok(())
284}
285
286#[driver_test(
293 id(ID),
294 requires(sql),
295 scenario(crate::scenarios::has_many_nullable_fk)
296)]
297pub async fn rmw_uses_savepoints(t: &mut Test) -> Result<()> {
298 let mut db = setup(t).await;
299
300 let user = User::create()
301 .todos([Todo::create().title("task")])
302 .exec(&mut db)
303 .await?;
304 let todos: Vec<_> = user.todos().exec(&mut db).await?;
305
306 t.log().clear();
307 user.todos().remove(&mut db, &todos[0]).await?;
308
309 if t.capability().cte_with_update {
310 assert_struct!(t.log().pop_op(), Operation::QuerySql(_));
312 } else {
313 assert_struct!(
315 t.log().pop_op(),
316 Operation::Transaction(Transaction::Start {
317 isolation: None,
318 read_only: false,
319 ..
320 })
321 );
322 assert_struct!(t.log().pop_op(), Operation::QuerySql(_)); assert_struct!(t.log().pop_op(), Operation::QuerySql(_)); assert_struct!(
325 t.log().pop_op(),
326 Operation::Transaction(Transaction::Commit)
327 );
328 }
329 assert!(t.log().is_empty());
330
331 Ok(())
332}
333
334#[driver_test(
338 id(ID),
339 requires(sql),
340 scenario(crate::scenarios::has_many_nullable_fk)
341)]
342pub async fn rmw_condition_failure_issues_rollback_to_savepoint(t: &mut Test) -> Result<()> {
343 let mut db = setup(t).await;
344
345 let user1 = User::create().exec(&mut db).await?;
346 let user2 = User::create()
347 .todos([Todo::create().title("task")])
348 .exec(&mut db)
349 .await?;
350 let u2_todos: Vec<_> = user2.todos().exec(&mut db).await?;
351
352 t.log().clear();
353 assert_err!(user1.todos().remove(&mut db, &u2_todos[0]).await);
355
356 if t.capability().cte_with_update {
357 assert_struct!(t.log().pop_op(), Operation::QuerySql(_));
359 } else {
360 assert_struct!(
363 t.log().pop_op(),
364 Operation::Transaction(Transaction::Start {
365 isolation: None,
366 read_only: false,
367 ..
368 })
369 );
370 assert_struct!(t.log().pop_op(), Operation::QuerySql(_)); assert_struct!(
372 t.log().pop_op(),
373 Operation::Transaction(Transaction::Rollback)
374 );
375 }
376 assert!(t.log().is_empty());
377
378 let reloaded = Todo::get_by_id(&mut db, u2_todos[0].id).await?;
380 assert_struct!(reloaded, { user_id: Some(== user2.id) });
381
382 Ok(())
383}