Skip to main content

toasty_driver_integration_suite/tests/
relation_has_many_same_target.rs

1//! Test has_many/belongs_to associations where multiple `belongs_to` fields on
2//! the same child model target the same parent model, disambiguated by
3//! `#[has_many(pair = <field>)]`.
4
5use crate::prelude::*;
6use hashbrown::HashSet;
7
8#[driver_test(id(ID), scenario(crate::scenarios::has_many_same_target))]
9pub async fn pair_hint_disambiguates_has_many(test: &mut Test) -> Result<()> {
10    let mut db = setup(test).await;
11
12    let users = toasty::create!(User::[
13        { name: "Alice" },
14        { name: "Bob" },
15    ])
16    .exec(&mut db)
17    .await?;
18    let (alice, bob) = (&users[0], &users[1]);
19
20    // Alice authors two articles (both reviewed by Bob); Bob authors one
21    // (reviewed by Alice).
22    let articles = toasty::create!(Article::[
23        { title: "one",   author: alice, reviewer: bob },
24        { title: "two",   author: alice, reviewer: bob },
25        { title: "three", author: bob,   reviewer: alice },
26    ])
27    .exec(&mut db)
28    .await?;
29    let (a1, a2, a3) = (&articles[0], &articles[1], &articles[2]);
30
31    // Each has_many side picks up only the articles that match its paired
32    // belongs_to — not every article referencing the user.
33    let alice_authored: HashSet<_> = alice
34        .authored_articles()
35        .exec(&mut db)
36        .await?
37        .into_iter()
38        .map(|a| a.id)
39        .collect();
40    assert_eq!(alice_authored, HashSet::from_iter([a1.id, a2.id]));
41
42    let alice_reviewed: HashSet<_> = alice
43        .reviewed_articles()
44        .exec(&mut db)
45        .await?
46        .into_iter()
47        .map(|a| a.id)
48        .collect();
49    assert_eq!(alice_reviewed, HashSet::from_iter([a3.id]));
50
51    // Navigating back from an article to each parent resolves the correct user.
52    let a1_author = a1.author().exec(&mut db).await?;
53    let a1_reviewer = a1.reviewer().exec(&mut db).await?;
54    assert_eq!(a1_author.id, alice.id);
55    assert_eq!(a1_reviewer.id, bob.id);
56
57    Ok(())
58}
59
60#[driver_test(id(ID), scenario(crate::scenarios::has_many_same_target))]
61pub async fn pair_hint_create_via_has_many_accessor(test: &mut Test) -> Result<()> {
62    let mut db = setup(test).await;
63
64    let users = toasty::create!(User::[
65        { name: "Alice" },
66        { name: "Bob" },
67    ])
68    .exec(&mut db)
69    .await?;
70    let (alice, bob) = (&users[0], &users[1]);
71
72    // Create an article scoped to Alice's authored side — the scoped create
73    // should fill in the `author` FK, and the other side (`reviewer`) still
74    // needs to be specified.
75    let article = toasty::create!(in alice.authored_articles() {
76        title: "draft",
77        reviewer: bob,
78    })
79    .exec(&mut db)
80    .await?;
81
82    assert_eq!(article.author_id, alice.id);
83    assert_eq!(article.reviewer_id, bob.id);
84
85    // The reviewer side for Alice is still empty even though she has an
86    // authored article.
87    assert!(alice.reviewed_articles().exec(&mut db).await?.is_empty());
88
89    Ok(())
90}