1use crate::prelude::*;
9
10#[driver_test(requires(and(sql, auto_increment)))]
13pub async fn include_with_newtype_foreign_key(test: &mut Test) -> Result<()> {
14 #[derive(Debug, toasty::Embed)]
15 struct UserId(u64);
16
17 #[derive(Debug, toasty::Model)]
18 struct User {
19 #[key]
20 #[auto]
21 id: UserId,
22
23 #[has_many]
24 comments: toasty::Deferred<Vec<Comment>>,
25
26 #[has_many(via = comments.article)]
27 commented_articles: toasty::Deferred<Vec<Article>>,
28 }
29
30 #[derive(Debug, toasty::Model)]
31 struct Comment {
32 #[key]
33 #[auto]
34 id: u64,
35
36 #[index]
37 user_id: Option<UserId>,
38
39 #[belongs_to(key = user_id, references = id)]
40 user: toasty::Deferred<Option<User>>,
41
42 #[index]
43 article_id: u64,
44
45 #[belongs_to(key = article_id, references = id)]
46 article: toasty::Deferred<Article>,
47 }
48
49 #[derive(Debug, toasty::Model)]
50 struct Article {
51 #[key]
52 #[auto]
53 id: u64,
54
55 #[has_many]
56 comments: toasty::Deferred<Vec<Comment>>,
57 }
58
59 let mut db = test.setup_db(models!(User, Comment, Article)).await;
60 let user = toasty::create!(User {}).exec(&mut db).await?;
61 let article = toasty::create!(Article {}).exec(&mut db).await?;
62 toasty::create!(Comment {
63 user: &user,
64 article: &article,
65 })
66 .exec(&mut db)
67 .await?;
68
69 let users: Vec<User> = User::all()
70 .include(User::fields().commented_articles())
71 .exec(&mut db)
72 .await?;
73
74 assert_struct!(users, [{
75 commented_articles.get().len(): 1,
76 }]);
77
78 Ok(())
79}
80
81#[driver_test(requires(sql))]
84pub async fn include_with_unit_enum_foreign_key(test: &mut Test) -> Result<()> {
85 #[derive(Debug, toasty::Embed)]
86 enum UserId {
87 #[column(variant = 1)]
88 Alice,
89 #[column(variant = 2)]
90 Bob,
91 }
92
93 #[derive(Debug, toasty::Model)]
94 struct User {
95 #[key]
96 id: UserId,
97
98 #[has_many]
99 comments: toasty::Deferred<Vec<Comment>>,
100
101 #[has_many(via = comments.article)]
102 commented_articles: toasty::Deferred<Vec<Article>>,
103 }
104
105 #[derive(Debug, toasty::Model)]
106 struct Comment {
107 #[key]
108 id: u64,
109
110 #[index]
111 user_id: UserId,
112
113 #[belongs_to(key = user_id, references = id)]
114 user: toasty::Deferred<User>,
115
116 #[index]
117 article_id: u64,
118
119 #[belongs_to(key = article_id, references = id)]
120 article: toasty::Deferred<Article>,
121 }
122
123 #[derive(Debug, toasty::Model)]
124 struct Article {
125 #[key]
126 id: u64,
127
128 #[has_many]
129 comments: toasty::Deferred<Vec<Comment>>,
130 }
131
132 let mut db = test.setup_db(models!(User, Comment, Article)).await;
133 let user = toasty::create!(User { id: UserId::Alice })
134 .exec(&mut db)
135 .await?;
136 let article = toasty::create!(Article { id: 1 }).exec(&mut db).await?;
137 toasty::create!(Comment {
138 id: 1,
139 user: &user,
140 article: &article,
141 })
142 .exec(&mut db)
143 .await?;
144
145 let users: Vec<User> = User::all()
146 .include(User::fields().commented_articles())
147 .exec(&mut db)
148 .await?;
149
150 assert_struct!(users, [{
151 commented_articles.get().len(): 1,
152 }]);
153
154 Ok(())
155}
156
157#[driver_test(requires(sql), scenario(crate::scenarios::user_comment_article))]
160pub async fn query_and_include_two_step_targets_and_values(test: &mut Test) -> Result<()> {
161 let mut db = setup(test).await;
162
163 let users = toasty::create!(User::[
164 { name: "Alice" },
165 { name: "Bob" },
166 { name: "Charlie" },
167 ])
168 .exec(&mut db)
169 .await?;
170 let (alice, bob) = (&users[0], &users[1]);
171
172 let articles = toasty::create!(Article::[
173 { title: "Rust" },
174 { title: "Toasty" },
175 { title: "SQL" },
176 ])
177 .exec(&mut db)
178 .await?;
179 let (rust, toasty_article, sql) = (&articles[0], &articles[1], &articles[2]);
180
181 toasty::create!(Comment::[
183 { body: "a1", user: alice, article: rust },
184 { body: "a2", user: alice, article: rust },
185 { body: "a3", user: alice, article: toasty_article },
186 { body: "b1", user: bob, article: sql },
187 ])
188 .exec(&mut db)
189 .await?;
190
191 let commented = alice.commented_articles().exec(&mut db).await?;
194 assert_eq_unordered!(commented.iter().map(|a| &a.title[..]), ["Rust", "Toasty"]);
195
196 let commented = bob.commented_articles().exec(&mut db).await?;
198 assert_eq_unordered!(commented.iter().map(|a| &a.title[..]), ["SQL"]);
199
200 let loaded: Vec<User> = User::all()
201 .include(User::fields().commented_articles())
202 .exec(&mut db)
203 .await?;
204 assert_eq!(3, loaded.len());
205 for user in &loaded {
206 let titles: Vec<&str> = user
207 .commented_articles
208 .get()
209 .iter()
210 .map(|a| &a.title[..])
211 .collect();
212 match &user.name[..] {
213 "Alice" => {
214 assert_eq_unordered!(titles, ["Rust", "Toasty"]);
215 }
216 "Bob" => {
217 assert_eq_unordered!(titles, ["SQL"]);
218 }
219 "Charlie" => assert!(titles.is_empty(), "Charlie has no comments; got {titles:?}"),
220 other => panic!("unexpected user {other}"),
221 }
222 }
223
224 let titles = alice.commented_article_titles().exec(&mut db).await?;
225 assert_eq_unordered!(titles.iter().map(|t| &t[..]), ["Rust", "Toasty"]);
226 let titles = bob.commented_article_titles().exec(&mut db).await?;
227 assert_eq_unordered!(titles.iter().map(|t| &t[..]), ["SQL"]);
228
229 let loaded: Vec<User> = User::all()
230 .include(User::fields().commented_article_titles())
231 .exec(&mut db)
232 .await?;
233 assert_eq!(3, loaded.len());
234 for user in &loaded {
235 let titles: Vec<&str> = user
236 .commented_article_titles
237 .get()
238 .iter()
239 .map(|t| &t[..])
240 .collect();
241 match &user.name[..] {
242 "Alice" => {
243 assert_eq_unordered!(titles, ["Rust", "Toasty"]);
244 }
245 "Bob" => {
246 assert_eq_unordered!(titles, ["SQL"]);
247 }
248 "Charlie" => assert!(titles.is_empty(), "Charlie has no comments; got {titles:?}"),
249 other => panic!("unexpected user {other}"),
250 }
251 }
252
253 Ok(())
254}
255
256#[driver_test(requires(sql), scenario(crate::scenarios::user_comment_article))]
258pub async fn query_with_no_intermediates_is_empty(test: &mut Test) -> Result<()> {
259 let mut db = setup(test).await;
260
261 let user = toasty::create!(User { name: "Alice" })
262 .exec(&mut db)
263 .await?;
264 toasty::create!(Article { title: "Rust" })
265 .exec(&mut db)
266 .await?;
267
268 let commented = user.commented_articles().exec(&mut db).await?;
269 assert!(commented.is_empty());
270
271 Ok(())
272}
273
274#[driver_test(requires(sql), scenario(crate::scenarios::user_comment_article))]
277pub async fn via_relation_query_can_be_filtered(test: &mut Test) -> Result<()> {
278 let mut db = setup(test).await;
279
280 let alice = toasty::create!(User { name: "Alice" })
281 .exec(&mut db)
282 .await?;
283
284 let articles = toasty::create!(Article::[
285 { title: "Rust" },
286 { title: "Toasty" },
287 { title: "SQL" },
288 ])
289 .exec(&mut db)
290 .await?;
291 let (rust, toasty_article, sql) = (&articles[0], &articles[1], &articles[2]);
292
293 toasty::create!(Comment::[
294 { body: "a1", user: &alice, article: rust },
295 { body: "a2", user: &alice, article: toasty_article },
296 { body: "a3", user: &alice, article: sql },
297 ])
298 .exec(&mut db)
299 .await?;
300
301 let filtered: Vec<_> = alice
302 .commented_articles()
303 .filter(Article::fields().title().eq("Toasty"))
304 .exec(&mut db)
305 .await?;
306 assert_eq_unordered!(filtered.iter().map(|a| &a.title[..]), ["Toasty"]);
307
308 Ok(())
309}
310
311#[driver_test(requires(sql), scenario(crate::scenarios::user_org_project_todo))]
315pub async fn filter_parent_by_via_any(test: &mut Test) -> Result<()> {
316 let mut db = setup(test).await;
317
318 let users = toasty::create!(User::[{ name: "Alice" }, { name: "Bob" }])
319 .exec(&mut db)
320 .await?;
321 let alice_org = toasty::create!(Organization {
322 name: "Alice Org",
323 user: &users[0]
324 })
325 .exec(&mut db)
326 .await?;
327 let bob_org = toasty::create!(Organization {
328 name: "Bob Org",
329 user: &users[1]
330 })
331 .exec(&mut db)
332 .await?;
333 let alice_project = toasty::create!(Project {
334 name: "Alice Project",
335 organization: &alice_org
336 })
337 .exec(&mut db)
338 .await?;
339 let bob_project = toasty::create!(Project {
340 name: "Bob Project",
341 organization: &bob_org
342 })
343 .exec(&mut db)
344 .await?;
345
346 toasty::create!(Todo::[
347 { title: "match", project: &alice_project },
348 { title: "other", project: &bob_project },
349 ])
350 .exec(&mut db)
351 .await?;
352
353 let users: Vec<User> = User::filter(
354 User::fields()
355 .todos()
356 .any(Todo::fields().title().eq("match")),
357 )
358 .exec(&mut db)
359 .await?;
360 assert_eq!(users.len(), 1);
361 assert_eq!(users[0].name, "Alice");
362
363 let users: Vec<User> = User::filter(
364 User::fields()
365 .nested_todos()
366 .any(Todo::fields().title().eq("match")),
367 )
368 .exec(&mut db)
369 .await?;
370 assert_eq!(users.len(), 1);
371 assert_eq!(users[0].name, "Alice");
372
373 Ok(())
374}
375
376#[driver_test(requires(sql), scenario(crate::scenarios::user_org_project_todo))]
402pub async fn include_via_three_step(test: &mut Test) -> Result<()> {
403 let mut db = setup(test).await;
404
405 let users = toasty::create!(User::[
406 { name: "Alice" },
407 { name: "Bob" },
408 ])
409 .exec(&mut db)
410 .await?;
411 let (alice, bob) = (&users[0], &users[1]);
412
413 let alice_org_a = toasty::create!(Organization {
414 name: "A-Co",
415 user: alice
416 })
417 .exec(&mut db)
418 .await?;
419 let alice_org_b = toasty::create!(Organization {
420 name: "B-Co",
421 user: alice
422 })
423 .exec(&mut db)
424 .await?;
425 let bob_org = toasty::create!(Organization {
426 name: "Bob-Inc",
427 user: bob
428 })
429 .exec(&mut db)
430 .await?;
431
432 let alice_proj_1 = toasty::create!(Project {
433 name: "p1",
434 organization: &alice_org_a
435 })
436 .exec(&mut db)
437 .await?;
438 let alice_proj_2 = toasty::create!(Project {
439 name: "p2",
440 organization: &alice_org_a
441 })
442 .exec(&mut db)
443 .await?;
444 let alice_proj_3 = toasty::create!(Project {
445 name: "p3",
446 organization: &alice_org_b
447 })
448 .exec(&mut db)
449 .await?;
450 let bob_proj = toasty::create!(Project {
451 name: "bp",
452 organization: &bob_org
453 })
454 .exec(&mut db)
455 .await?;
456
457 toasty::create!(Todo::[
458 { title: "a-1", project: &alice_proj_1 },
459 { title: "a-2", project: &alice_proj_1 },
460 { title: "a-3", project: &alice_proj_2 },
461 { title: "a-4", project: &alice_proj_3 },
462 { title: "b-1", project: &bob_proj },
463 { title: "b-2", project: &bob_proj },
464 ])
465 .exec(&mut db)
466 .await?;
467
468 let loaded: Vec<User> = User::all()
469 .include(User::fields().todos())
470 .exec(&mut db)
471 .await?;
472 assert_eq!(2, loaded.len());
473
474 for user in &loaded {
475 let titles: Vec<&str> = user.todos.get().iter().map(|t| &t.title[..]).collect();
476 match &user.name[..] {
477 "Alice" => {
478 assert_eq_unordered!(titles, ["a-1", "a-2", "a-3", "a-4"]);
479 }
480 "Bob" => {
481 assert_eq_unordered!(titles, ["b-1", "b-2"]);
482 }
483 other => panic!("unexpected user {other}"),
484 }
485 }
486
487 Ok(())
488}
489
490#[driver_test(requires(sql), scenario(crate::scenarios::user_org_project_todo))]
497pub async fn include_via_nested_via(test: &mut Test) -> Result<()> {
498 let mut db = setup(test).await;
499
500 let users = toasty::create!(User::[
501 { name: "Alice" },
502 { name: "Bob" },
503 ])
504 .exec(&mut db)
505 .await?;
506 let (alice, bob) = (&users[0], &users[1]);
507
508 let alice_org_a = toasty::create!(Organization {
509 name: "A-Co",
510 user: alice
511 })
512 .exec(&mut db)
513 .await?;
514 let alice_org_b = toasty::create!(Organization {
515 name: "B-Co",
516 user: alice
517 })
518 .exec(&mut db)
519 .await?;
520 let bob_org = toasty::create!(Organization {
521 name: "Bob-Inc",
522 user: bob
523 })
524 .exec(&mut db)
525 .await?;
526
527 let alice_proj_1 = toasty::create!(Project {
528 name: "p1",
529 organization: &alice_org_a
530 })
531 .exec(&mut db)
532 .await?;
533 let alice_proj_2 = toasty::create!(Project {
534 name: "p2",
535 organization: &alice_org_a
536 })
537 .exec(&mut db)
538 .await?;
539 let alice_proj_3 = toasty::create!(Project {
540 name: "p3",
541 organization: &alice_org_b
542 })
543 .exec(&mut db)
544 .await?;
545 let bob_proj = toasty::create!(Project {
546 name: "bp",
547 organization: &bob_org
548 })
549 .exec(&mut db)
550 .await?;
551
552 toasty::create!(Todo::[
553 { title: "a-1", project: &alice_proj_1 },
554 { title: "a-2", project: &alice_proj_1 },
555 { title: "a-3", project: &alice_proj_2 },
556 { title: "a-4", project: &alice_proj_3 },
557 { title: "b-1", project: &bob_proj },
558 { title: "b-2", project: &bob_proj },
559 ])
560 .exec(&mut db)
561 .await?;
562
563 let loaded: Vec<User> = User::all()
564 .include(User::fields().nested_todos())
565 .exec(&mut db)
566 .await?;
567 assert_eq!(2, loaded.len());
568
569 for user in &loaded {
570 let titles: Vec<&str> = user
571 .nested_todos
572 .get()
573 .iter()
574 .map(|t| &t.title[..])
575 .collect();
576 match &user.name[..] {
577 "Alice" => {
578 assert_eq_unordered!(titles, ["a-1", "a-2", "a-3", "a-4"]);
579 }
580 "Bob" => {
581 assert_eq_unordered!(titles, ["b-1", "b-2"]);
582 }
583 other => panic!("unexpected user {other}"),
584 }
585 }
586
587 Ok(())
588}
589
590#[driver_test(requires(sql), scenario(crate::scenarios::user_org_project_todo))]
598pub async fn scalar_via_of_via(test: &mut Test) -> Result<()> {
599 let mut db = setup(test).await;
600
601 let alice = toasty::create!(User { name: "Alice" })
602 .exec(&mut db)
603 .await?;
604 let org_a = toasty::create!(Organization {
605 name: "A-Co",
606 user: &alice
607 })
608 .exec(&mut db)
609 .await?;
610 let org_b = toasty::create!(Organization {
611 name: "B-Co",
612 user: &alice
613 })
614 .exec(&mut db)
615 .await?;
616 let proj_a = toasty::create!(Project {
617 name: "p-a",
618 organization: &org_a
619 })
620 .exec(&mut db)
621 .await?;
622 let proj_b = toasty::create!(Project {
623 name: "p-b",
624 organization: &org_b
625 })
626 .exec(&mut db)
627 .await?;
628
629 toasty::create!(Todo::[
631 { title: "x", project: &proj_a },
632 { title: "y", project: &proj_a },
633 { title: "y", project: &proj_b },
634 { title: "z", project: &proj_b },
635 ])
636 .exec(&mut db)
637 .await?;
638
639 let titles = alice.nested_todo_titles().exec(&mut db).await?;
641 assert_eq_unordered!(titles.iter().map(|t| &t[..]), ["x", "y", "z"]);
642
643 let loaded = User::filter_by_id(alice.id)
645 .include(User::fields().nested_todo_titles())
646 .get(&mut db)
647 .await?;
648 let titles: Vec<&str> = loaded
649 .nested_todo_titles
650 .get()
651 .iter()
652 .map(|t| &t[..])
653 .collect();
654 assert_eq_unordered!(titles, ["x", "y", "z"]);
655
656 Ok(())
657}
658
659#[driver_test(requires(sql), scenario(crate::scenarios::user_org_project_todo))]
662pub async fn include_via_three_step_no_intermediates(test: &mut Test) -> Result<()> {
663 let mut db = setup(test).await;
664
665 let alice = toasty::create!(User { name: "Alice" })
666 .exec(&mut db)
667 .await?;
668
669 let loaded = User::filter_by_id(alice.id)
670 .include(User::fields().todos())
671 .get(&mut db)
672 .await?;
673 assert!(loaded.todos.get().is_empty());
674
675 Ok(())
676}
677
678#[driver_test(requires(sql), scenario(crate::scenarios::user_comment_article))]
685pub async fn select_via_two_step(test: &mut Test) -> Result<()> {
686 let mut db = setup(test).await;
687
688 let alice = toasty::create!(User { name: "Alice" })
689 .exec(&mut db)
690 .await?;
691
692 let articles = toasty::create!(Article::[
693 { title: "Rust" },
694 { title: "Toasty" },
695 ])
696 .exec(&mut db)
697 .await?;
698 let (rust, toasty_article) = (&articles[0], &articles[1]);
699
700 toasty::create!(Comment::[
701 { body: "a1", user: &alice, article: rust },
702 { body: "a2", user: &alice, article: rust },
703 { body: "a3", user: &alice, article: toasty_article },
704 ])
705 .exec(&mut db)
706 .await?;
707
708 let articles_per_user: Vec<Vec<Article>> = User::all()
709 .select(User::fields().commented_articles())
710 .exec(&mut db)
711 .await?;
712
713 assert_eq!(1, articles_per_user.len());
714 let titles: Vec<&str> = articles_per_user[0].iter().map(|a| &a.title[..]).collect();
715 assert_eq_unordered!(titles, ["Rust", "Toasty"]);
716
717 Ok(())
718}
719
720#[driver_test(requires(sql), scenario(crate::scenarios::user_account_subscription))]
727pub async fn include_via_has_one(test: &mut Test) -> Result<()> {
728 let mut db = setup(test).await;
729
730 toasty::create!(User {
733 name: "Alice",
734 account: Account::create().subscription(Subscription::create().plan("pro")),
735 })
736 .exec(&mut db)
737 .await?;
738 toasty::create!(User {
739 name: "Bob",
740 account: Account::create(),
741 })
742 .exec(&mut db)
743 .await?;
744 toasty::create!(User { name: "Carol" })
745 .exec(&mut db)
746 .await?;
747
748 let loaded: Vec<User> = User::all()
749 .include(User::fields().subscription())
750 .exec(&mut db)
751 .await?;
752 assert_eq!(3, loaded.len());
753
754 for user in &loaded {
755 let plan = user.subscription.get().as_ref().map(|s| &s.plan[..]);
756 match &user.name[..] {
757 "Alice" => assert_eq!(plan, Some("pro")),
758 "Bob" => assert_eq!(plan, None, "Bob has an account but no subscription"),
759 "Carol" => assert_eq!(plan, None, "Carol has no account"),
760 other => panic!("unexpected user {other}"),
761 }
762 }
763
764 Ok(())
765}
766
767#[driver_test(requires(sql), scenario(crate::scenarios::user_account_subscription))]
773pub async fn select_via_has_one(test: &mut Test) -> Result<()> {
774 let mut db = setup(test).await;
775
776 toasty::create!(User {
777 name: "Alice",
778 account: Account::create().subscription(Subscription::create().plan("pro")),
779 })
780 .exec(&mut db)
781 .await?;
782
783 let subscriptions: Vec<Subscription> = User::filter(User::fields().name().eq("Alice"))
784 .select(User::fields().subscription())
785 .exec(&mut db)
786 .await?;
787
788 assert_eq!(1, subscriptions.len());
789 assert_eq!(subscriptions[0].plan, "pro");
790
791 Ok(())
792}
793
794#[driver_test(requires(sql), scenario(crate::scenarios::user_comment_article))]
811pub async fn scalar_via_distinct_values_across_distinct_targets(test: &mut Test) -> Result<()> {
812 let mut db = setup(test).await;
813
814 let alice = toasty::create!(User { name: "Alice" })
815 .exec(&mut db)
816 .await?;
817
818 let articles = toasty::create!(Article::[{ title: "Rust" }, { title: "Rust" }])
820 .exec(&mut db)
821 .await?;
822 let (rust_a, rust_b) = (&articles[0], &articles[1]);
823
824 toasty::create!(Comment::[
825 { body: "a1", user: &alice, article: rust_a },
826 { body: "a2", user: &alice, article: rust_b },
827 ])
828 .exec(&mut db)
829 .await?;
830
831 let commented = alice.commented_articles().exec(&mut db).await?;
833 assert_eq!(commented.len(), 2);
834
835 let titles = alice.commented_article_titles().exec(&mut db).await?;
838 assert_eq_unordered!(titles.iter().map(|t| &t[..]), ["Rust"]);
839
840 let loaded = User::filter_by_id(alice.id)
842 .include(User::fields().commented_article_titles())
843 .get(&mut db)
844 .await?;
845 let titles: Vec<&str> = loaded
846 .commented_article_titles
847 .get()
848 .iter()
849 .map(|t| &t[..])
850 .collect();
851 assert_eq_unordered!(titles, ["Rust"]);
852
853 Ok(())
854}
855
856#[driver_test(requires(sql), scenario(crate::scenarios::user_comment_article))]
862pub async fn query_scalar_via_two_step(test: &mut Test) -> Result<()> {
863 let mut db = setup(test).await;
864
865 let alice = toasty::create!(User { name: "Alice" })
866 .exec(&mut db)
867 .await?;
868 let article = toasty::create!(Article { title: "Rust" })
869 .exec(&mut db)
870 .await?;
871
872 toasty::create!(Comment::[
874 { body: "a1", user: &alice, article: &article },
875 { body: "a1", user: &alice, article: &article },
876 { body: "a2", user: &alice, article: &article },
877 ])
878 .exec(&mut db)
879 .await?;
880
881 let bodies = alice.comment_bodies().exec(&mut db).await?;
882 assert_eq_unordered!(bodies.iter().map(|b| &b[..]), ["a1", "a2"]);
883
884 let loaded = User::filter_by_id(alice.id)
885 .include(User::fields().comment_bodies())
886 .get(&mut db)
887 .await?;
888 let bodies: Vec<&str> = loaded.comment_bodies.get().iter().map(|b| &b[..]).collect();
889 assert_eq_unordered!(bodies, ["a1", "a2"]);
890
891 Ok(())
892}
893
894#[driver_test(requires(sql), scenario(crate::scenarios::user_comment_article))]
898pub async fn query_chain_scalar_via(test: &mut Test) -> Result<()> {
899 let mut db = setup(test).await;
900
901 let alice = toasty::create!(User { name: "Alice" })
902 .exec(&mut db)
903 .await?;
904
905 let articles = toasty::create!(Article::[{ title: "Rust" }, { title: "Toasty" }])
906 .exec(&mut db)
907 .await?;
908 let (rust, toasty_article) = (&articles[0], &articles[1]);
909
910 toasty::create!(Comment::[
911 { body: "a1", user: &alice, article: rust },
912 { body: "a2", user: &alice, article: rust },
913 { body: "a3", user: &alice, article: toasty_article },
914 ])
915 .exec(&mut db)
916 .await?;
917
918 let titles = User::filter(User::fields().name().eq("Alice"))
919 .commented_article_titles()
920 .exec(&mut db)
921 .await?;
922 assert_eq_unordered!(titles.iter().map(|t| &t[..]), ["Rust", "Toasty"]);
923
924 Ok(())
925}
926
927#[driver_test(requires(sql), scenario(crate::scenarios::user_comment_article))]
930pub async fn select_scalar_via(test: &mut Test) -> Result<()> {
931 let mut db = setup(test).await;
932
933 let alice = toasty::create!(User { name: "Alice" })
934 .exec(&mut db)
935 .await?;
936
937 let articles = toasty::create!(Article::[{ title: "Rust" }, { title: "Toasty" }])
938 .exec(&mut db)
939 .await?;
940 let (rust, toasty_article) = (&articles[0], &articles[1]);
941
942 toasty::create!(Comment::[
943 { body: "a1", user: &alice, article: rust },
944 { body: "a2", user: &alice, article: rust },
945 { body: "a3", user: &alice, article: toasty_article },
946 ])
947 .exec(&mut db)
948 .await?;
949
950 let titles_per_user: Vec<Vec<String>> = User::all()
951 .select(User::fields().commented_article_titles())
952 .exec(&mut db)
953 .await?;
954
955 assert_eq!(1, titles_per_user.len());
956 let titles: Vec<&str> = titles_per_user[0].iter().map(|t| &t[..]).collect();
957 assert_eq_unordered!(titles, ["Rust", "Toasty"]);
958
959 Ok(())
960}
961
962#[driver_test(requires(sql), scenario(crate::scenarios::user_tag_names))]
970pub async fn eager_scalar_via_auto_loads(test: &mut Test) -> Result<()> {
971 let mut db = setup(test).await;
972
973 let users = toasty::create!(User::[{ name: "Alice" }, { name: "Bob" }])
974 .exec(&mut db)
975 .await?;
976 let (alice, bob) = (&users[0], &users[1]);
977
978 toasty::create!(Tag::[
979 { name: "rust", user: alice },
980 { name: "rust", user: alice },
981 { name: "db", user: alice },
982 { name: "sql", user: bob },
983 ])
984 .exec(&mut db)
985 .await?;
986
987 let loaded: Vec<User> = User::all().exec(&mut db).await?;
989 assert_eq!(2, loaded.len());
990
991 for user in &loaded {
992 let names: Vec<&str> = user.tag_names.iter().map(|n| &n[..]).collect();
993 match &user.name[..] {
994 "Alice" => {
996 assert_eq_unordered!(names, ["rust", "db"]);
997 }
998 "Bob" => {
999 assert_eq_unordered!(names, ["sql"]);
1000 }
1001 other => panic!("unexpected user {other}"),
1002 }
1003 }
1004
1005 Ok(())
1006}