toasty_driver_integration_suite/tests/
tx_atomic_stmt.rs1use crate::prelude::*;
2
3use toasty_core::driver::{Operation, operation::Transaction};
4
5#[driver_test(
10 requires(sql),
11 scenario(crate::scenarios::has_many_belongs_to::id_uuid)
12)]
13pub async fn multi_op_create_wraps_in_transaction(t: &mut Test) -> Result<()> {
14 let mut db = setup(t).await;
15
16 t.log().clear();
17 let user = User::create()
18 .name("Alice")
19 .todos([Todo::create().title("task")])
20 .exec(&mut db)
21 .await?;
22
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::Insert(_)); assert_struct!(t.log().pop_op(), Operation::Insert(_)); assert_struct!(
34 t.log().pop_op(),
35 Operation::Transaction(Transaction::Commit)
36 );
37 assert!(t.log().is_empty());
38
39 let todos = user.todos().exec(&mut db).await?;
40 assert_eq!(1, todos.len());
41
42 Ok(())
43}
44
45#[driver_test(requires(scan), scenario(crate::scenarios::two_models))]
48pub async fn single_op_skips_transaction(t: &mut Test) -> Result<()> {
49 let mut db = setup(t).await;
50
51 t.log().clear();
52 User::create().name("x").exec(&mut db).await?;
53
54 assert_struct!(t.log().pop_op(), Operation::Insert(_));
56 assert!(t.log().is_empty());
57
58 Ok(())
59}
60
61#[driver_test(requires(and(sql, auto_increment)))]
73pub async fn create_with_has_many_rolls_back_on_failure(t: &mut Test) -> Result<()> {
74 #[derive(Debug, toasty::Model)]
75 struct User {
76 #[key]
77 #[auto]
78 id: u64,
79
80 #[has_many]
81 todos: toasty::Deferred<Vec<Todo>>,
82 }
83
84 #[derive(Debug, toasty::Model)]
85 struct Todo {
86 #[key]
87 #[auto]
88 id: u64,
89
90 #[index]
91 user_id: u64,
92
93 #[belongs_to(key = user_id, references = id)]
94 user: toasty::Deferred<User>,
95
96 #[unique]
97 title: String,
98 }
99
100 let mut db = t.setup_db(models!(User, Todo)).await;
101
102 User::create()
104 .todos([Todo::create().title("taken")])
105 .exec(&mut db)
106 .await?;
107
108 t.log().clear();
109 assert_err!(
110 User::create()
111 .todos([Todo::create().title("taken")])
112 .exec(&mut db)
113 .await
114 );
115
116 assert_struct!(
119 t.log().pop_op(),
120 Operation::Transaction(Transaction::Start {
121 isolation: None,
122 read_only: false,
123 ..
124 })
125 );
126 assert_struct!(t.log().pop_op(), Operation::Insert(_)); assert_struct!(
128 t.log().pop_op(),
129 Operation::Transaction(Transaction::Rollback)
130 );
131 assert!(t.log().is_empty());
132
133 let users = User::all().exec(&mut db).await?;
135 assert_eq!(1, users.len());
136
137 Ok(())
138}
139
140#[driver_test(requires(and(sql, auto_increment)))]
148pub async fn create_with_has_one_rolls_back_on_failure(t: &mut Test) -> Result<()> {
149 #[derive(Debug, toasty::Model)]
150 struct User {
151 #[key]
152 #[auto]
153 id: u64,
154
155 #[has_one]
156 profile: toasty::Deferred<Option<Profile>>,
157 }
158
159 #[derive(Debug, toasty::Model)]
160 struct Profile {
161 #[key]
162 #[auto]
163 id: u64,
164
165 #[unique]
166 bio: String,
167
168 #[unique]
169 user_id: u64,
170
171 #[belongs_to(key = user_id, references = id)]
172 user: toasty::Deferred<User>,
173 }
174
175 let mut db = t.setup_db(models!(User, Profile)).await;
176
177 User::create()
179 .profile(Profile::create().bio("taken"))
180 .exec(&mut db)
181 .await?;
182
183 t.log().clear();
184 assert_err!(
185 User::create()
186 .profile(Profile::create().bio("taken"))
187 .exec(&mut db)
188 .await
189 );
190
191 assert_struct!(
192 t.log().pop_op(),
193 Operation::Transaction(Transaction::Start {
194 isolation: None,
195 read_only: false,
196 ..
197 })
198 );
199 assert_struct!(t.log().pop_op(), Operation::Insert(_)); assert_struct!(
201 t.log().pop_op(),
202 Operation::Transaction(Transaction::Rollback)
203 );
204 assert!(t.log().is_empty());
205
206 let users = User::all().exec(&mut db).await?;
208 assert_eq!(1, users.len());
209
210 Ok(())
211}
212
213#[driver_test(requires(sql))]
221pub async fn update_with_new_association_rolls_back_on_failure(t: &mut Test) -> Result<()> {
222 #[derive(Debug, toasty::Model)]
223 struct User {
224 #[key]
225 #[auto]
226 id: uuid::Uuid,
227
228 #[unique]
229 name: String,
230
231 #[has_many]
232 todos: toasty::Deferred<Vec<Todo>>,
233 }
234
235 #[derive(Debug, toasty::Model)]
236 struct Todo {
237 #[key]
238 #[auto]
239 id: uuid::Uuid,
240
241 #[index]
242 user_id: uuid::Uuid,
243
244 #[belongs_to(key = user_id, references = id)]
245 user: toasty::Deferred<User>,
246
247 title: String,
248 }
249
250 let mut db = t.setup_db(models!(User, Todo)).await;
251
252 let mut user = User::create().name("original").exec(&mut db).await?;
253 User::create().name("taken").exec(&mut db).await?;
255
256 t.log().clear();
257 assert_err!(
258 user.update()
259 .name("taken") .todos(toasty::stmt::insert(Todo::create().title("new-todo"))) .exec(&mut db)
262 .await
263 );
264
265 assert_struct!(
268 t.log().pop_op(),
269 Operation::Transaction(Transaction::Start {
270 isolation: None,
271 read_only: false,
272 ..
273 })
274 );
275 assert_struct!(t.log().pop_op(), Operation::Insert(_)); assert_struct!(
277 t.log().pop_op(),
278 Operation::Transaction(Transaction::Rollback)
279 );
280 assert!(t.log().is_empty());
281
282 let todos = user.todos().exec(&mut db).await?;
284 assert!(todos.is_empty());
285
286 Ok(())
287}
288
289#[driver_test(requires(sql), scenario(crate::scenarios::has_many_nullable_fk))]
296pub async fn rmw_uses_savepoints(t: &mut Test) -> Result<()> {
297 let mut db = setup(t).await;
298
299 let user = User::create()
300 .todos([Todo::create().title("task")])
301 .exec(&mut db)
302 .await?;
303 let todos: Vec<_> = user.todos().exec(&mut db).await?;
304
305 t.log().clear();
306 user.todos().remove(&todos[0]).exec(&mut db).await?;
307
308 if t.capability().cte_with_update {
309 assert_struct!(t.log().pop_op(), Operation::QuerySql(_));
311 } else {
312 assert_struct!(
314 t.log().pop_op(),
315 Operation::Transaction(Transaction::Start {
316 isolation: None,
317 read_only: false,
318 ..
319 })
320 );
321 assert_struct!(t.log().pop_op(), Operation::QuerySql(_)); assert_struct!(t.log().pop_op(), Operation::QuerySql(_)); assert_struct!(
324 t.log().pop_op(),
325 Operation::Transaction(Transaction::Commit)
326 );
327 }
328 assert!(t.log().is_empty());
329
330 Ok(())
331}
332
333#[driver_test(requires(sql), scenario(crate::scenarios::has_many_nullable_fk))]
337pub async fn rmw_condition_failure_issues_rollback_to_savepoint(t: &mut Test) -> Result<()> {
338 let mut db = setup(t).await;
339
340 let user1 = User::create().exec(&mut db).await?;
341 let user2 = User::create()
342 .todos([Todo::create().title("task")])
343 .exec(&mut db)
344 .await?;
345 let u2_todos: Vec<_> = user2.todos().exec(&mut db).await?;
346
347 t.log().clear();
348 assert_err!(user1.todos().remove(&u2_todos[0]).exec(&mut db).await);
350
351 if t.capability().cte_with_update {
352 assert_struct!(t.log().pop_op(), Operation::QuerySql(_));
354 } else {
355 assert_struct!(
358 t.log().pop_op(),
359 Operation::Transaction(Transaction::Start {
360 isolation: None,
361 read_only: false,
362 ..
363 })
364 );
365 assert_struct!(t.log().pop_op(), Operation::QuerySql(_)); assert_struct!(
367 t.log().pop_op(),
368 Operation::Transaction(Transaction::Rollback)
369 );
370 }
371 assert!(t.log().is_empty());
372
373 let reloaded = Todo::get_by_id(&mut db, u2_todos[0].id).await?;
375 assert_struct!(reloaded, { user_id: Some(== user2.id) });
376
377 Ok(())
378}