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 .todo(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 assert_struct!(t.log().pop_op(), Operation::QuerySql(_)); assert_struct!(t.log().pop_op(), Operation::QuerySql(_)); assert_struct!(
30 t.log().pop_op(),
31 Operation::Transaction(Transaction::Commit)
32 );
33 assert!(t.log().is_empty());
34
35 let todos = user.todos().exec(&mut db).await?;
36 assert_eq!(1, todos.len());
37
38 Ok(())
39}
40
41#[driver_test(id(ID), requires(sql), scenario(crate::scenarios::two_models))]
44pub async fn single_op_skips_transaction(t: &mut Test) -> Result<()> {
45 let mut db = setup(t).await;
46
47 t.log().clear();
48 User::create().name("x").exec(&mut db).await?;
49
50 assert_struct!(t.log().pop_op(), Operation::QuerySql(_));
52 assert!(t.log().is_empty());
53
54 Ok(())
55}
56
57#[driver_test(requires(sql))]
69pub async fn create_with_has_many_rolls_back_on_failure(t: &mut Test) -> Result<()> {
70 #[derive(Debug, toasty::Model)]
71 struct User {
72 #[key]
73 #[auto]
74 id: u64,
75
76 #[has_many]
77 todos: toasty::HasMany<Todo>,
78 }
79
80 #[derive(Debug, toasty::Model)]
81 struct Todo {
82 #[key]
83 #[auto]
84 id: u64,
85
86 #[index]
87 user_id: u64,
88
89 #[belongs_to(key = user_id, references = id)]
90 user: toasty::BelongsTo<User>,
91
92 #[unique]
93 title: String,
94 }
95
96 let mut db = t.setup_db(models!(User, Todo)).await;
97
98 User::create()
100 .todo(Todo::create().title("taken"))
101 .exec(&mut db)
102 .await?;
103
104 t.log().clear();
105 assert_err!(
106 User::create()
107 .todo(Todo::create().title("taken"))
108 .exec(&mut db)
109 .await
110 );
111
112 assert_struct!(
115 t.log().pop_op(),
116 Operation::Transaction(Transaction::Start {
117 isolation: None,
118 read_only: false
119 })
120 );
121 assert_struct!(t.log().pop_op(), Operation::QuerySql(_)); assert_struct!(
123 t.log().pop_op(),
124 Operation::Transaction(Transaction::Rollback)
125 );
126 assert!(t.log().is_empty());
127
128 let users = User::all().exec(&mut db).await?;
130 assert_eq!(1, users.len());
131
132 Ok(())
133}
134
135#[driver_test(requires(sql))]
143pub async fn create_with_has_one_rolls_back_on_failure(t: &mut Test) -> Result<()> {
144 #[derive(Debug, toasty::Model)]
145 struct User {
146 #[key]
147 #[auto]
148 id: u64,
149
150 #[has_one]
151 profile: toasty::HasOne<Option<Profile>>,
152 }
153
154 #[derive(Debug, toasty::Model)]
155 struct Profile {
156 #[key]
157 #[auto]
158 id: u64,
159
160 #[unique]
161 bio: String,
162
163 #[unique]
164 user_id: u64,
165
166 #[belongs_to(key = user_id, references = id)]
167 user: toasty::BelongsTo<User>,
168 }
169
170 let mut db = t.setup_db(models!(User, Profile)).await;
171
172 User::create()
174 .profile(Profile::create().bio("taken"))
175 .exec(&mut db)
176 .await?;
177
178 t.log().clear();
179 assert_err!(
180 User::create()
181 .profile(Profile::create().bio("taken"))
182 .exec(&mut db)
183 .await
184 );
185
186 assert_struct!(
187 t.log().pop_op(),
188 Operation::Transaction(Transaction::Start {
189 isolation: None,
190 read_only: false
191 })
192 );
193 assert_struct!(t.log().pop_op(), Operation::QuerySql(_)); assert_struct!(
195 t.log().pop_op(),
196 Operation::Transaction(Transaction::Rollback)
197 );
198 assert!(t.log().is_empty());
199
200 let users = User::all().exec(&mut db).await?;
202 assert_eq!(1, users.len());
203
204 Ok(())
205}
206
207#[driver_test(id(ID), requires(sql))]
215pub async fn update_with_new_association_rolls_back_on_failure(t: &mut Test) -> Result<()> {
216 #[derive(Debug, toasty::Model)]
217 struct User {
218 #[key]
219 #[auto]
220 id: ID,
221
222 #[unique]
223 name: String,
224
225 #[has_many]
226 todos: toasty::HasMany<Todo>,
227 }
228
229 #[derive(Debug, toasty::Model)]
230 struct Todo {
231 #[key]
232 #[auto]
233 id: ID,
234
235 #[index]
236 user_id: ID,
237
238 #[belongs_to(key = user_id, references = id)]
239 user: toasty::BelongsTo<User>,
240
241 title: String,
242 }
243
244 let mut db = t.setup_db(models!(User, Todo)).await;
245
246 let mut user = User::create().name("original").exec(&mut db).await?;
247 User::create().name("taken").exec(&mut db).await?;
249
250 t.log().clear();
251 assert_err!(
252 user.update()
253 .name("taken") .todos(toasty::stmt::insert(Todo::create().title("new-todo"))) .exec(&mut db)
256 .await
257 );
258
259 assert_struct!(
262 t.log().pop_op(),
263 Operation::Transaction(Transaction::Start {
264 isolation: None,
265 read_only: false
266 })
267 );
268 assert_struct!(t.log().pop_op(), Operation::QuerySql(_)); assert_struct!(
270 t.log().pop_op(),
271 Operation::Transaction(Transaction::Rollback)
272 );
273 assert!(t.log().is_empty());
274
275 let todos = user.todos().exec(&mut db).await?;
277 assert!(todos.is_empty());
278
279 Ok(())
280}
281
282#[driver_test(id(ID), requires(sql))]
289pub async fn rmw_uses_savepoints(t: &mut Test) -> Result<()> {
290 #[derive(Debug, toasty::Model)]
291 struct User {
292 #[key]
293 #[auto]
294 id: ID,
295
296 #[has_many]
297 todos: toasty::HasMany<Todo>,
298 }
299
300 #[derive(Debug, toasty::Model)]
301 struct Todo {
302 #[key]
303 #[auto]
304 id: ID,
305
306 #[index]
307 user_id: Option<ID>,
308
309 #[belongs_to(key = user_id, references = id)]
310 user: toasty::BelongsTo<Option<User>>,
311 }
312
313 let mut db = t.setup_db(models!(User, Todo)).await;
314
315 let user = User::create().todo(Todo::create()).exec(&mut db).await?;
316 let todos: Vec<_> = user.todos().exec(&mut db).await?;
317
318 t.log().clear();
319 user.todos().remove(&mut db, &todos[0]).await?;
320
321 if t.capability().cte_with_update {
322 assert_struct!(t.log().pop_op(), Operation::QuerySql(_));
324 } else {
325 assert_struct!(
327 t.log().pop_op(),
328 Operation::Transaction(Transaction::Start {
329 isolation: None,
330 read_only: false
331 })
332 );
333 assert_struct!(t.log().pop_op(), Operation::QuerySql(_)); assert_struct!(t.log().pop_op(), Operation::QuerySql(_)); assert_struct!(
336 t.log().pop_op(),
337 Operation::Transaction(Transaction::Commit)
338 );
339 }
340 assert!(t.log().is_empty());
341
342 Ok(())
343}
344
345#[driver_test(id(ID), requires(sql))]
349pub async fn rmw_condition_failure_issues_rollback_to_savepoint(t: &mut Test) -> Result<()> {
350 #[derive(Debug, toasty::Model)]
351 struct User {
352 #[key]
353 #[auto]
354 id: ID,
355
356 #[has_many]
357 todos: toasty::HasMany<Todo>,
358 }
359
360 #[derive(Debug, toasty::Model)]
361 struct Todo {
362 #[key]
363 #[auto]
364 id: ID,
365
366 #[index]
367 user_id: Option<ID>,
368
369 #[belongs_to(key = user_id, references = id)]
370 user: toasty::BelongsTo<Option<User>>,
371 }
372
373 let mut db = t.setup_db(models!(User, Todo)).await;
374
375 let user1 = User::create().exec(&mut db).await?;
376 let user2 = User::create().todo(Todo::create()).exec(&mut db).await?;
377 let u2_todos: Vec<_> = user2.todos().exec(&mut db).await?;
378
379 t.log().clear();
380 assert_err!(user1.todos().remove(&mut db, &u2_todos[0]).await);
382
383 if t.capability().cte_with_update {
384 assert_struct!(t.log().pop_op(), Operation::QuerySql(_));
386 } else {
387 assert_struct!(
390 t.log().pop_op(),
391 Operation::Transaction(Transaction::Start {
392 isolation: None,
393 read_only: false
394 })
395 );
396 assert_struct!(t.log().pop_op(), Operation::QuerySql(_)); assert_struct!(
398 t.log().pop_op(),
399 Operation::Transaction(Transaction::Rollback)
400 );
401 }
402 assert!(t.log().is_empty());
403
404 let reloaded = Todo::get_by_id(&mut db, u2_todos[0].id).await?;
406 assert_struct!(reloaded, { user_id: Some(== user2.id) });
407
408 Ok(())
409}