zf

zenflows testing
git clone https://s.sonu.ch/~srfsh/zf.git
Log | Files | Refs | Submodules | README | LICENSE

preload.exs (28767B)


      1 defmodule Ecto.Integration.PreloadTest do
      2   use Ecto.Integration.Case, async: Application.compile_env(:ecto, :async_integration_tests, true)
      3 
      4   alias Ecto.Integration.TestRepo
      5   import Ecto.Query
      6 
      7   alias Ecto.Integration.Post
      8   alias Ecto.Integration.Comment
      9   alias Ecto.Integration.Item
     10   alias Ecto.Integration.Permalink
     11   alias Ecto.Integration.User
     12   alias Ecto.Integration.Custom
     13   alias Ecto.Integration.Order
     14 
     15   test "preload with parameter from select_merge" do
     16     p1 = TestRepo.insert!(%Post{title: "p1"})
     17     TestRepo.insert!(%Comment{text: "c1", post: p1})
     18 
     19     comments =
     20       from(c in Comment, select: struct(c, [:text]))
     21       |> select_merge([c], %{post_id: c.post_id})
     22       |> preload(:post)
     23       |> TestRepo.all()
     24 
     25     assert [%{text: "c1", post: %{title: "p1"}}] = comments
     26   end
     27 
     28   test "preload has_many" do
     29     p1 = TestRepo.insert!(%Post{title: "1"})
     30     p2 = TestRepo.insert!(%Post{title: "2"})
     31     p3 = TestRepo.insert!(%Post{title: "3"})
     32 
     33     # We use the same text to expose bugs in preload sorting
     34     %Comment{id: cid1} = TestRepo.insert!(%Comment{text: "1", post_id: p1.id})
     35     %Comment{id: cid3} = TestRepo.insert!(%Comment{text: "2", post_id: p2.id})
     36     %Comment{id: cid2} = TestRepo.insert!(%Comment{text: "2", post_id: p1.id})
     37     %Comment{id: cid4} = TestRepo.insert!(%Comment{text: "3", post_id: p2.id})
     38 
     39     assert %Ecto.Association.NotLoaded{} = p1.comments
     40 
     41     [p3, p1, p2] = TestRepo.preload([p3, p1, p2], :comments)
     42     assert [%Comment{id: ^cid1}, %Comment{id: ^cid2}] = p1.comments |> sort_by_id()
     43     assert [%Comment{id: ^cid3}, %Comment{id: ^cid4}] = p2.comments |> sort_by_id()
     44     assert [] = p3.comments
     45   end
     46 
     47   test "preload has_many multiple times" do
     48     p1 = TestRepo.insert!(%Post{title: "1"})
     49     %Comment{id: cid1} = TestRepo.insert!(%Comment{text: "1", post_id: p1.id})
     50     %Comment{id: cid2} = TestRepo.insert!(%Comment{text: "2", post_id: p1.id})
     51 
     52     [p1, p1] = TestRepo.preload([p1, p1], :comments)
     53     assert [%Comment{id: ^cid1}, %Comment{id: ^cid2}] = p1.comments |> sort_by_id()
     54 
     55     [p1, p1] = TestRepo.preload([p1, p1], :comments)
     56     assert [%Comment{id: ^cid1}, %Comment{id: ^cid2}] = p1.comments |> sort_by_id()
     57   end
     58 
     59   test "preload has_one" do
     60     p1 = TestRepo.insert!(%Post{title: "1"})
     61     p2 = TestRepo.insert!(%Post{title: "2"})
     62     p3 = TestRepo.insert!(%Post{title: "3"})
     63 
     64     %Permalink{id: pid1} = TestRepo.insert!(%Permalink{url: "1", post_id: p1.id})
     65     %Permalink{}         = TestRepo.insert!(%Permalink{url: "2", post_id: nil})
     66     %Permalink{id: pid3} = TestRepo.insert!(%Permalink{url: "3", post_id: p3.id})
     67 
     68     assert %Ecto.Association.NotLoaded{} = p1.permalink
     69     assert %Ecto.Association.NotLoaded{} = p2.permalink
     70 
     71     [p3, p1, p2] = TestRepo.preload([p3, p1, p2], :permalink)
     72     assert %Permalink{id: ^pid1} = p1.permalink
     73     refute p2.permalink
     74     assert %Permalink{id: ^pid3} = p3.permalink
     75   end
     76 
     77   test "preload belongs_to" do
     78     %Post{id: pid1} = TestRepo.insert!(%Post{title: "1"})
     79     TestRepo.insert!(%Post{title: "2"})
     80     %Post{id: pid3} = TestRepo.insert!(%Post{title: "3"})
     81 
     82     pl1 = TestRepo.insert!(%Permalink{url: "1", post_id: pid1})
     83     pl2 = TestRepo.insert!(%Permalink{url: "2", post_id: nil})
     84     pl3 = TestRepo.insert!(%Permalink{url: "3", post_id: pid3})
     85     assert %Ecto.Association.NotLoaded{} = pl1.post
     86 
     87     [pl3, pl1, pl2] = TestRepo.preload([pl3, pl1, pl2], :post)
     88     assert %Post{id: ^pid1} = pl1.post
     89     refute pl2.post
     90     assert %Post{id: ^pid3} = pl3.post
     91   end
     92 
     93   test "preload multiple belongs_to" do
     94     %User{id: uid} = TestRepo.insert!(%User{name: "foo"})
     95     %Post{id: pid} = TestRepo.insert!(%Post{title: "1"})
     96     %Comment{id: cid} = TestRepo.insert!(%Comment{post_id: pid, author_id: uid})
     97 
     98     comment = TestRepo.get!(Comment, cid)
     99     comment = TestRepo.preload(comment, [:author, :post])
    100     assert comment.author.id == uid
    101     assert comment.post.id == pid
    102   end
    103 
    104   test "preload belongs_to with shared parent" do
    105     %Post{id: pid1} = TestRepo.insert!(%Post{title: "1"})
    106     %Post{id: pid2} = TestRepo.insert!(%Post{title: "2"})
    107 
    108     c1 = TestRepo.insert!(%Comment{text: "1", post_id: pid1})
    109     c2 = TestRepo.insert!(%Comment{text: "2", post_id: pid1})
    110     c3 = TestRepo.insert!(%Comment{text: "3", post_id: pid2})
    111 
    112     [c3, c1, c2] = TestRepo.preload([c3, c1, c2], :post)
    113     assert %Post{id: ^pid1} = c1.post
    114     assert %Post{id: ^pid1} = c2.post
    115     assert %Post{id: ^pid2} = c3.post
    116   end
    117 
    118   test "preload many_to_many" do
    119     p1 = TestRepo.insert!(%Post{title: "1"})
    120     p2 = TestRepo.insert!(%Post{title: "2"})
    121     p3 = TestRepo.insert!(%Post{title: "3"})
    122 
    123     # We use the same name to expose bugs in preload sorting
    124     %User{id: uid1} = TestRepo.insert!(%User{name: "1"})
    125     %User{id: uid3} = TestRepo.insert!(%User{name: "2"})
    126     %User{id: uid2} = TestRepo.insert!(%User{name: "2"})
    127     %User{id: uid4} = TestRepo.insert!(%User{name: "3"})
    128 
    129     TestRepo.insert_all "posts_users", [[post_id: p1.id, user_id: uid1],
    130                                         [post_id: p1.id, user_id: uid2],
    131                                         [post_id: p2.id, user_id: uid3],
    132                                         [post_id: p2.id, user_id: uid4],
    133                                         [post_id: p3.id, user_id: uid1],
    134                                         [post_id: p3.id, user_id: uid4]]
    135 
    136     assert %Ecto.Association.NotLoaded{} = p1.users
    137 
    138     [p1, p2, p3] = TestRepo.preload([p1, p2, p3], :users)
    139     assert [%User{id: ^uid1}, %User{id: ^uid2}] = p1.users |> sort_by_id
    140     assert [%User{id: ^uid3}, %User{id: ^uid4}] = p2.users |> sort_by_id
    141     assert [%User{id: ^uid1}, %User{id: ^uid4}] = p3.users |> sort_by_id
    142   end
    143 
    144   test "preload has_many through" do
    145     %Post{id: pid1} = p1 = TestRepo.insert!(%Post{})
    146     %Post{id: pid2} = p2 = TestRepo.insert!(%Post{})
    147 
    148     %User{id: uid1} = TestRepo.insert!(%User{name: "foo"})
    149     %User{id: uid2} = TestRepo.insert!(%User{name: "bar"})
    150 
    151     %Comment{} = TestRepo.insert!(%Comment{post_id: pid1, author_id: uid1})
    152     %Comment{} = TestRepo.insert!(%Comment{post_id: pid1, author_id: uid1})
    153     %Comment{} = TestRepo.insert!(%Comment{post_id: pid1, author_id: uid2})
    154     %Comment{} = TestRepo.insert!(%Comment{post_id: pid2, author_id: uid2})
    155 
    156     [p1, p2] = TestRepo.preload([p1, p2], :comments_authors)
    157 
    158     # Through was preloaded
    159     [u1, u2] = p1.comments_authors |> sort_by_id
    160     assert u1.id == uid1
    161     assert u2.id == uid2
    162 
    163     [u2] = p2.comments_authors
    164     assert u2.id == uid2
    165 
    166     # But we also preloaded everything along the way
    167     assert [c1, c2, c3] = p1.comments |> sort_by_id
    168     assert c1.author.id == uid1
    169     assert c2.author.id == uid1
    170     assert c3.author.id == uid2
    171 
    172     assert [c4] = p2.comments
    173     assert c4.author.id == uid2
    174   end
    175 
    176   test "preload has_one through" do
    177     %Post{id: pid1} = TestRepo.insert!(%Post{})
    178     %Post{id: pid2} = TestRepo.insert!(%Post{})
    179 
    180     %Permalink{id: lid1} = TestRepo.insert!(%Permalink{post_id: pid1, url: "1"})
    181     %Permalink{id: lid2} = TestRepo.insert!(%Permalink{post_id: pid2, url: "2"})
    182 
    183     %Comment{} = c1 = TestRepo.insert!(%Comment{post_id: pid1})
    184     %Comment{} = c2 = TestRepo.insert!(%Comment{post_id: pid1})
    185     %Comment{} = c3 = TestRepo.insert!(%Comment{post_id: pid2})
    186 
    187     [c1, c2, c3] = TestRepo.preload([c1, c2, c3], :post_permalink)
    188 
    189     # Through was preloaded
    190     assert c1.post.id == pid1
    191     assert c1.post.permalink.id == lid1
    192     assert c1.post_permalink.id == lid1
    193 
    194     assert c2.post.id == pid1
    195     assert c2.post.permalink.id == lid1
    196     assert c2.post_permalink.id == lid1
    197 
    198     assert c3.post.id == pid2
    199     assert c3.post.permalink.id == lid2
    200     assert c3.post_permalink.id == lid2
    201   end
    202 
    203   test "preload through with nil association" do
    204     %Comment{} = c = TestRepo.insert!(%Comment{post_id: nil})
    205 
    206     c = TestRepo.preload(c, [:post, :post_permalink])
    207     assert c.post == nil
    208     assert c.post_permalink == nil
    209 
    210     c = TestRepo.preload(c, [:post, :post_permalink])
    211     assert c.post == nil
    212     assert c.post_permalink == nil
    213   end
    214 
    215   test "preload has_many through-through" do
    216     %Post{id: pid1} = TestRepo.insert!(%Post{})
    217     %Post{id: pid2} = TestRepo.insert!(%Post{})
    218 
    219     %Permalink{} = l1 = TestRepo.insert!(%Permalink{post_id: pid1, url: "1"})
    220     %Permalink{} = l2 = TestRepo.insert!(%Permalink{post_id: pid2, url: "2"})
    221 
    222     %User{id: uid1} = TestRepo.insert!(%User{name: "foo"})
    223     %User{id: uid2} = TestRepo.insert!(%User{name: "bar"})
    224 
    225     %Comment{} = TestRepo.insert!(%Comment{post_id: pid1, author_id: uid1})
    226     %Comment{} = TestRepo.insert!(%Comment{post_id: pid1, author_id: uid1})
    227     %Comment{} = TestRepo.insert!(%Comment{post_id: pid1, author_id: uid2})
    228     %Comment{} = TestRepo.insert!(%Comment{post_id: pid2, author_id: uid2})
    229 
    230     # With assoc query
    231     [l1, l2] = TestRepo.preload([l1, l2], :post_comments_authors)
    232 
    233     # Through was preloaded
    234     [u1, u2] = l1.post_comments_authors |> sort_by_id
    235     assert u1.id == uid1
    236     assert u2.id == uid2
    237 
    238     [u2] = l2.post_comments_authors
    239     assert u2.id == uid2
    240 
    241     # But we also preloaded everything along the way
    242     assert l1.post.id == pid1
    243     assert l1.post.comments != []
    244 
    245     assert l2.post.id == pid2
    246     assert l2.post.comments != []
    247   end
    248 
    249   test "preload has_many through many_to_many" do
    250     %Post{} = p1 = TestRepo.insert!(%Post{})
    251     %Post{} = p2 = TestRepo.insert!(%Post{})
    252 
    253     %User{id: uid1} = TestRepo.insert!(%User{name: "foo"})
    254     %User{id: uid2} = TestRepo.insert!(%User{name: "bar"})
    255 
    256     TestRepo.insert_all "posts_users", [[post_id: p1.id, user_id: uid1],
    257                                         [post_id: p1.id, user_id: uid2],
    258                                         [post_id: p2.id, user_id: uid2]]
    259 
    260     %Comment{id: cid1} = TestRepo.insert!(%Comment{author_id: uid1})
    261     %Comment{id: cid2} = TestRepo.insert!(%Comment{author_id: uid1})
    262     %Comment{id: cid3} = TestRepo.insert!(%Comment{author_id: uid2})
    263     %Comment{id: cid4} = TestRepo.insert!(%Comment{author_id: uid2})
    264 
    265     [p1, p2] = TestRepo.preload([p1, p2], :users_comments)
    266 
    267     # Through was preloaded
    268     [c1, c2, c3, c4] = p1.users_comments |> sort_by_id
    269     assert c1.id == cid1
    270     assert c2.id == cid2
    271     assert c3.id == cid3
    272     assert c4.id == cid4
    273 
    274     [c3, c4] = p2.users_comments |> sort_by_id
    275     assert c3.id == cid3
    276     assert c4.id == cid4
    277 
    278     # But we also preloaded everything along the way
    279     assert [u1, u2] = p1.users |> sort_by_id
    280     assert u1.id == uid1
    281     assert u2.id == uid2
    282 
    283     assert [u2] = p2.users
    284     assert u2.id == uid2
    285   end
    286 
    287   ## Empties
    288 
    289   test "preload empty" do
    290     assert TestRepo.preload([], :anything_goes) == []
    291   end
    292 
    293   test "preload has_many with no associated entries" do
    294     p = TestRepo.insert!(%Post{title: "1"})
    295     p = TestRepo.preload(p, :comments)
    296 
    297     assert p.title == "1"
    298     assert p.comments == []
    299   end
    300 
    301   test "preload has_one with no associated entries" do
    302     p = TestRepo.insert!(%Post{title: "1"})
    303     p = TestRepo.preload(p, :permalink)
    304 
    305     assert p.title == "1"
    306     assert p.permalink == nil
    307   end
    308 
    309   test "preload belongs_to with no associated entry" do
    310     c = TestRepo.insert!(%Comment{text: "1"})
    311     c = TestRepo.preload(c, :post)
    312 
    313     assert c.text == "1"
    314     assert c.post == nil
    315   end
    316 
    317   test "preload many_to_many with no associated entries" do
    318     p = TestRepo.insert!(%Post{title: "1"})
    319     p = TestRepo.preload(p, :users)
    320 
    321     assert p.title == "1"
    322     assert p.users == []
    323   end
    324 
    325   ## With queries
    326 
    327   test "preload with function" do
    328     p1 = TestRepo.insert!(%Post{title: "1"})
    329     p2 = TestRepo.insert!(%Post{title: "2"})
    330     p3 = TestRepo.insert!(%Post{title: "3"})
    331 
    332     # We use the same text to expose bugs in preload sorting
    333     %Comment{id: cid1} = TestRepo.insert!(%Comment{text: "1", post_id: p1.id})
    334     %Comment{id: cid3} = TestRepo.insert!(%Comment{text: "2", post_id: p2.id})
    335     %Comment{id: cid2} = TestRepo.insert!(%Comment{text: "2", post_id: p1.id})
    336     %Comment{id: cid4} = TestRepo.insert!(%Comment{text: "3", post_id: p2.id})
    337 
    338     assert [pe3, pe1, pe2] = TestRepo.preload([p3, p1, p2],
    339                                               comments: fn _ -> TestRepo.all(Comment) end)
    340     assert [%Comment{id: ^cid1}, %Comment{id: ^cid2}] = pe1.comments
    341     assert [%Comment{id: ^cid3}, %Comment{id: ^cid4}] = pe2.comments
    342     assert [] = pe3.comments
    343   end
    344 
    345   test "preload many_to_many with function" do
    346     p1 = TestRepo.insert!(%Post{title: "1"})
    347     p2 = TestRepo.insert!(%Post{title: "2"})
    348     p3 = TestRepo.insert!(%Post{title: "3"})
    349 
    350     # We use the same name to expose bugs in preload sorting
    351     %User{id: uid1} = TestRepo.insert!(%User{name: "1"})
    352     %User{id: uid3} = TestRepo.insert!(%User{name: "2"})
    353     %User{id: uid2} = TestRepo.insert!(%User{name: "2"})
    354     %User{id: uid4} = TestRepo.insert!(%User{name: "3"})
    355 
    356     TestRepo.insert_all "posts_users", [[post_id: p1.id, user_id: uid1],
    357                                         [post_id: p1.id, user_id: uid2],
    358                                         [post_id: p2.id, user_id: uid3],
    359                                         [post_id: p2.id, user_id: uid4],
    360                                         [post_id: p3.id, user_id: uid1],
    361                                         [post_id: p3.id, user_id: uid4]]
    362 
    363     wrong_preloader = fn post_ids ->
    364       TestRepo.all(
    365         from u in User,
    366              join: pu in "posts_users",
    367              where: pu.post_id in ^post_ids and pu.user_id == u.id,
    368              order_by: u.id,
    369              select: map(u, [:id])
    370       )
    371     end
    372 
    373     assert_raise RuntimeError, ~r/invalid custom preload for `users` on `Ecto.Integration.Post`/, fn ->
    374       TestRepo.preload([p1, p2, p3], users: wrong_preloader)
    375     end
    376 
    377     right_preloader = fn post_ids ->
    378       TestRepo.all(
    379         from u in User,
    380              join: pu in "posts_users",
    381              where: pu.post_id in ^post_ids and pu.user_id == u.id,
    382              order_by: u.id,
    383              select: {pu.post_id, map(u, [:id])}
    384       )
    385     end
    386 
    387     [p1, p2, p3] = TestRepo.preload([p1, p2, p3], users: right_preloader)
    388     assert p1.users == [%{id: uid1}, %{id: uid2}]
    389     assert p2.users == [%{id: uid3}, %{id: uid4}]
    390     assert p3.users == [%{id: uid1}, %{id: uid4}]
    391   end
    392 
    393   test "preload with query" do
    394     p1 = TestRepo.insert!(%Post{title: "1"})
    395     p2 = TestRepo.insert!(%Post{title: "2"})
    396     p3 = TestRepo.insert!(%Post{title: "3"})
    397 
    398     # We use the same text to expose bugs in preload sorting
    399     %Comment{id: cid1} = TestRepo.insert!(%Comment{text: "1", post_id: p1.id})
    400     %Comment{id: cid3} = TestRepo.insert!(%Comment{text: "2", post_id: p2.id})
    401     %Comment{id: cid2} = TestRepo.insert!(%Comment{text: "2", post_id: p1.id})
    402     %Comment{id: cid4} = TestRepo.insert!(%Comment{text: "3", post_id: p2.id})
    403 
    404     assert %Ecto.Association.NotLoaded{} = p1.comments
    405 
    406     # With empty query
    407     assert [pe3, pe1, pe2] = TestRepo.preload([p3, p1, p2],
    408                                               comments: from(c in Comment, where: false))
    409     assert [] = pe1.comments
    410     assert [] = pe2.comments
    411     assert [] = pe3.comments
    412 
    413     # With custom select
    414     assert [pe3, pe1, pe2] = TestRepo.preload([p3, p1, p2],
    415                                               comments: from(c in Comment, select: c.id, order_by: c.id))
    416     assert [^cid1, ^cid2] = pe1.comments
    417     assert [^cid3, ^cid4] = pe2.comments
    418     assert [] = pe3.comments
    419 
    420     # With custom ordered query
    421     assert [pe3, pe1, pe2] = TestRepo.preload([p3, p1, p2],
    422                                               comments: from(c in Comment, order_by: [desc: c.text]))
    423     assert [%Comment{id: ^cid2}, %Comment{id: ^cid1}] = pe1.comments
    424     assert [%Comment{id: ^cid4}, %Comment{id: ^cid3}] = pe2.comments
    425     assert [] = pe3.comments
    426 
    427     # With custom ordered query with preload
    428     assert [pe3, pe1, pe2] = TestRepo.preload([p3, p1, p2],
    429                                               comments: {from(c in Comment, order_by: [desc: c.text]), :post})
    430     assert [%Comment{id: ^cid2} = c2, %Comment{id: ^cid1} = c1] = pe1.comments
    431     assert [%Comment{id: ^cid4} = c4, %Comment{id: ^cid3} = c3] = pe2.comments
    432     assert [] = pe3.comments
    433 
    434     assert c1.post.title == "1"
    435     assert c2.post.title == "1"
    436     assert c3.post.title == "2"
    437     assert c4.post.title == "2"
    438   end
    439 
    440   test "preload through with query" do
    441     %Post{id: pid1} = p1 = TestRepo.insert!(%Post{})
    442 
    443     u1 = TestRepo.insert!(%User{name: "foo"})
    444     u2 = TestRepo.insert!(%User{name: "bar"})
    445     u3 = TestRepo.insert!(%User{name: "baz"})
    446     u4 = TestRepo.insert!(%User{name: "norf"})
    447 
    448     %Comment{} = TestRepo.insert!(%Comment{post_id: pid1, author_id: u1.id})
    449     %Comment{} = TestRepo.insert!(%Comment{post_id: pid1, author_id: u1.id})
    450     %Comment{} = TestRepo.insert!(%Comment{post_id: pid1, author_id: u2.id})
    451     %Comment{} = TestRepo.insert!(%Comment{post_id: pid1, author_id: u3.id})
    452     %Comment{} = TestRepo.insert!(%Comment{post_id: pid1, author_id: u4.id})
    453 
    454     np1 = TestRepo.preload(p1, comments_authors: from(u in User, where: u.name == "foo"))
    455     assert np1.comments_authors == [u1]
    456 
    457     assert_raise ArgumentError, ~r/Ecto expected a map\/struct with the key `id` but got: \d+/, fn ->
    458       TestRepo.preload(p1, comments_authors: from(u in User, order_by: u.name, select: u.id))
    459     end
    460 
    461     # The subpreload order does not matter because the result is dictated by comments
    462     np1 = TestRepo.preload(p1, comments_authors: from(u in User, order_by: u.name, select: %{id: u.id}))
    463     assert np1.comments_authors ==
    464            [%{id: u1.id}, %{id: u2.id}, %{id: u3.id}, %{id: u4.id}]
    465   end
    466 
    467   ## With take
    468 
    469   test "preload with take" do
    470     p1 = TestRepo.insert!(%Post{title: "1"})
    471     p2 = TestRepo.insert!(%Post{title: "2"})
    472     _p = TestRepo.insert!(%Post{title: "3"})
    473 
    474     %Comment{id: cid1} = TestRepo.insert!(%Comment{text: "1", post_id: p1.id})
    475     %Comment{id: cid3} = TestRepo.insert!(%Comment{text: "2", post_id: p2.id})
    476     %Comment{id: cid2} = TestRepo.insert!(%Comment{text: "2", post_id: p1.id})
    477     %Comment{id: cid4} = TestRepo.insert!(%Comment{text: "3", post_id: p2.id})
    478 
    479     assert %Ecto.Association.NotLoaded{} = p1.comments
    480 
    481     posts = TestRepo.all(from Post, preload: [:comments], select: [:id, comments: [:id, :post_id]])
    482     [p1, p2, p3] = sort_by_id(posts)
    483     assert p1.title == nil
    484     assert p2.title == nil
    485     assert p3.title == nil
    486 
    487     assert [%{id: ^cid1, text: nil}, %{id: ^cid2, text: nil}] = sort_by_id(p1.comments)
    488     assert [%{id: ^cid3, text: nil}, %{id: ^cid4, text: nil}] = sort_by_id(p2.comments)
    489     assert [] = sort_by_id(p3.comments)
    490   end
    491 
    492   test "preload through with take" do
    493     %Post{id: pid1} = TestRepo.insert!(%Post{})
    494 
    495     %User{id: uid1} = TestRepo.insert!(%User{name: "foo"})
    496     %User{id: uid2} = TestRepo.insert!(%User{name: "bar"})
    497 
    498     %Comment{} = TestRepo.insert!(%Comment{post_id: pid1, author_id: uid1})
    499     %Comment{} = TestRepo.insert!(%Comment{post_id: pid1, author_id: uid1})
    500     %Comment{} = TestRepo.insert!(%Comment{post_id: pid1, author_id: uid2})
    501 
    502     [p1] = TestRepo.all from Post, preload: [:comments_authors], select: [:id, comments_authors: :id]
    503     [%{id: ^uid1, name: nil}, %{id: ^uid2, name: nil}] = p1.comments_authors |> sort_by_id
    504   end
    505 
    506   ## Nested
    507 
    508   test "preload many assocs" do
    509     p1 = TestRepo.insert!(%Post{title: "1"})
    510     p2 = TestRepo.insert!(%Post{title: "2"})
    511 
    512     assert [p2, p1] = TestRepo.preload([p2, p1], [:comments, :users])
    513     assert p1.comments == []
    514     assert p2.comments == []
    515     assert p1.users == []
    516     assert p2.users == []
    517   end
    518 
    519   test "preload nested" do
    520     p1 = TestRepo.insert!(%Post{title: "1"})
    521     p2 = TestRepo.insert!(%Post{title: "2"})
    522 
    523     TestRepo.insert!(%Comment{text: "1", post_id: p1.id})
    524     TestRepo.insert!(%Comment{text: "2", post_id: p1.id})
    525     TestRepo.insert!(%Comment{text: "3", post_id: p2.id})
    526     TestRepo.insert!(%Comment{text: "4", post_id: p2.id})
    527 
    528     assert [p2, p1] = TestRepo.preload([p2, p1], [comments: :post])
    529     assert [c1, c2] = p1.comments
    530     assert [c3, c4] = p2.comments
    531     assert p1.id == c1.post.id
    532     assert p1.id == c2.post.id
    533     assert p2.id == c3.post.id
    534     assert p2.id == c4.post.id
    535   end
    536 
    537   test "preload nested via custom query" do
    538     p1 = TestRepo.insert!(%Post{title: "1"})
    539     p2 = TestRepo.insert!(%Post{title: "2"})
    540 
    541     TestRepo.insert!(%Comment{text: "1", post_id: p1.id})
    542     TestRepo.insert!(%Comment{text: "2", post_id: p1.id})
    543     TestRepo.insert!(%Comment{text: "3", post_id: p2.id})
    544     TestRepo.insert!(%Comment{text: "4", post_id: p2.id})
    545 
    546     query = from(c in Comment, preload: :post, order_by: [desc: c.text])
    547     assert [p2, p1] = TestRepo.preload([p2, p1], comments: query)
    548     assert [c2, c1] = p1.comments
    549     assert [c4, c3] = p2.comments
    550     assert p1.id == c1.post.id
    551     assert p1.id == c2.post.id
    552     assert p2.id == c3.post.id
    553     assert p2.id == c4.post.id
    554   end
    555 
    556   test "custom preload_order" do
    557     post = TestRepo.insert!(%Post{users: [%User{name: "bar"}, %User{name: "foo"}], title: "1"})
    558 
    559     TestRepo.insert!(%Comment{text: "2", post_id: post.id})
    560     TestRepo.insert!(%Comment{text: "1", post_id: post.id})
    561 
    562     post = TestRepo.preload(post, [:ordered_comments, :ordered_users])
    563 
    564     # asc
    565     assert [%{text: "1"}, %{text: "2"}] = post.ordered_comments
    566 
    567     # desc
    568     assert [%{name: "foo"}, %{name: "bar"}] = post.ordered_users
    569   end
    570 
    571   ## Others
    572 
    573   @tag :invalid_prefix
    574   test "preload custom prefix from schema" do
    575     p = TestRepo.insert!(%Post{title: "1"})
    576     p = Ecto.put_meta(p, prefix: "this_surely_does_not_exist")
    577     # This preload should fail because it points to a prefix that does not exist
    578     assert catch_error(TestRepo.preload(p, [:comments]))
    579   end
    580 
    581   @tag :invalid_prefix
    582   test "preload custom prefix from options" do
    583     p = TestRepo.insert!(%Post{title: "1"})
    584     # This preload should fail because it points to a prefix that does not exist
    585     assert catch_error(TestRepo.preload(p, [:comments], prefix: "this_surely_does_not_exist"))
    586   end
    587 
    588   test "preload with binary_id" do
    589     c = TestRepo.insert!(%Custom{})
    590     u = TestRepo.insert!(%User{custom_id: c.bid})
    591 
    592     u = TestRepo.preload(u, :custom)
    593     assert u.custom.bid == c.bid
    594   end
    595 
    596   test "preload raises with association set but without id" do
    597     c1 = TestRepo.insert!(%Comment{text: "1"})
    598     u1 = TestRepo.insert!(%User{name: "name"})
    599     updated = %{c1 | author: u1, author_id: nil}
    600 
    601     assert ExUnit.CaptureLog.capture_log(fn ->
    602       assert TestRepo.preload(updated, [:author]).author == u1
    603     end) =~ ~r/its association key `author_id` is nil/
    604 
    605     assert TestRepo.preload(updated, [:author], force: true).author == nil
    606   end
    607 
    608   test "preload skips already loaded for cardinality one" do
    609     %Post{id: pid} = TestRepo.insert!(%Post{title: "1"})
    610 
    611     c1 = %Comment{id: cid1} = TestRepo.insert!(%Comment{text: "1", post_id: pid})
    612     c2 = %Comment{id: _cid} = TestRepo.insert!(%Comment{text: "2", post_id: nil})
    613 
    614     [c1, c2] = TestRepo.preload([c1, c2], :post)
    615     assert %Post{id: ^pid} = c1.post
    616     assert c2.post == nil
    617 
    618     [c1, c2] = TestRepo.preload([c1, c2], post: :comments)
    619     assert [%Comment{id: ^cid1}] = c1.post.comments
    620 
    621     TestRepo.update_all Post, set: [title: "0"]
    622     TestRepo.update_all Comment, set: [post_id: pid]
    623 
    624     # Preloading once again shouldn't change the result
    625     [c1, c2] = TestRepo.preload([c1, c2], :post)
    626     assert %Post{id: ^pid, title: "1", comments: [_|_]} = c1.post
    627     assert c2.post == nil
    628 
    629     [c1, c2] = TestRepo.preload([c1, %{c2 | post_id: pid}], :post, force: true)
    630     assert %Post{id: ^pid, title: "0", comments: %Ecto.Association.NotLoaded{}} = c1.post
    631     assert %Post{id: ^pid, title: "0", comments: %Ecto.Association.NotLoaded{}} = c2.post
    632   end
    633 
    634   test "preload skips already loaded for cardinality many" do
    635     p1 = TestRepo.insert!(%Post{title: "1"})
    636     p2 = TestRepo.insert!(%Post{title: "2"})
    637 
    638     %Comment{id: cid1} = TestRepo.insert!(%Comment{text: "1", post_id: p1.id})
    639     %Comment{id: cid2} = TestRepo.insert!(%Comment{text: "2", post_id: p2.id})
    640 
    641     [p1, p2] = TestRepo.preload([p1, p2], :comments)
    642     assert [%Comment{id: ^cid1}] = p1.comments
    643     assert [%Comment{id: ^cid2}] = p2.comments
    644 
    645     [p1, p2] = TestRepo.preload([p1, p2], comments: :post)
    646     assert hd(p1.comments).post.id == p1.id
    647     assert hd(p2.comments).post.id == p2.id
    648 
    649     TestRepo.update_all Comment, set: [text: "0"]
    650 
    651     # Preloading once again shouldn't change the result
    652     [p1, p2] = TestRepo.preload([p1, p2], :comments)
    653     assert [%Comment{id: ^cid1, text: "1", post: %Post{}}] = p1.comments
    654     assert [%Comment{id: ^cid2, text: "2", post: %Post{}}] = p2.comments
    655 
    656     [p1, p2] = TestRepo.preload([p1, p2], :comments, force: true)
    657     assert [%Comment{id: ^cid1, text: "0", post: %Ecto.Association.NotLoaded{}}] = p1.comments
    658     assert [%Comment{id: ^cid2, text: "0", post: %Ecto.Association.NotLoaded{}}] = p2.comments
    659   end
    660 
    661   test "preload keyword query" do
    662     p1 = TestRepo.insert!(%Post{title: "1"})
    663     p2 = TestRepo.insert!(%Post{title: "2"})
    664     TestRepo.insert!(%Post{title: "3"})
    665 
    666     %Comment{id: cid1} = TestRepo.insert!(%Comment{text: "1", post_id: p1.id})
    667     %Comment{id: cid2} = TestRepo.insert!(%Comment{text: "2", post_id: p1.id})
    668     %Comment{id: cid3} = TestRepo.insert!(%Comment{text: "3", post_id: p2.id})
    669     %Comment{id: cid4} = TestRepo.insert!(%Comment{text: "4", post_id: p2.id})
    670 
    671     # Regular query
    672     query = from(p in Post, preload: [:comments], select: p)
    673 
    674     assert [p1, p2, p3] = TestRepo.all(query) |> sort_by_id
    675     assert [%Comment{id: ^cid1}, %Comment{id: ^cid2}] = p1.comments |> sort_by_id
    676     assert [%Comment{id: ^cid3}, %Comment{id: ^cid4}] = p2.comments |> sort_by_id
    677     assert [] = p3.comments
    678 
    679     # Query with interpolated preload query
    680     query = from(p in Post, preload: [comments: ^from(c in Comment, where: false)], select: p)
    681 
    682     assert [p1, p2, p3] = TestRepo.all(query)
    683     assert [] = p1.comments
    684     assert [] = p2.comments
    685     assert [] = p3.comments
    686 
    687     # Now let's use an interpolated preload too
    688     comments = [:comments]
    689     query = from(p in Post, preload: ^comments, select: {0, [p], 1, 2})
    690 
    691     posts = TestRepo.all(query)
    692     [p1, p2, p3] = Enum.map(posts, fn {0, [p], 1, 2} -> p end) |> sort_by_id
    693 
    694     assert [%Comment{id: ^cid1}, %Comment{id: ^cid2}] = p1.comments |> sort_by_id
    695     assert [%Comment{id: ^cid3}, %Comment{id: ^cid4}] = p2.comments |> sort_by_id
    696     assert [] = p3.comments
    697   end
    698 
    699 
    700   test "preload belongs_to in embedded_schema" do
    701     %User{id: uid1} = TestRepo.insert!(%User{name: "1"})
    702     item = %Item{user_id: uid1}
    703 
    704     # Starts as not loaded
    705     assert %Ecto.Association.NotLoaded{} = item.user
    706 
    707     # Now we preload it
    708     item = TestRepo.preload(item, :user)
    709     assert %User{id: ^uid1} = item.user
    710   end
    711 
    712   describe "preload associations from nested embeds" do
    713     setup do
    714       %User{id: uid1} = TestRepo.insert!(%User{name: "1"})
    715       %User{id: uid2} = TestRepo.insert!(%User{name: "2"})
    716       %User{id: uid3} = TestRepo.insert!(%User{name: "3"})
    717       item1 = %Item{id: 1, user_id: uid1}
    718       item2 = %Item{id: 2, user_id: uid2}
    719       item3 = %Item{id: 3, user_id: uid3}
    720       order1 = %Order{items: [item1, item3, item2], item: item1}
    721       order2 = %Order{items: [], item: nil}
    722       order3 = %Order{items: nil, item: nil}
    723       order4 = %Order{items: [item1, item2], item: item2}
    724 
    725       [orders: [order1, order2, order3, order4]]
    726     end
    727 
    728     test "cannot preload embed without its associations", context do
    729       assert_raise ArgumentError, ~r/cannot preload embedded field/, fn ->
    730         TestRepo.preload(context.orders, :item)
    731       end
    732     end
    733 
    734     test "embeds_one", context do
    735       [nil | preloaded_orders] = [nil | context.orders] |> TestRepo.preload(item: :user)
    736 
    737       expected_item_user =
    738         Enum.map(context.orders, fn
    739           %{item: nil} -> {nil, nil}
    740           %{item: item} -> {item.id, item.user_id}
    741         end)
    742 
    743       actual_item_user =
    744         Enum.map(preloaded_orders, fn
    745           %{item: nil} -> {nil, nil}
    746           %{item: item} -> {item.id, item.user.id}
    747         end)
    748 
    749       assert expected_item_user == actual_item_user
    750     end
    751 
    752     test "embeds_many", context do
    753       [nil | preloaded_orders] = [nil | context.orders] |> TestRepo.preload(items: :user)
    754 
    755       expected_items_user =
    756         Enum.map(context.orders, fn
    757           %{items: nil} -> {nil, nil}
    758           %{items: items} -> Enum.map(items, & {&1.id, &1.user_id})
    759         end)
    760 
    761       actual_items_user =
    762         Enum.map(preloaded_orders, fn
    763           %{items: nil} -> {nil, nil}
    764           %{items: items} -> Enum.map(items, & {&1.id, &1.user.id})
    765         end)
    766 
    767       assert expected_items_user == actual_items_user
    768     end
    769   end
    770 
    771   defp sort_by_id(values) do
    772     Enum.sort_by(values, &(&1.id))
    773   end
    774 end