1use crate::prelude::*;
2
3use toasty::schema::Model;
4use toasty_core::stmt;
5
6#[driver_test]
7pub async fn eager_has_many_and_has_one_load_without_include(t: &mut Test) -> Result<()> {
8 #[derive(Debug, toasty::Model)]
9 struct User {
10 #[key]
11 id: uuid::Uuid,
12 name: String,
13
14 #[has_many]
15 posts: Vec<Post>,
16
17 #[has_one]
18 profile: Option<Profile>,
19 }
20
21 #[derive(Debug, toasty::Model)]
22 struct Post {
23 #[key]
24 #[auto]
25 id: uuid::Uuid,
26 title: String,
27
28 #[index]
29 user_id: uuid::Uuid,
30
31 #[belongs_to(key = user_id, references = id)]
32 user: toasty::Deferred<User>,
33 }
34
35 #[derive(Debug, toasty::Model)]
36 struct Profile {
37 #[key]
38 id: uuid::Uuid,
39 bio: String,
40
41 #[unique]
42 user_id: uuid::Uuid,
43
44 #[belongs_to(key = user_id, references = id)]
45 user: toasty::Deferred<Option<User>>,
46 }
47
48 let mut db = t.setup_db(models!(User, Post, Profile)).await;
49 let user_id = uuid::Uuid::from_u128(1);
50
51 insert_row::<User>(
52 &mut db,
53 vec![
54 stmt::Value::Uuid(user_id).into(),
55 stmt::Value::from("Alice").into(),
56 stmt::Value::Null.into(),
57 stmt::Value::Null.into(),
58 ],
59 )
60 .await?;
61 insert_row::<Post>(
62 &mut db,
63 vec![
64 stmt::Value::Uuid(uuid::Uuid::from_u128(2)).into(),
65 stmt::Value::from("hello").into(),
66 stmt::Value::Uuid(user_id).into(),
67 stmt::Value::Null.into(),
68 ],
69 )
70 .await?;
71 insert_row::<Profile>(
72 &mut db,
73 vec![
74 stmt::Value::Uuid(uuid::Uuid::from_u128(3)).into(),
75 stmt::Value::from("writer").into(),
76 stmt::Value::Uuid(user_id).into(),
77 stmt::Value::Null.into(),
78 ],
79 )
80 .await?;
81
82 let user = User::filter_by_id(user_id).get(&mut db).await?;
83
84 assert_eq!(user.posts.len(), 1);
85 assert_eq!(user.posts[0].title, "hello");
86 assert_eq!(user.profile.as_ref().unwrap().bio, "writer");
87
88 Ok(())
89}
90
91#[driver_test]
92pub async fn eager_belongs_to_loads_without_include(t: &mut Test) -> Result<()> {
93 #[derive(Debug, toasty::Model)]
94 struct User {
95 #[key]
96 id: uuid::Uuid,
97 name: String,
98 }
99
100 #[derive(Debug, toasty::Model)]
101 struct Post {
102 #[key]
103 #[auto]
104 id: uuid::Uuid,
105 title: String,
106
107 #[index]
108 user_id: uuid::Uuid,
109
110 #[belongs_to(key = user_id, references = id)]
111 user: User,
112 }
113
114 let mut db = t.setup_db(models!(User, Post)).await;
115 let user_id = uuid::Uuid::from_u128(4);
116 let post_id = uuid::Uuid::from_u128(5);
117
118 insert_row::<User>(
119 &mut db,
120 vec![
121 stmt::Value::Uuid(user_id).into(),
122 stmt::Value::from("Alice").into(),
123 ],
124 )
125 .await?;
126 insert_row::<Post>(
127 &mut db,
128 vec![
129 stmt::Value::Uuid(post_id).into(),
130 stmt::Value::from("hello").into(),
131 stmt::Value::Uuid(user_id).into(),
132 stmt::Value::Null.into(),
133 ],
134 )
135 .await?;
136
137 let post = Post::filter_by_id(post_id).get(&mut db).await?;
138
139 assert_eq!(post.title, "hello");
140 assert_eq!(post.user.name, "Alice");
141
142 Ok(())
143}
144
145#[driver_test(id(ID))]
146pub async fn eager_has_many_create_returning_loads_relations(t: &mut Test) -> Result<()> {
147 #[derive(Debug, toasty::Model)]
148 struct User {
149 #[key]
150 #[auto]
151 id: ID,
152 name: String,
153
154 #[has_many]
155 posts: Vec<Post>,
156 }
157
158 #[derive(Debug, toasty::Model)]
159 struct Post {
160 #[key]
161 #[auto]
162 id: ID,
163 title: String,
164
165 #[index]
166 user_id: ID,
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, Post)).await;
173
174 let user = User::create()
175 .name("Alice")
176 .posts([Post::create().title("hello")])
177 .exec(&mut db)
178 .await?;
179
180 assert_eq!(user.name, "Alice");
181 assert_eq!(user.posts.len(), 1);
182 assert_eq!(user.posts[0].title, "hello");
183
184 Ok(())
185}
186
187#[driver_test]
188pub async fn eager_nested_relations_load_without_include(t: &mut Test) -> Result<()> {
189 #[derive(Debug, toasty::Model)]
190 struct User {
191 #[key]
192 id: uuid::Uuid,
193 name: String,
194
195 #[has_many]
196 posts: Vec<Post>,
197 }
198
199 #[derive(Debug, toasty::Model)]
200 struct Post {
201 #[key]
202 id: uuid::Uuid,
203 title: String,
204
205 #[index]
206 user_id: uuid::Uuid,
207
208 #[belongs_to(key = user_id, references = id)]
209 user: toasty::Deferred<User>,
210
211 #[has_many]
212 comments: Vec<Comment>,
213 }
214
215 #[derive(Debug, toasty::Model)]
216 struct Comment {
217 #[key]
218 id: uuid::Uuid,
219 body: String,
220
221 #[index]
222 post_id: uuid::Uuid,
223
224 #[belongs_to(key = post_id, references = id)]
225 post: toasty::Deferred<Post>,
226 }
227
228 let mut db = t.setup_db(models!(User, Post, Comment)).await;
229 let user_id = uuid::Uuid::from_u128(10);
230 let post_id = uuid::Uuid::from_u128(11);
231
232 insert_row::<User>(
233 &mut db,
234 vec![
235 stmt::Value::Uuid(user_id).into(),
236 stmt::Value::from("Alice").into(),
237 stmt::Value::Null.into(),
238 ],
239 )
240 .await?;
241 insert_row::<Post>(
242 &mut db,
243 vec![
244 stmt::Value::Uuid(post_id).into(),
245 stmt::Value::from("hello").into(),
246 stmt::Value::Uuid(user_id).into(),
247 stmt::Value::Null.into(),
248 stmt::Value::Null.into(),
249 ],
250 )
251 .await?;
252 insert_row::<Comment>(
253 &mut db,
254 vec![
255 stmt::Value::Uuid(uuid::Uuid::from_u128(12)).into(),
256 stmt::Value::from("first").into(),
257 stmt::Value::Uuid(post_id).into(),
258 stmt::Value::Null.into(),
259 ],
260 )
261 .await?;
262
263 let user = User::filter_by_id(user_id).get(&mut db).await?;
264
265 assert_eq!(user.posts.len(), 1);
266 assert_eq!(user.posts[0].title, "hello");
267 assert_eq!(user.posts[0].comments.len(), 1);
268 assert_eq!(user.posts[0].comments[0].body, "first");
269
270 Ok(())
271}
272
273#[driver_test]
274pub async fn eager_relations_reload_after_update(t: &mut Test) -> Result<()> {
275 #[derive(Debug, toasty::Model)]
276 struct User {
277 #[key]
278 id: uuid::Uuid,
279 name: String,
280
281 #[has_many]
282 posts: Vec<Post>,
283 }
284
285 #[derive(Debug, toasty::Model)]
286 struct Post {
287 #[key]
288 #[auto]
289 id: uuid::Uuid,
290 title: String,
291
292 #[index]
293 user_id: uuid::Uuid,
294
295 #[belongs_to(key = user_id, references = id)]
296 user: toasty::Deferred<User>,
297 }
298
299 let mut db = t.setup_db(models!(User, Post)).await;
300 let user_id = uuid::Uuid::from_u128(20);
301
302 insert_row::<User>(
303 &mut db,
304 vec![
305 stmt::Value::Uuid(user_id).into(),
306 stmt::Value::from("Alice").into(),
307 stmt::Value::Null.into(),
308 ],
309 )
310 .await?;
311
312 let mut user = User::filter_by_id(user_id).get(&mut db).await?;
313 assert!(user.posts.is_empty());
314
315 user.update()
316 .name("Alice Updated")
317 .posts(toasty::stmt::insert(Post::create().title("first")))
318 .exec(&mut db)
319 .await?;
320
321 let mut titles = user
322 .posts
323 .iter()
324 .map(|post| post.title.as_str())
325 .collect::<Vec<_>>();
326 titles.sort_unstable();
327
328 assert_eq!(user.name, "Alice Updated");
329 assert_eq!(titles, vec!["first"]);
330
331 Ok(())
332}
333
334#[driver_test(id(ID))]
335pub async fn eager_relation_cycle_is_rejected(t: &mut Test) -> Result<()> {
336 #[derive(Debug, toasty::Model)]
337 struct User {
338 #[key]
339 #[auto]
340 id: ID,
341
342 #[has_many]
343 posts: Vec<Post>,
344 }
345
346 #[derive(Debug, toasty::Model)]
347 struct Post {
348 #[key]
349 #[auto]
350 id: ID,
351
352 #[index]
353 user_id: ID,
354
355 #[belongs_to(key = user_id, references = id)]
356 user: User,
357 }
358
359 let err = t.try_setup_db(models!(User, Post)).await.unwrap_err();
360 let msg = err.to_string();
361
362 assert!(
363 msg.contains("eager relation cycle"),
364 "expected eager relation cycle error, got: {msg}"
365 );
366
367 Ok(())
368}
369
370#[driver_test(id(ID))]
371pub async fn eager_relation_self_cycle_is_rejected(t: &mut Test) -> Result<()> {
372 #[derive(Debug, toasty::Model)]
373 struct Node {
374 #[key]
375 #[auto]
376 id: ID,
377
378 #[index]
379 parent_id: Option<ID>,
380
381 #[belongs_to(key = parent_id, references = id)]
382 parent: toasty::Deferred<Option<Node>>,
383
384 #[has_many(pair = parent)]
385 children: Vec<Node>,
386 }
387
388 let err = t.try_setup_db(models!(Node)).await.unwrap_err();
389 let msg = err.to_string();
390
391 assert!(
392 msg.contains("eager relation cycle"),
393 "expected eager relation cycle error, got: {msg}"
394 );
395
396 Ok(())
397}
398
399#[driver_test(id(ID))]
400pub async fn eager_relation_long_cycle_is_rejected(t: &mut Test) -> Result<()> {
401 #[derive(Debug, toasty::Model)]
402 struct User {
403 #[key]
404 #[auto]
405 id: ID,
406
407 #[has_many]
408 posts: Vec<Post>,
409 }
410
411 #[derive(Debug, toasty::Model)]
412 struct Post {
413 #[key]
414 #[auto]
415 id: ID,
416
417 #[index]
418 user_id: ID,
419
420 #[belongs_to(key = user_id, references = id)]
421 user: toasty::Deferred<User>,
422
423 #[has_one]
424 detail: Option<Detail>,
425 }
426
427 #[derive(Debug, toasty::Model)]
428 struct Detail {
429 #[key]
430 #[auto]
431 id: ID,
432
433 #[unique]
434 post_id: ID,
435
436 #[belongs_to(key = post_id, references = id)]
437 post: toasty::Deferred<Post>,
438
439 #[index]
440 user_id: ID,
441
442 #[belongs_to(key = user_id, references = id)]
443 user: User,
444 }
445
446 let err = t
447 .try_setup_db(models!(User, Post, Detail))
448 .await
449 .unwrap_err();
450 let msg = err.to_string();
451
452 assert!(
453 msg.contains("eager relation cycle"),
454 "expected eager relation cycle error, got: {msg}"
455 );
456
457 Ok(())
458}
459
460async fn insert_row<M: Model>(db: &mut toasty::Db, fields: Vec<stmt::Expr>) -> Result<()> {
461 let insert = stmt::Insert {
462 target: stmt::InsertTarget::Model(<M as toasty::schema::Model>::id()),
463 source: stmt::Query::new_single(vec![stmt::Expr::record(fields)]),
464 returning: None,
465 };
466
467 toasty::Statement::<()>::from_untyped_stmt(insert.into())
468 .exec(db)
469 .await
470}