toasty_driver_integration_suite/tests/
batch_associations.rs1use crate::prelude::*;
5
6#[driver_test(id(ID), requires(sql), scenario(crate::scenarios::has_many_belongs_to))]
8pub async fn batch_two_scoped_creates_same_relation(t: &mut Test) -> Result<()> {
9 let mut db = setup(t).await;
10 let user = User::create().name("Alice").exec(&mut db).await?;
11
12 let (t1, t2): (Todo, Todo) = toasty::batch((
13 user.todos().create().title("first"),
14 user.todos().create().title("second"),
15 ))
16 .exec(&mut db)
17 .await?;
18
19 assert_eq!(t1.title, "first");
20 assert_eq!(t2.title, "second");
21 assert_eq!(t1.user_id, user.id);
22 assert_eq!(t2.user_id, user.id);
23
24 let all: Vec<Todo> = user.todos().exec(&mut db).await?;
25 assert_eq!(all.len(), 2);
26
27 Ok(())
28}
29
30#[driver_test(id(ID), requires(sql))]
32pub async fn batch_two_scoped_queries_same_relation(t: &mut Test) -> Result<()> {
33 #[derive(Debug, toasty::Model)]
34 struct User {
35 #[key]
36 #[auto]
37 id: ID,
38 #[has_many]
39 todos: toasty::HasMany<Todo>,
40 }
41
42 #[derive(Debug, toasty::Model)]
43 struct Todo {
44 #[key]
45 #[auto]
46 id: ID,
47 #[index]
48 user_id: ID,
49 #[belongs_to(key = user_id, references = id)]
50 user: toasty::BelongsTo<User>,
51 #[index]
52 title: String,
53 }
54
55 let mut db = t.setup_db(models!(User, Todo)).await;
56
57 let u1 = User::create().exec(&mut db).await?;
58 let u2 = User::create().exec(&mut db).await?;
59 u1.todos().create().title("u1 todo").exec(&mut db).await?;
60 u2.todos().create().title("u2 todo").exec(&mut db).await?;
61
62 let (u1_todos, u2_todos): (Vec<Todo>, Vec<Todo>) = toasty::batch((u1.todos(), u2.todos()))
63 .exec(&mut db)
64 .await?;
65
66 assert_struct!(u1_todos, [_ { title: "u1 todo" }]);
67 assert_struct!(u2_todos, [_ { title: "u2 todo" }]);
68
69 Ok(())
70}
71
72#[driver_test(id(ID), requires(sql), scenario(crate::scenarios::has_many_belongs_to))]
74pub async fn batch_scoped_update_and_delete_same_relation(t: &mut Test) -> Result<()> {
75 let mut db = setup(t).await;
76 let user = User::create().name("Alice").exec(&mut db).await?;
77 let todo_keep = user.todos().create().title("keep").exec(&mut db).await?;
78 let todo_drop = user.todos().create().title("drop").exec(&mut db).await?;
79
80 let ((), ()): ((), ()) = toasty::batch((
81 user.todos()
82 .filter_by_id(todo_keep.id)
83 .update()
84 .title("kept"),
85 user.todos().filter_by_id(todo_drop.id).delete(),
86 ))
87 .exec(&mut db)
88 .await?;
89
90 let remaining: Vec<Todo> = user.todos().exec(&mut db).await?;
91 assert_eq!(remaining.len(), 1);
92 assert_eq!(remaining[0].title, "kept");
93
94 Ok(())
95}
96
97#[driver_test(id(ID), requires(sql), scenario(crate::scenarios::has_many_belongs_to))]
99pub async fn batch_scoped_all_four_crud(t: &mut Test) -> Result<()> {
100 let mut db = setup(t).await;
101 let user = User::create().name("Alice").exec(&mut db).await?;
102 let existing = user
103 .todos()
104 .create()
105 .title("existing")
106 .exec(&mut db)
107 .await?;
108 let doomed = user.todos().create().title("doomed").exec(&mut db).await?;
109
110 let (queried, created, (), ()): (Vec<Todo>, Todo, (), ()) = toasty::batch((
111 user.todos(),
112 user.todos().create().title("new"),
113 user.todos()
114 .filter_by_id(existing.id)
115 .update()
116 .title("updated"),
117 user.todos().filter_by_id(doomed.id).delete(),
118 ))
119 .exec(&mut db)
120 .await?;
121
122 assert_eq!(queried.len(), 2);
124 assert_eq!(created.title, "new");
125
126 let final_todos: Vec<Todo> = user.todos().exec(&mut db).await?;
128 assert_eq!(final_todos.len(), 2); let titles: Vec<&str> = final_todos.iter().map(|t| t.title.as_str()).collect();
131 assert!(titles.contains(&"updated"));
132 assert!(titles.contains(&"new"));
133
134 Ok(())
135}
136
137#[driver_test(id(ID), requires(sql))]
139pub async fn batch_scoped_with_root_statements(t: &mut Test) -> Result<()> {
140 #[derive(Debug, toasty::Model)]
141 struct User {
142 #[key]
143 #[auto]
144 id: ID,
145 #[index]
146 name: String,
147 #[has_many]
148 todos: toasty::HasMany<Todo>,
149 }
150
151 #[derive(Debug, toasty::Model)]
152 struct Todo {
153 #[key]
154 #[auto]
155 id: ID,
156 #[index]
157 user_id: ID,
158 #[belongs_to(key = user_id, references = id)]
159 user: toasty::BelongsTo<User>,
160 title: String,
161 }
162
163 let mut db = t.setup_db(models!(User, Todo)).await;
164 let user = User::create().name("Alice").exec(&mut db).await?;
165
166 let (users, todo, new_user): (Vec<User>, Todo, User) = toasty::batch((
168 User::filter_by_name("Alice"),
169 user.todos().create().title("from batch"),
170 User::create().name("Bob"),
171 ))
172 .exec(&mut db)
173 .await?;
174
175 assert_struct!(users, [_ { name: "Alice" }]);
176 assert_eq!(todo.title, "from batch");
177 assert_eq!(todo.user_id, user.id);
178 assert_eq!(new_user.name, "Bob");
179
180 Ok(())
181}
182
183#[driver_test(id(ID), requires(sql))]
185pub async fn batch_scoped_across_relations(t: &mut Test) -> Result<()> {
186 #[derive(Debug, toasty::Model)]
187 struct User {
188 #[key]
189 #[auto]
190 id: ID,
191 #[has_many]
192 todos: toasty::HasMany<Todo>,
193 #[has_many]
194 posts: toasty::HasMany<Post>,
195 }
196
197 #[derive(Debug, toasty::Model)]
198 struct Todo {
199 #[key]
200 #[auto]
201 id: ID,
202 #[index]
203 user_id: ID,
204 #[belongs_to(key = user_id, references = id)]
205 user: toasty::BelongsTo<User>,
206 title: String,
207 }
208
209 #[derive(Debug, toasty::Model)]
210 struct Post {
211 #[key]
212 #[auto]
213 id: ID,
214 #[index]
215 user_id: ID,
216 #[belongs_to(key = user_id, references = id)]
217 user: toasty::BelongsTo<User>,
218 body: String,
219 }
220
221 let mut db = t.setup_db(models!(User, Todo, Post)).await;
222 let user = User::create().exec(&mut db).await?;
223
224 let (todo, post): (Todo, Post) = toasty::batch((
226 user.todos().create().title("my todo"),
227 user.posts().create().body("my post"),
228 ))
229 .exec(&mut db)
230 .await?;
231
232 assert_eq!(todo.title, "my todo");
233 assert_eq!(todo.user_id, user.id);
234 assert_eq!(post.body, "my post");
235 assert_eq!(post.user_id, user.id);
236
237 Ok(())
238}
239
240#[driver_test(id(ID), requires(sql))]
242pub async fn batch_query_across_relations(t: &mut Test) -> Result<()> {
243 #[derive(Debug, toasty::Model)]
244 struct User {
245 #[key]
246 #[auto]
247 id: ID,
248 #[has_many]
249 todos: toasty::HasMany<Todo>,
250 #[has_many]
251 posts: toasty::HasMany<Post>,
252 }
253
254 #[derive(Debug, toasty::Model)]
255 struct Todo {
256 #[key]
257 #[auto]
258 id: ID,
259 #[index]
260 user_id: ID,
261 #[belongs_to(key = user_id, references = id)]
262 user: toasty::BelongsTo<User>,
263 title: String,
264 }
265
266 #[derive(Debug, toasty::Model)]
267 struct Post {
268 #[key]
269 #[auto]
270 id: ID,
271 #[index]
272 user_id: ID,
273 #[belongs_to(key = user_id, references = id)]
274 user: toasty::BelongsTo<User>,
275 body: String,
276 }
277
278 let mut db = t.setup_db(models!(User, Todo, Post)).await;
279 let user = User::create().exec(&mut db).await?;
280 user.todos().create().title("t1").exec(&mut db).await?;
281 user.todos().create().title("t2").exec(&mut db).await?;
282 user.posts().create().body("p1").exec(&mut db).await?;
283
284 let (todos, posts): (Vec<Todo>, Vec<Post>) = toasty::batch((user.todos(), user.posts()))
285 .exec(&mut db)
286 .await?;
287
288 assert_eq!(todos.len(), 2);
289 assert_eq!(posts.len(), 1);
290 assert_eq!(posts[0].body, "p1");
291
292 Ok(())
293}
294
295#[driver_test(id(ID), requires(sql), scenario(crate::scenarios::has_many_belongs_to))]
297pub async fn batch_scoped_different_parents(t: &mut Test) -> Result<()> {
298 let mut db = setup(t).await;
299 let alice = User::create().name("Alice").exec(&mut db).await?;
300 let bob = User::create().name("Bob").exec(&mut db).await?;
301
302 let (alice_todo, bob_todo): (Todo, Todo) = toasty::batch((
304 alice.todos().create().title("alice task"),
305 bob.todos().create().title("bob task"),
306 ))
307 .exec(&mut db)
308 .await?;
309
310 assert_eq!(alice_todo.user_id, alice.id);
311 assert_eq!(bob_todo.user_id, bob.id);
312
313 let (alice_todos, bob_todos): (Vec<Todo>, Vec<Todo>) =
315 toasty::batch((alice.todos(), bob.todos()))
316 .exec(&mut db)
317 .await?;
318
319 assert_struct!(alice_todos, [_ { title: "alice task" }]);
320 assert_struct!(bob_todos, [_ { title: "bob task" }]);
321
322 Ok(())
323}
324
325#[driver_test(id(ID), requires(sql))]
327pub async fn batch_scoped_delete_with_root_update(t: &mut Test) -> Result<()> {
328 #[derive(Debug, toasty::Model)]
329 struct User {
330 #[key]
331 #[auto]
332 id: ID,
333 #[index]
334 name: String,
335 #[has_many]
336 todos: toasty::HasMany<Todo>,
337 }
338
339 #[derive(Debug, toasty::Model)]
340 struct Todo {
341 #[key]
342 #[auto]
343 id: ID,
344 #[index]
345 user_id: ID,
346 #[belongs_to(key = user_id, references = id)]
347 user: toasty::BelongsTo<User>,
348 title: String,
349 }
350
351 let mut db = t.setup_db(models!(User, Todo)).await;
352 let user = User::create().name("Alice").exec(&mut db).await?;
353 let todo = user.todos().create().title("done").exec(&mut db).await?;
354
355 let ((), ()): ((), ()) = toasty::batch((
356 user.todos().filter_by_id(todo.id).delete(),
357 User::filter_by_name("Alice").update().name("Alice2"),
358 ))
359 .exec(&mut db)
360 .await?;
361
362 let remaining: Vec<Todo> = user.todos().exec(&mut db).await?;
364 assert!(remaining.is_empty());
365
366 let updated: Vec<User> = User::filter_by_name("Alice2").exec(&mut db).await?;
368 assert_eq!(updated.len(), 1);
369
370 Ok(())
371}