update
This commit is contained in:
865
phoenix/deps/ecto/integration_test/cases/assoc.exs
Normal file
865
phoenix/deps/ecto/integration_test/cases/assoc.exs
Normal file
@@ -0,0 +1,865 @@
|
||||
defmodule Ecto.Integration.AssocTest do
|
||||
use Ecto.Integration.Case, async: Application.compile_env(:ecto, :async_integration_tests, true)
|
||||
|
||||
alias Ecto.Integration.TestRepo
|
||||
import Ecto.Query
|
||||
|
||||
alias Ecto.Integration.Custom
|
||||
alias Ecto.Integration.Post
|
||||
alias Ecto.Integration.User
|
||||
alias Ecto.Integration.PostUser
|
||||
alias Ecto.Integration.Comment
|
||||
alias Ecto.Integration.Permalink
|
||||
|
||||
test "has_many assoc" do
|
||||
p1 = TestRepo.insert!(%Post{title: "1"})
|
||||
p2 = TestRepo.insert!(%Post{title: "2"})
|
||||
|
||||
%Comment{id: cid1} = TestRepo.insert!(%Comment{text: "1", post_id: p1.id})
|
||||
%Comment{id: cid2} = TestRepo.insert!(%Comment{text: "2", post_id: p1.id})
|
||||
%Comment{id: cid3} = TestRepo.insert!(%Comment{text: "3", post_id: p2.id})
|
||||
|
||||
[c1, c2] = TestRepo.all Ecto.assoc(p1, :comments)
|
||||
assert c1.id == cid1
|
||||
assert c2.id == cid2
|
||||
|
||||
[c1, c2, c3] = TestRepo.all Ecto.assoc([p1, p2], :comments)
|
||||
assert c1.id == cid1
|
||||
assert c2.id == cid2
|
||||
assert c3.id == cid3
|
||||
end
|
||||
|
||||
test "has_one assoc" do
|
||||
p1 = TestRepo.insert!(%Post{title: "1"})
|
||||
p2 = TestRepo.insert!(%Post{title: "2"})
|
||||
|
||||
%Permalink{id: lid1} = TestRepo.insert!(%Permalink{url: "1", post_id: p1.id})
|
||||
%Permalink{} = TestRepo.insert!(%Permalink{url: "2"})
|
||||
%Permalink{id: lid3} = TestRepo.insert!(%Permalink{url: "3", post_id: p2.id})
|
||||
|
||||
[l1, l3] = TestRepo.all Ecto.assoc([p1, p2], :permalink)
|
||||
assert l1.id == lid1
|
||||
assert l3.id == lid3
|
||||
end
|
||||
|
||||
test "belongs_to assoc" do
|
||||
%Post{id: pid1} = TestRepo.insert!(%Post{title: "1"})
|
||||
%Post{id: pid2} = TestRepo.insert!(%Post{title: "2"})
|
||||
|
||||
l1 = TestRepo.insert!(%Permalink{url: "1", post_id: pid1})
|
||||
l2 = TestRepo.insert!(%Permalink{url: "2"})
|
||||
l3 = TestRepo.insert!(%Permalink{url: "3", post_id: pid2})
|
||||
|
||||
assert [p1, p2] = TestRepo.all Ecto.assoc([l1, l2, l3], :post)
|
||||
assert p1.id == pid1
|
||||
assert p2.id == pid2
|
||||
end
|
||||
|
||||
test "has_many through assoc" do
|
||||
p1 = TestRepo.insert!(%Post{})
|
||||
p2 = TestRepo.insert!(%Post{})
|
||||
|
||||
u1 = TestRepo.insert!(%User{name: "zzz"})
|
||||
u2 = TestRepo.insert!(%User{name: "aaa"})
|
||||
|
||||
%Comment{} = TestRepo.insert!(%Comment{post_id: p1.id, author_id: u1.id})
|
||||
%Comment{} = TestRepo.insert!(%Comment{post_id: p1.id, author_id: u1.id})
|
||||
%Comment{} = TestRepo.insert!(%Comment{post_id: p1.id, author_id: u2.id})
|
||||
%Comment{} = TestRepo.insert!(%Comment{post_id: p2.id, author_id: u2.id})
|
||||
|
||||
query = Ecto.assoc([p1, p2], :comments_authors) |> order_by([a], a.name)
|
||||
assert [^u2, ^u1] = TestRepo.all(query)
|
||||
|
||||
# Dynamic through
|
||||
query = Ecto.assoc([p1, p2], [:comments, :author]) |> order_by([a], a.name)
|
||||
assert [^u2, ^u1] = TestRepo.all(query)
|
||||
end
|
||||
|
||||
@tag :on_replace_nilify
|
||||
test "has_many through-through assoc leading" do
|
||||
p1 = TestRepo.insert!(%Post{})
|
||||
p2 = TestRepo.insert!(%Post{})
|
||||
|
||||
u1 = TestRepo.insert!(%User{})
|
||||
u2 = TestRepo.insert!(%User{})
|
||||
|
||||
pl1 = TestRepo.insert!(%Permalink{user_id: u1.id, url: "zzz"})
|
||||
pl2 = TestRepo.insert!(%Permalink{user_id: u2.id, url: "aaa"})
|
||||
|
||||
%Comment{} = TestRepo.insert!(%Comment{post_id: p1.id, author_id: u1.id})
|
||||
%Comment{} = TestRepo.insert!(%Comment{post_id: p1.id, author_id: u1.id})
|
||||
%Comment{} = TestRepo.insert!(%Comment{post_id: p1.id, author_id: u2.id})
|
||||
%Comment{} = TestRepo.insert!(%Comment{post_id: p2.id, author_id: u2.id})
|
||||
|
||||
query = Ecto.assoc([p1, p2], :comments_authors_permalinks) |> order_by([p], p.url)
|
||||
assert [^pl2, ^pl1] = TestRepo.all(query)
|
||||
|
||||
# Dynamic through
|
||||
query = Ecto.assoc([p1, p2], [:comments, :author, :permalink]) |> order_by([p], p.url)
|
||||
assert [^pl2, ^pl1] = TestRepo.all(query)
|
||||
end
|
||||
|
||||
test "has_many through-through assoc trailing" do
|
||||
p1 = TestRepo.insert!(%Post{})
|
||||
u1 = TestRepo.insert!(%User{})
|
||||
pl1 = TestRepo.insert!(%Permalink{user_id: u1.id, post_id: p1.id})
|
||||
|
||||
%Comment{} = TestRepo.insert!(%Comment{post_id: p1.id, author_id: u1.id})
|
||||
|
||||
query = Ecto.assoc([pl1], :post_comments_authors)
|
||||
assert [^u1] = TestRepo.all(query)
|
||||
|
||||
# Dynamic through
|
||||
query = Ecto.assoc([pl1], [:post, :comments, :author])
|
||||
assert [^u1] = TestRepo.all(query)
|
||||
end
|
||||
|
||||
test "has_many through has_many, many_to_many and has_many" do
|
||||
user1 = %User{id: uid1} = TestRepo.insert!(%User{name: "Gabriel"})
|
||||
%User{id: uid2} = TestRepo.insert!(%User{name: "Isadora"})
|
||||
%User{id: uid3} = TestRepo.insert!(%User{name: "Joey Mush"})
|
||||
|
||||
p1 = TestRepo.insert!(%Post{title: "p1", author_id: uid1})
|
||||
p2 = TestRepo.insert!(%Post{title: "p2", author_id: uid2})
|
||||
p3 = TestRepo.insert!(%Post{title: "p3", author_id: uid2})
|
||||
TestRepo.insert!(%Post{title: "p4", author_id: uid3})
|
||||
|
||||
TestRepo.insert_all "posts_users", [[post_id: p1.id, user_id: uid1],
|
||||
[post_id: p1.id, user_id: uid2],
|
||||
[post_id: p2.id, user_id: uid3]]
|
||||
|
||||
[pid1, pid2, pid3] =
|
||||
Ecto.assoc(user1, :related_2nd_order_posts)
|
||||
|> TestRepo.all()
|
||||
|> Enum.map(fn %Post{id: id} -> id end)
|
||||
|> Enum.sort()
|
||||
|
||||
assert p1.id == pid1
|
||||
assert p2.id == pid2
|
||||
assert p3.id == pid3
|
||||
end
|
||||
|
||||
test "has_many through has_many, belongs_to and a nested has through" do
|
||||
user1 = TestRepo.insert!(%User{name: "Gabriel"})
|
||||
user2 = TestRepo.insert!(%User{name: "Isadora"})
|
||||
user3 = TestRepo.insert!(%User{name: "Joey"})
|
||||
|
||||
post1 = TestRepo.insert!(%Post{title: "p1"})
|
||||
post2 = TestRepo.insert!(%Post{title: "p2"})
|
||||
|
||||
TestRepo.insert!(%Comment{author_id: user1.id, text: "c1", post_id: post1.id})
|
||||
TestRepo.insert!(%Comment{author_id: user2.id, text: "c2", post_id: post1.id})
|
||||
TestRepo.insert!(%Comment{author_id: user3.id, text: "c3", post_id: post2.id})
|
||||
|
||||
[u1_id, u2_id] =
|
||||
Ecto.assoc(user1, :co_commenters)
|
||||
|> TestRepo.all()
|
||||
|> Enum.map(fn %User{id: id} -> id end)
|
||||
|> Enum.sort()
|
||||
|
||||
assert u1_id == user1.id
|
||||
assert u2_id == user2.id
|
||||
end
|
||||
|
||||
test "has_many through two many_to_many associations" do
|
||||
user1 = %User{id: uid1} = TestRepo.insert!(%User{name: "Gabriel"})
|
||||
%User{id: uid2} = TestRepo.insert!(%User{name: "Isadora"})
|
||||
%User{id: uid3} = TestRepo.insert!(%User{name: "Joey Mush"})
|
||||
|
||||
p1 = TestRepo.insert!(%Post{title: "p1", author_id: uid1})
|
||||
TestRepo.insert!(%Post{title: "p2", author_id: uid2})
|
||||
p3 = TestRepo.insert!(%Post{title: "p3", author_id: uid2})
|
||||
p4 = TestRepo.insert!(%Post{title: "p4", author_id: uid3})
|
||||
|
||||
TestRepo.insert_all "posts_users", [[post_id: p3.id, user_id: uid1],
|
||||
[post_id: p3.id, user_id: uid2],
|
||||
[post_id: p1.id, user_id: uid3]]
|
||||
|
||||
TestRepo.insert!(%PostUser{post_id: p1.id, user_id: uid2})
|
||||
TestRepo.insert!(%PostUser{post_id: p3.id, user_id: uid1})
|
||||
TestRepo.insert!(%PostUser{post_id: p3.id, user_id: uid2})
|
||||
TestRepo.insert!(%PostUser{post_id: p4.id, user_id: uid3})
|
||||
|
||||
[u1, u2] =
|
||||
Ecto.assoc(user1, :users_through_schema_posts)
|
||||
|> TestRepo.all()
|
||||
|> Enum.map(fn %User{id: id} -> id end)
|
||||
|> Enum.sort()
|
||||
|
||||
assert uid1 == u1
|
||||
assert uid2 == u2
|
||||
end
|
||||
|
||||
test "has_many through with where" do
|
||||
post1 = TestRepo.insert!(%Post{title: "p1"})
|
||||
post2 = TestRepo.insert!(%Post{title: "p2"})
|
||||
post3 = TestRepo.insert!(%Post{title: "p3"})
|
||||
|
||||
author = TestRepo.insert!(%User{name: "john"})
|
||||
|
||||
TestRepo.insert!(%Comment{text: "1", lock_version: 1, post_id: post1.id, author_id: author.id})
|
||||
TestRepo.insert!(%Comment{text: "2", lock_version: 2, post_id: post2.id, author_id: author.id})
|
||||
TestRepo.insert!(%Comment{text: "3", lock_version: 2, post_id: post3.id, author_id: author.id})
|
||||
|
||||
[p2, p3] = Ecto.assoc(author, :v2_comments_posts) |> TestRepo.all() |> Enum.sort_by(&(&1.id))
|
||||
assert p2.id == post2.id
|
||||
assert p3.id == post3.id
|
||||
end
|
||||
|
||||
test "many_to_many assoc" do
|
||||
p1 = TestRepo.insert!(%Post{title: "1"})
|
||||
p2 = TestRepo.insert!(%Post{title: "2"})
|
||||
p3 = TestRepo.insert!(%Post{title: "3"})
|
||||
|
||||
%User{id: uid1} = TestRepo.insert!(%User{name: "john"})
|
||||
%User{id: uid2} = TestRepo.insert!(%User{name: "mary"})
|
||||
|
||||
TestRepo.insert_all "posts_users", [[post_id: p1.id, user_id: uid1],
|
||||
[post_id: p1.id, user_id: uid2],
|
||||
[post_id: p2.id, user_id: uid2]]
|
||||
|
||||
[u1, u2] = TestRepo.all Ecto.assoc([p1], :users)
|
||||
assert u1.id == uid1
|
||||
assert u2.id == uid2
|
||||
|
||||
[u2] = TestRepo.all Ecto.assoc([p2], :users)
|
||||
assert u2.id == uid2
|
||||
[] = TestRepo.all Ecto.assoc([p3], :users)
|
||||
|
||||
[u1, u2, u2] = TestRepo.all Ecto.assoc([p1, p2, p3], :users)
|
||||
assert u1.id == uid1
|
||||
assert u2.id == uid2
|
||||
end
|
||||
|
||||
## Changesets
|
||||
|
||||
test "has_one changeset assoc (on_replace: :delete)" do
|
||||
# Insert new
|
||||
changeset =
|
||||
%Post{title: "1"}
|
||||
|> Ecto.Changeset.change
|
||||
|> Ecto.Changeset.put_assoc(:permalink, %Permalink{url: "1"})
|
||||
post = TestRepo.insert!(changeset)
|
||||
assert post.permalink.id
|
||||
assert post.permalink.post_id == post.id
|
||||
assert post.permalink.url == "1"
|
||||
post = TestRepo.get!(from(Post, preload: [:permalink]), post.id)
|
||||
assert post.permalink.url == "1"
|
||||
|
||||
# Replace with new
|
||||
changeset =
|
||||
post
|
||||
|> Ecto.Changeset.change
|
||||
|> Ecto.Changeset.put_assoc(:permalink, %Permalink{url: "2"})
|
||||
post = TestRepo.update!(changeset)
|
||||
assert post.permalink.id
|
||||
assert post.permalink.post_id == post.id
|
||||
assert post.permalink.url == "2"
|
||||
post = TestRepo.get!(from(Post, preload: [:permalink]), post.id)
|
||||
assert post.permalink.url == "2"
|
||||
|
||||
# Replacing with existing
|
||||
existing = TestRepo.insert!(%Permalink{url: "3"})
|
||||
changeset =
|
||||
post
|
||||
|> Ecto.Changeset.change
|
||||
|> Ecto.Changeset.put_assoc(:permalink, existing)
|
||||
post = TestRepo.update!(changeset)
|
||||
assert post.permalink.id
|
||||
assert post.permalink.post_id == post.id
|
||||
assert post.permalink.url == "3"
|
||||
post = TestRepo.get!(from(Post, preload: [:permalink]), post.id)
|
||||
assert post.permalink.url == "3"
|
||||
|
||||
# Replacing with nil (on_replace: :delete)
|
||||
changeset =
|
||||
post
|
||||
|> Ecto.Changeset.change
|
||||
|> Ecto.Changeset.put_assoc(:permalink, nil)
|
||||
post = TestRepo.update!(changeset)
|
||||
refute post.permalink
|
||||
post = TestRepo.get!(from(Post, preload: [:permalink]), post.id)
|
||||
refute post.permalink
|
||||
|
||||
assert [0] == TestRepo.all(from(p in Permalink, select: count(p.id)))
|
||||
end
|
||||
|
||||
test "has_one changeset assoc (on_replace: :delete_if_exists)" do
|
||||
permalink = TestRepo.insert!(%Permalink{url: "1"})
|
||||
post = TestRepo.insert!(%Post{title: "1", permalink: permalink, force_permalink: permalink})
|
||||
TestRepo.delete!(permalink)
|
||||
|
||||
assert_raise Ecto.StaleEntryError, fn ->
|
||||
post
|
||||
|> Ecto.Changeset.change()
|
||||
|> Ecto.Changeset.put_assoc(:permalink, nil)
|
||||
|> TestRepo.update!()
|
||||
end
|
||||
|
||||
post =
|
||||
post
|
||||
|> Ecto.Changeset.change()
|
||||
|> Ecto.Changeset.put_assoc(:force_permalink, nil)
|
||||
|> TestRepo.update!()
|
||||
|
||||
assert post.force_permalink == nil
|
||||
end
|
||||
|
||||
@tag :on_replace_nilify
|
||||
test "has_one changeset assoc (on_replace: :nilify)" do
|
||||
# Insert new
|
||||
changeset =
|
||||
%User{name: "1"}
|
||||
|> Ecto.Changeset.change
|
||||
|> Ecto.Changeset.put_assoc(:permalink, %Permalink{url: "1"})
|
||||
user = TestRepo.insert!(changeset)
|
||||
assert user.permalink.id
|
||||
assert user.permalink.user_id == user.id
|
||||
assert user.permalink.url == "1"
|
||||
user = TestRepo.get!(from(User, preload: [:permalink]), user.id)
|
||||
assert user.permalink.url == "1"
|
||||
|
||||
# Replace with new
|
||||
changeset =
|
||||
user
|
||||
|> Ecto.Changeset.change
|
||||
|> Ecto.Changeset.put_assoc(:permalink, %Permalink{url: "2"})
|
||||
user = TestRepo.update!(changeset)
|
||||
assert user.permalink.id
|
||||
assert user.permalink.user_id == user.id
|
||||
assert user.permalink.url == "2"
|
||||
user = TestRepo.get!(from(User, preload: [:permalink]), user.id)
|
||||
assert user.permalink.url == "2"
|
||||
|
||||
# Replacing with nil (on_replace: :nilify)
|
||||
changeset =
|
||||
user
|
||||
|> Ecto.Changeset.change
|
||||
|> Ecto.Changeset.put_assoc(:permalink, nil)
|
||||
user = TestRepo.update!(changeset)
|
||||
refute user.permalink
|
||||
user = TestRepo.get!(from(User, preload: [:permalink]), user.id)
|
||||
refute user.permalink
|
||||
|
||||
assert [2] == TestRepo.all(from(p in Permalink, select: count(p.id)))
|
||||
end
|
||||
|
||||
@tag :on_replace_update
|
||||
test "has_one changeset assoc (on_replace: :update)" do
|
||||
# Insert new
|
||||
changeset =
|
||||
%Post{title: "1"}
|
||||
|> Ecto.Changeset.change
|
||||
|> Ecto.Changeset.put_assoc(:update_permalink, %Permalink{url: "1"})
|
||||
post = TestRepo.insert!(changeset)
|
||||
assert post.update_permalink.id
|
||||
assert post.update_permalink.post_id == post.id
|
||||
assert post.update_permalink.url == "1"
|
||||
post = TestRepo.get!(from(Post, preload: [:update_permalink]), post.id)
|
||||
assert post.update_permalink.url == "1"
|
||||
|
||||
perma = post.update_permalink
|
||||
|
||||
# Put on update
|
||||
changeset =
|
||||
post
|
||||
|> Ecto.Changeset.change()
|
||||
|> Ecto.Changeset.put_assoc(:update_permalink, %{url: "2"})
|
||||
post = TestRepo.update!(changeset)
|
||||
assert post.update_permalink.id == perma.id
|
||||
assert post.update_permalink.post_id == post.id
|
||||
assert post.update_permalink.url == "2"
|
||||
post = TestRepo.get!(from(Post, preload: [:update_permalink]), post.id)
|
||||
assert post.update_permalink.url == "2"
|
||||
|
||||
# Cast on update
|
||||
changeset =
|
||||
post
|
||||
|> Ecto.Changeset.cast(%{update_permalink: %{url: "3"}}, [])
|
||||
|> Ecto.Changeset.cast_assoc(:update_permalink)
|
||||
post = TestRepo.update!(changeset)
|
||||
assert post.update_permalink.id == perma.id
|
||||
assert post.update_permalink.post_id == post.id
|
||||
assert post.update_permalink.url == "3"
|
||||
post = TestRepo.get!(from(Post, preload: [:update_permalink]), post.id)
|
||||
assert post.update_permalink.url == "3"
|
||||
|
||||
# Replace with new struct
|
||||
assert_raise RuntimeError, ~r"you are only allowed\sto update the existing entry", fn ->
|
||||
post
|
||||
|> Ecto.Changeset.change()
|
||||
|> Ecto.Changeset.put_assoc(:update_permalink, %Permalink{url: "4"})
|
||||
end
|
||||
|
||||
# Replace with existing struct
|
||||
assert_raise RuntimeError, ~r"you are only allowed\sto update the existing entry", fn ->
|
||||
post
|
||||
|> Ecto.Changeset.change()
|
||||
|> Ecto.Changeset.put_assoc(:update_permalink, TestRepo.insert!(%Permalink{url: "5"}))
|
||||
end
|
||||
|
||||
# Replacing with nil (on_replace: :update)
|
||||
changeset =
|
||||
post
|
||||
|> Ecto.Changeset.change
|
||||
|> Ecto.Changeset.put_assoc(:update_permalink, nil)
|
||||
post = TestRepo.update!(changeset)
|
||||
refute post.update_permalink
|
||||
post = TestRepo.get!(from(Post, preload: [:update_permalink]), post.id)
|
||||
refute post.update_permalink
|
||||
|
||||
assert [2] == TestRepo.all(from(p in Permalink, select: count(p.id)))
|
||||
end
|
||||
|
||||
test "has_many changeset assoc (on_replace: :delete)" do
|
||||
c1 = TestRepo.insert! %Comment{text: "1"}
|
||||
c2 = %Comment{text: "2"}
|
||||
|
||||
# Inserting
|
||||
changeset =
|
||||
%Post{title: "1"}
|
||||
|> Ecto.Changeset.change
|
||||
|> Ecto.Changeset.put_assoc(:comments, [c2])
|
||||
post = TestRepo.insert!(changeset)
|
||||
[c2] = post.comments
|
||||
assert c2.id
|
||||
assert c2.post_id == post.id
|
||||
post = TestRepo.get!(from(Post, preload: [:comments]), post.id)
|
||||
[c2] = post.comments
|
||||
assert c2.text == "2"
|
||||
|
||||
# Updating
|
||||
changeset =
|
||||
post
|
||||
|> Ecto.Changeset.change
|
||||
|> Ecto.Changeset.put_assoc(:comments, [Ecto.Changeset.change(c1, text: "11"),
|
||||
Ecto.Changeset.change(c2, text: "22")])
|
||||
post = TestRepo.update!(changeset)
|
||||
[c1, _c2] = post.comments |> Enum.sort_by(&(&1.id))
|
||||
assert c1.id
|
||||
assert c1.post_id == post.id
|
||||
post = TestRepo.get!(from(Post, preload: [:comments]), post.id)
|
||||
[c1, c2] = post.comments |> Enum.sort_by(&(&1.id))
|
||||
assert c1.text == "11"
|
||||
assert c2.text == "22"
|
||||
|
||||
# Replacing (on_replace: :delete)
|
||||
changeset =
|
||||
post
|
||||
|> Ecto.Changeset.change
|
||||
|> Ecto.Changeset.put_assoc(:comments, [])
|
||||
post = TestRepo.update!(changeset)
|
||||
assert post.comments == []
|
||||
post = TestRepo.get!(from(Post, preload: [:comments]), post.id)
|
||||
assert post.comments == []
|
||||
|
||||
assert [0] == TestRepo.all(from(c in Comment, select: count(c.id)))
|
||||
end
|
||||
|
||||
test "has_many changeset assoc (on_replace: :delete_if_exists)" do
|
||||
comment = TestRepo.insert!(%Comment{text: "1"})
|
||||
post = TestRepo.insert!(%Post{title: "1", comments: [comment], force_comments: [comment]})
|
||||
|
||||
TestRepo.delete!(comment)
|
||||
|
||||
assert_raise Ecto.StaleEntryError, fn ->
|
||||
post
|
||||
|> Ecto.Changeset.change()
|
||||
|> Ecto.Changeset.put_assoc(:comments, [])
|
||||
|> TestRepo.update!()
|
||||
end
|
||||
|
||||
post =
|
||||
post
|
||||
|> Ecto.Changeset.change()
|
||||
|> Ecto.Changeset.put_assoc(:force_comments, [])
|
||||
|> TestRepo.update!()
|
||||
|
||||
assert post.force_comments == []
|
||||
end
|
||||
|
||||
test "has_many changeset assoc (on_replace: :nilify)" do
|
||||
c1 = TestRepo.insert! %Comment{text: "1"}
|
||||
c2 = %Comment{text: "2"}
|
||||
|
||||
# Inserting
|
||||
changeset =
|
||||
%User{name: "1"}
|
||||
|> Ecto.Changeset.change
|
||||
|> Ecto.Changeset.put_assoc(:comments, [c1, c2])
|
||||
user = TestRepo.insert!(changeset)
|
||||
[c1, c2] = user.comments
|
||||
assert c1.id
|
||||
assert c1.author_id == user.id
|
||||
assert c2.id
|
||||
assert c2.author_id == user.id
|
||||
user = TestRepo.get!(from(User, preload: [:comments]), user.id)
|
||||
[c1, c2] = user.comments
|
||||
assert c1.text == "1"
|
||||
assert c2.text == "2"
|
||||
|
||||
# Replacing (on_replace: :nilify)
|
||||
changeset =
|
||||
user
|
||||
|> Ecto.Changeset.change
|
||||
|> Ecto.Changeset.put_assoc(:comments, [])
|
||||
user = TestRepo.update!(changeset)
|
||||
assert user.comments == []
|
||||
user = TestRepo.get!(from(User, preload: [:comments]), user.id)
|
||||
assert user.comments == []
|
||||
|
||||
assert [2] == TestRepo.all(from(c in Comment, select: count(c.id)))
|
||||
end
|
||||
|
||||
test "many_to_many changeset assoc" do
|
||||
u1 = TestRepo.insert! %User{name: "1"}
|
||||
u2 = %User{name: "2"}
|
||||
|
||||
# Inserting
|
||||
changeset =
|
||||
%Post{title: "1"}
|
||||
|> Ecto.Changeset.change
|
||||
|> Ecto.Changeset.put_assoc(:users, [u2])
|
||||
post = TestRepo.insert!(changeset)
|
||||
[u2] = post.users
|
||||
assert u2.id
|
||||
post = TestRepo.get!(from(Post, preload: [:users]), post.id)
|
||||
[u2] = post.users
|
||||
assert u2.name == "2"
|
||||
|
||||
assert [1] == TestRepo.all(from(j in "posts_users", select: count(j.post_id)))
|
||||
|
||||
# Updating
|
||||
changeset =
|
||||
post
|
||||
|> Ecto.Changeset.change
|
||||
|> Ecto.Changeset.put_assoc(:users, [Ecto.Changeset.change(u1, name: "11"),
|
||||
Ecto.Changeset.change(u2, name: "22")])
|
||||
post = TestRepo.update!(changeset)
|
||||
[u1, _u2] = post.users |> Enum.sort_by(&(&1.id))
|
||||
assert u1.id
|
||||
post = TestRepo.get!(from(Post, preload: [:users]), post.id)
|
||||
[u1, u2] = post.users |> Enum.sort_by(&(&1.id))
|
||||
assert u1.name == "11"
|
||||
assert u2.name == "22"
|
||||
|
||||
assert [2] == TestRepo.all(from(j in "posts_users", select: count(j.post_id)))
|
||||
|
||||
# Replacing (on_replace: :delete)
|
||||
changeset =
|
||||
post
|
||||
|> Ecto.Changeset.change
|
||||
|> Ecto.Changeset.put_assoc(:users, [])
|
||||
post = TestRepo.update!(changeset)
|
||||
assert post.users == []
|
||||
post = TestRepo.get!(from(Post, preload: [:users]), post.id)
|
||||
assert post.users == []
|
||||
|
||||
assert [0] == TestRepo.all(from(j in "posts_users", select: count(j.post_id)))
|
||||
assert [2] == TestRepo.all(from(c in User, select: count(c.id)))
|
||||
end
|
||||
|
||||
test "many_to_many changeset assoc with schema" do
|
||||
p1 = TestRepo.insert! %Post{title: "1"}
|
||||
p2 = %Post{title: "2"}
|
||||
|
||||
# Inserting
|
||||
changeset =
|
||||
%User{name: "1"}
|
||||
|> Ecto.Changeset.change
|
||||
|> Ecto.Changeset.put_assoc(:schema_posts, [p2])
|
||||
user = TestRepo.insert!(changeset)
|
||||
[p2] = user.schema_posts
|
||||
assert p2.id
|
||||
user = TestRepo.get!(from(User, preload: [:schema_posts]), user.id)
|
||||
[p2] = user.schema_posts
|
||||
assert p2.title == "2"
|
||||
|
||||
[up2] = TestRepo.all(PostUser) |> Enum.sort_by(&(&1.id))
|
||||
assert up2.post_id == p2.id
|
||||
assert up2.user_id == user.id
|
||||
assert up2.inserted_at
|
||||
assert up2.updated_at
|
||||
|
||||
# Updating
|
||||
changeset =
|
||||
user
|
||||
|> Ecto.Changeset.change
|
||||
|> Ecto.Changeset.put_assoc(:schema_posts, [Ecto.Changeset.change(p1, title: "11"),
|
||||
Ecto.Changeset.change(p2, title: "22")])
|
||||
user = TestRepo.update!(changeset)
|
||||
[p1, _p2] = user.schema_posts |> Enum.sort_by(&(&1.id))
|
||||
assert p1.id
|
||||
user = TestRepo.get!(from(User, preload: [:schema_posts]), user.id)
|
||||
[p1, p2] = user.schema_posts |> Enum.sort_by(&(&1.id))
|
||||
assert p1.title == "11"
|
||||
assert p2.title == "22"
|
||||
|
||||
[_up2, up1] = TestRepo.all(PostUser) |> Enum.sort_by(&(&1.id))
|
||||
assert up1.post_id == p1.id
|
||||
assert up1.user_id == user.id
|
||||
assert up1.inserted_at
|
||||
assert up1.updated_at
|
||||
end
|
||||
|
||||
test "many_to_many changeset assoc with self-referential binary_id" do
|
||||
assoc_custom = TestRepo.insert!(%Custom{uuid: Ecto.UUID.generate()})
|
||||
custom = TestRepo.insert!(%Custom{customs: [assoc_custom]})
|
||||
|
||||
custom = Custom |> TestRepo.get!(custom.bid) |> TestRepo.preload(:customs)
|
||||
assert [_] = custom.customs
|
||||
|
||||
custom =
|
||||
custom
|
||||
|> Ecto.Changeset.change(%{})
|
||||
|> Ecto.Changeset.put_assoc(:customs, [])
|
||||
|> TestRepo.update!
|
||||
assert [] = custom.customs
|
||||
|
||||
custom = Custom |> TestRepo.get!(custom.bid) |> TestRepo.preload(:customs)
|
||||
assert [] = custom.customs
|
||||
end
|
||||
|
||||
@tag :unique_constraint
|
||||
test "has_many changeset assoc with constraints" do
|
||||
author = TestRepo.insert!(%User{name: "john doe"})
|
||||
p1 = TestRepo.insert!(%Post{title: "hello", author_id: author.id})
|
||||
TestRepo.insert!(%Post{title: "world", author_id: author.id})
|
||||
|
||||
# Asserts that `unique_constraint` for `uuid` exists
|
||||
assert_raise Ecto.ConstraintError, fn ->
|
||||
TestRepo.insert!(%Post{title: "another", author_id: author.id, uuid: p1.uuid})
|
||||
end
|
||||
|
||||
author = TestRepo.preload author, [:posts]
|
||||
posts_params = Enum.map author.posts, fn %Post{uuid: u} ->
|
||||
%{uuid: u, title: "fresh"}
|
||||
end
|
||||
|
||||
# This will only work if we delete before performing inserts
|
||||
changeset =
|
||||
author
|
||||
|> Ecto.Changeset.cast(%{"posts" => posts_params}, ~w())
|
||||
|> Ecto.Changeset.cast_assoc(:posts)
|
||||
author = TestRepo.update! changeset
|
||||
assert Enum.map(author.posts, &(&1.title)) == ["fresh", "fresh"]
|
||||
end
|
||||
|
||||
test "belongs_to changeset assoc" do
|
||||
# Insert new
|
||||
changeset =
|
||||
%Permalink{url: "1"}
|
||||
|> Ecto.Changeset.change
|
||||
|> Ecto.Changeset.put_assoc(:post, %Post{title: "1"})
|
||||
perma = TestRepo.insert!(changeset)
|
||||
post = perma.post
|
||||
assert perma.post_id
|
||||
assert perma.post_id == post.id
|
||||
assert perma.post.title == "1"
|
||||
|
||||
# Replace with new
|
||||
changeset =
|
||||
perma
|
||||
|> Ecto.Changeset.change
|
||||
|> Ecto.Changeset.put_assoc(:post, %Post{title: "2"})
|
||||
perma = TestRepo.update!(changeset)
|
||||
assert perma.post.id != post.id
|
||||
post = perma.post
|
||||
assert perma.post_id
|
||||
assert perma.post_id == post.id
|
||||
assert perma.post.title == "2"
|
||||
|
||||
# Replace with existing
|
||||
existing = TestRepo.insert!(%Post{title: "3"})
|
||||
changeset =
|
||||
perma
|
||||
|> Ecto.Changeset.change
|
||||
|> Ecto.Changeset.put_assoc(:post, existing)
|
||||
perma = TestRepo.update!(changeset)
|
||||
post = perma.post
|
||||
assert perma.post_id == post.id
|
||||
assert perma.post_id == existing.id
|
||||
assert perma.post.title == "3"
|
||||
|
||||
# Replace with nil
|
||||
changeset =
|
||||
perma
|
||||
|> Ecto.Changeset.change
|
||||
|> Ecto.Changeset.put_assoc(:post, nil)
|
||||
perma = TestRepo.update!(changeset)
|
||||
assert perma.post == nil
|
||||
assert perma.post_id == nil
|
||||
end
|
||||
|
||||
test "belongs_to changeset assoc (on_replace: :update)" do
|
||||
# Insert new
|
||||
changeset =
|
||||
%Permalink{url: "1"}
|
||||
|> Ecto.Changeset.change
|
||||
|> Ecto.Changeset.put_assoc(:update_post, %Post{title: "1"})
|
||||
perma = TestRepo.insert!(changeset)
|
||||
post = perma.update_post
|
||||
assert perma.post_id
|
||||
assert perma.post_id == post.id
|
||||
assert perma.update_post.title == "1"
|
||||
|
||||
# Casting on update
|
||||
changeset =
|
||||
perma
|
||||
|> Ecto.Changeset.cast(%{update_post: %{title: "2"}}, [])
|
||||
|> Ecto.Changeset.cast_assoc(:update_post)
|
||||
perma = TestRepo.update!(changeset)
|
||||
assert perma.update_post.id == post.id
|
||||
post = perma.update_post
|
||||
assert perma.post_id
|
||||
assert perma.post_id == post.id
|
||||
assert perma.update_post.title == "2"
|
||||
|
||||
# Replace with nil
|
||||
changeset =
|
||||
perma
|
||||
|> Ecto.Changeset.change
|
||||
|> Ecto.Changeset.put_assoc(:update_post, nil)
|
||||
perma = TestRepo.update!(changeset)
|
||||
assert perma.update_post == nil
|
||||
assert perma.post_id == nil
|
||||
end
|
||||
|
||||
test "inserting struct with associations" do
|
||||
tree = %Permalink{
|
||||
url: "root",
|
||||
post: %Post{
|
||||
title: "belongs_to",
|
||||
comments: [
|
||||
%Comment{text: "child 1"},
|
||||
%Comment{text: "child 2"},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
tree = TestRepo.insert!(tree)
|
||||
assert tree.id
|
||||
assert tree.post.id
|
||||
assert length(tree.post.comments) == 2
|
||||
assert Enum.all?(tree.post.comments, & &1.id)
|
||||
|
||||
tree = TestRepo.get!(from(Permalink, preload: [post: :comments]), tree.id)
|
||||
assert tree.id
|
||||
assert tree.post.id
|
||||
assert length(tree.post.comments) == 2
|
||||
assert Enum.all?(tree.post.comments, & &1.id)
|
||||
end
|
||||
|
||||
test "inserting struct with empty associations" do
|
||||
permalink = TestRepo.insert!(%Permalink{url: "root", post: nil})
|
||||
assert permalink.post == nil
|
||||
|
||||
post = TestRepo.insert!(%Post{title: "empty", comments: []})
|
||||
assert post.comments == []
|
||||
end
|
||||
|
||||
test "inserting changeset with empty cast associations" do
|
||||
changeset =
|
||||
%Permalink{}
|
||||
|> Ecto.Changeset.cast(%{url: "root", post: nil}, [:url])
|
||||
|> Ecto.Changeset.cast_assoc(:post)
|
||||
permalink = TestRepo.insert!(changeset)
|
||||
assert permalink.post == nil
|
||||
|
||||
changeset =
|
||||
%Post{}
|
||||
|> Ecto.Changeset.cast(%{title: "root", comments: []}, [:title])
|
||||
|> Ecto.Changeset.cast_assoc(:comments)
|
||||
post = TestRepo.insert!(changeset)
|
||||
assert post.comments == []
|
||||
end
|
||||
|
||||
test "inserting changeset with empty put associations" do
|
||||
changeset =
|
||||
%Permalink{}
|
||||
|> Ecto.Changeset.change()
|
||||
|> Ecto.Changeset.put_assoc(:post, nil)
|
||||
permalink = TestRepo.insert!(changeset)
|
||||
assert permalink.post == nil
|
||||
|
||||
changeset =
|
||||
%Post{}
|
||||
|> Ecto.Changeset.change()
|
||||
|> Ecto.Changeset.put_assoc(:comments, [])
|
||||
post = TestRepo.insert!(changeset)
|
||||
assert post.comments == []
|
||||
end
|
||||
|
||||
test "updating changeset with empty cast associations" do
|
||||
post = TestRepo.insert!(%Post{})
|
||||
c1 = TestRepo.insert!(%Comment{post_id: post.id})
|
||||
c2 = TestRepo.insert!(%Comment{post_id: post.id})
|
||||
|
||||
assert TestRepo.all(Comment) == [c1, c2]
|
||||
|
||||
post = TestRepo.get!(from(Post, preload: [:comments]), post.id)
|
||||
|
||||
post
|
||||
|> Ecto.Changeset.change
|
||||
|> Ecto.Changeset.put_assoc(:comments, [])
|
||||
|> TestRepo.update!()
|
||||
|
||||
assert TestRepo.all(Comment) == []
|
||||
end
|
||||
|
||||
## Dependent
|
||||
|
||||
test "has_many assoc on delete deletes all" do
|
||||
post = TestRepo.insert!(%Post{})
|
||||
TestRepo.insert!(%Comment{post_id: post.id})
|
||||
TestRepo.insert!(%Comment{post_id: post.id})
|
||||
TestRepo.delete!(post)
|
||||
|
||||
assert TestRepo.all(Comment) == []
|
||||
refute Process.get(Comment)
|
||||
end
|
||||
|
||||
test "has_many assoc on delete nilifies all" do
|
||||
user = TestRepo.insert!(%User{})
|
||||
TestRepo.insert!(%Comment{author_id: user.id})
|
||||
TestRepo.insert!(%Comment{author_id: user.id})
|
||||
TestRepo.delete!(user)
|
||||
|
||||
author_ids = Comment |> TestRepo.all() |> Enum.map(fn(comment) -> comment.author_id end)
|
||||
|
||||
assert author_ids == [nil, nil]
|
||||
refute Process.get(Comment)
|
||||
end
|
||||
|
||||
test "has_many assoc on delete does nothing" do
|
||||
user = TestRepo.insert!(%User{})
|
||||
TestRepo.insert!(%Post{author_id: user.id})
|
||||
|
||||
TestRepo.delete!(user)
|
||||
assert Enum.count(TestRepo.all(Post)) == 1
|
||||
end
|
||||
|
||||
test "many_to_many assoc on delete deletes all" do
|
||||
p1 = TestRepo.insert!(%Post{title: "1", visits: 1})
|
||||
p2 = TestRepo.insert!(%Post{title: "2", visits: 2})
|
||||
|
||||
u1 = TestRepo.insert!(%User{name: "john"})
|
||||
u2 = TestRepo.insert!(%User{name: "mary"})
|
||||
|
||||
TestRepo.insert_all "posts_users", [[post_id: p1.id, user_id: u1.id],
|
||||
[post_id: p1.id, user_id: u1.id],
|
||||
[post_id: p2.id, user_id: u2.id]]
|
||||
TestRepo.delete!(p1)
|
||||
|
||||
[pid2] = TestRepo.all from(p in Post, select: p.id)
|
||||
assert pid2 == p2.id
|
||||
|
||||
[[pid2, uid2]] = TestRepo.all from(j in "posts_users", select: [j.post_id, j.user_id])
|
||||
assert pid2 == p2.id
|
||||
assert uid2 == u2.id
|
||||
|
||||
[uid1, uid2] = TestRepo.all from(u in User, select: u.id)
|
||||
assert uid1 == u1.id
|
||||
assert uid2 == u2.id
|
||||
end
|
||||
end
|
||||
419
phoenix/deps/ecto/integration_test/cases/interval.exs
Normal file
419
phoenix/deps/ecto/integration_test/cases/interval.exs
Normal file
@@ -0,0 +1,419 @@
|
||||
defmodule Ecto.Integration.IntervalTest do
|
||||
use Ecto.Integration.Case, async: Application.compile_env(:ecto, :async_integration_tests, true)
|
||||
|
||||
alias Ecto.Integration.{Post, User, Usec}
|
||||
alias Ecto.Integration.TestRepo
|
||||
import Ecto.Query
|
||||
|
||||
@posted ~D[2014-01-01]
|
||||
@inserted_at ~N[2014-01-01 02:00:00]
|
||||
|
||||
setup do
|
||||
TestRepo.insert!(%Post{posted: @posted, inserted_at: @inserted_at})
|
||||
:ok
|
||||
end
|
||||
|
||||
test "date_add with year" do
|
||||
dec = Decimal.new(1)
|
||||
assert [~D[2015-01-01]] = TestRepo.all(from p in Post, select: date_add(p.posted, 1, "year"))
|
||||
assert [~D[2015-01-01]] = TestRepo.all(from p in Post, select: date_add(p.posted, 1.0, "year"))
|
||||
assert [~D[2015-01-01]] = TestRepo.all(from p in Post, select: date_add(p.posted, ^1, "year"))
|
||||
assert [~D[2015-01-01]] = TestRepo.all(from p in Post, select: date_add(p.posted, ^1.0, "year"))
|
||||
assert [~D[2015-01-01]] = TestRepo.all(from p in Post, select: date_add(p.posted, ^dec, "year"))
|
||||
end
|
||||
|
||||
test "date_add with month" do
|
||||
dec = Decimal.new(3)
|
||||
assert [~D[2014-04-01]] = TestRepo.all(from p in Post, select: date_add(p.posted, 3, "month"))
|
||||
assert [~D[2014-04-01]] = TestRepo.all(from p in Post, select: date_add(p.posted, 3.0, "month"))
|
||||
assert [~D[2014-04-01]] = TestRepo.all(from p in Post, select: date_add(p.posted, ^3, "month"))
|
||||
assert [~D[2014-04-01]] = TestRepo.all(from p in Post, select: date_add(p.posted, ^3.0, "month"))
|
||||
assert [~D[2014-04-01]] = TestRepo.all(from p in Post, select: date_add(p.posted, ^dec, "month"))
|
||||
end
|
||||
|
||||
test "date_add with week" do
|
||||
dec = Decimal.new(3)
|
||||
assert [~D[2014-01-22]] = TestRepo.all(from p in Post, select: date_add(p.posted, 3, "week"))
|
||||
assert [~D[2014-01-22]] = TestRepo.all(from p in Post, select: date_add(p.posted, 3.0, "week"))
|
||||
assert [~D[2014-01-22]] = TestRepo.all(from p in Post, select: date_add(p.posted, ^3, "week"))
|
||||
assert [~D[2014-01-22]] = TestRepo.all(from p in Post, select: date_add(p.posted, ^3.0, "week"))
|
||||
assert [~D[2014-01-22]] = TestRepo.all(from p in Post, select: date_add(p.posted, ^dec, "week"))
|
||||
end
|
||||
|
||||
test "date_add with day" do
|
||||
dec = Decimal.new(5)
|
||||
assert [~D[2014-01-06]] = TestRepo.all(from p in Post, select: date_add(p.posted, 5, "day"))
|
||||
assert [~D[2014-01-06]] = TestRepo.all(from p in Post, select: date_add(p.posted, 5.0, "day"))
|
||||
assert [~D[2014-01-06]] = TestRepo.all(from p in Post, select: date_add(p.posted, ^5, "day"))
|
||||
assert [~D[2014-01-06]] = TestRepo.all(from p in Post, select: date_add(p.posted, ^5.0, "day"))
|
||||
assert [~D[2014-01-06]] = TestRepo.all(from p in Post, select: date_add(p.posted, ^dec, "day"))
|
||||
end
|
||||
|
||||
test "date_add with hour" do
|
||||
dec = Decimal.new(48)
|
||||
assert [~D[2014-01-03]] = TestRepo.all(from p in Post, select: date_add(p.posted, 48, "hour"))
|
||||
assert [~D[2014-01-03]] = TestRepo.all(from p in Post, select: date_add(p.posted, 48.0, "hour"))
|
||||
assert [~D[2014-01-03]] = TestRepo.all(from p in Post, select: date_add(p.posted, ^48, "hour"))
|
||||
assert [~D[2014-01-03]] = TestRepo.all(from p in Post, select: date_add(p.posted, ^48.0, "hour"))
|
||||
assert [~D[2014-01-03]] = TestRepo.all(from p in Post, select: date_add(p.posted, ^dec, "hour"))
|
||||
end
|
||||
|
||||
test "date_add with dynamic" do
|
||||
posted = @posted
|
||||
assert [~D[2015-01-01]] = TestRepo.all(from p in Post, select: date_add(^posted, ^1, ^"year"))
|
||||
assert [~D[2014-04-01]] = TestRepo.all(from p in Post, select: date_add(^posted, ^3, ^"month"))
|
||||
assert [~D[2014-01-22]] = TestRepo.all(from p in Post, select: date_add(^posted, ^3, ^"week"))
|
||||
assert [~D[2014-01-06]] = TestRepo.all(from p in Post, select: date_add(^posted, ^5, ^"day"))
|
||||
assert [~D[2014-01-03]] = TestRepo.all(from p in Post, select: date_add(^posted, ^48, ^"hour"))
|
||||
end
|
||||
|
||||
test "date_add with negative interval" do
|
||||
dec = Decimal.new(-1)
|
||||
assert [~D[2013-01-01]] = TestRepo.all(from p in Post, select: date_add(p.posted, -1, "year"))
|
||||
assert [~D[2013-01-01]] = TestRepo.all(from p in Post, select: date_add(p.posted, -1.0, "year"))
|
||||
assert [~D[2013-01-01]] = TestRepo.all(from p in Post, select: date_add(p.posted, ^-1, "year"))
|
||||
assert [~D[2013-01-01]] = TestRepo.all(from p in Post, select: date_add(p.posted, ^-1.0, "year"))
|
||||
assert [~D[2013-01-01]] = TestRepo.all(from p in Post, select: date_add(p.posted, ^dec, "year"))
|
||||
end
|
||||
|
||||
test "datetime_add with year" do
|
||||
dec = Decimal.new(1)
|
||||
assert [~N[2015-01-01 02:00:00]] =
|
||||
TestRepo.all(from p in Post, select: datetime_add(p.inserted_at, 1, "year"))
|
||||
assert [~N[2015-01-01 02:00:00]] =
|
||||
TestRepo.all(from p in Post, select: datetime_add(p.inserted_at, 1.0, "year"))
|
||||
assert [~N[2015-01-01 02:00:00]] =
|
||||
TestRepo.all(from p in Post, select: datetime_add(p.inserted_at, ^1, "year"))
|
||||
assert [~N[2015-01-01 02:00:00]] =
|
||||
TestRepo.all(from p in Post, select: datetime_add(p.inserted_at, ^1.0, "year"))
|
||||
assert [~N[2015-01-01 02:00:00]] =
|
||||
TestRepo.all(from p in Post, select: datetime_add(p.inserted_at, ^dec, "year"))
|
||||
end
|
||||
|
||||
test "datetime_add with month" do
|
||||
dec = Decimal.new(3)
|
||||
assert [~N[2014-04-01 02:00:00]] =
|
||||
TestRepo.all(from p in Post, select: datetime_add(p.inserted_at, 3, "month"))
|
||||
assert [~N[2014-04-01 02:00:00]] =
|
||||
TestRepo.all(from p in Post, select: datetime_add(p.inserted_at, 3.0, "month"))
|
||||
assert [~N[2014-04-01 02:00:00]] =
|
||||
TestRepo.all(from p in Post, select: datetime_add(p.inserted_at, ^3, "month"))
|
||||
assert [~N[2014-04-01 02:00:00]] =
|
||||
TestRepo.all(from p in Post, select: datetime_add(p.inserted_at, ^3.0, "month"))
|
||||
assert [~N[2014-04-01 02:00:00]] =
|
||||
TestRepo.all(from p in Post, select: datetime_add(p.inserted_at, ^dec, "month"))
|
||||
end
|
||||
|
||||
test "datetime_add with week" do
|
||||
dec = Decimal.new(3)
|
||||
assert [~N[2014-01-22 02:00:00]] =
|
||||
TestRepo.all(from p in Post, select: datetime_add(p.inserted_at, 3, "week"))
|
||||
assert [~N[2014-01-22 02:00:00]] =
|
||||
TestRepo.all(from p in Post, select: datetime_add(p.inserted_at, 3.0, "week"))
|
||||
assert [~N[2014-01-22 02:00:00]] =
|
||||
TestRepo.all(from p in Post, select: datetime_add(p.inserted_at, ^3, "week"))
|
||||
assert [~N[2014-01-22 02:00:00]] =
|
||||
TestRepo.all(from p in Post, select: datetime_add(p.inserted_at, ^3.0, "week"))
|
||||
assert [~N[2014-01-22 02:00:00]] =
|
||||
TestRepo.all(from p in Post, select: datetime_add(p.inserted_at, ^dec, "week"))
|
||||
end
|
||||
|
||||
test "datetime_add with day" do
|
||||
dec = Decimal.new(5)
|
||||
assert [~N[2014-01-06 02:00:00]] =
|
||||
TestRepo.all(from p in Post, select: datetime_add(p.inserted_at, 5, "day"))
|
||||
assert [~N[2014-01-06 02:00:00]] =
|
||||
TestRepo.all(from p in Post, select: datetime_add(p.inserted_at, 5.0, "day"))
|
||||
assert [~N[2014-01-06 02:00:00]] =
|
||||
TestRepo.all(from p in Post, select: datetime_add(p.inserted_at, ^5, "day"))
|
||||
assert [~N[2014-01-06 02:00:00]] =
|
||||
TestRepo.all(from p in Post, select: datetime_add(p.inserted_at, ^5.0, "day"))
|
||||
assert [~N[2014-01-06 02:00:00]] =
|
||||
TestRepo.all(from p in Post, select: datetime_add(p.inserted_at, ^dec, "day"))
|
||||
end
|
||||
|
||||
test "datetime_add with hour" do
|
||||
dec = Decimal.new(60)
|
||||
assert [~N[2014-01-03 14:00:00]] =
|
||||
TestRepo.all(from p in Post, select: datetime_add(p.inserted_at, 60, "hour"))
|
||||
assert [~N[2014-01-03 14:00:00]] =
|
||||
TestRepo.all(from p in Post, select: datetime_add(p.inserted_at, 60.0, "hour"))
|
||||
assert [~N[2014-01-03 14:00:00]] =
|
||||
TestRepo.all(from p in Post, select: datetime_add(p.inserted_at, ^60, "hour"))
|
||||
assert [~N[2014-01-03 14:00:00]] =
|
||||
TestRepo.all(from p in Post, select: datetime_add(p.inserted_at, ^60.0, "hour"))
|
||||
assert [~N[2014-01-03 14:00:00]] =
|
||||
TestRepo.all(from p in Post, select: datetime_add(p.inserted_at, ^dec, "hour"))
|
||||
end
|
||||
|
||||
test "datetime_add with minute" do
|
||||
dec = Decimal.new(90)
|
||||
assert [~N[2014-01-01 03:30:00]] =
|
||||
TestRepo.all(from p in Post, select: datetime_add(p.inserted_at, 90, "minute"))
|
||||
assert [~N[2014-01-01 03:30:00]] =
|
||||
TestRepo.all(from p in Post, select: datetime_add(p.inserted_at, 90.0, "minute"))
|
||||
assert [~N[2014-01-01 03:30:00]] =
|
||||
TestRepo.all(from p in Post, select: datetime_add(p.inserted_at, ^90, "minute"))
|
||||
assert [~N[2014-01-01 03:30:00]] =
|
||||
TestRepo.all(from p in Post, select: datetime_add(p.inserted_at, ^90.0, "minute"))
|
||||
assert [~N[2014-01-01 03:30:00]] =
|
||||
TestRepo.all(from p in Post, select: datetime_add(p.inserted_at, ^dec, "minute"))
|
||||
end
|
||||
|
||||
test "datetime_add with second" do
|
||||
dec = Decimal.new(90)
|
||||
assert [~N[2014-01-01 02:01:30]] =
|
||||
TestRepo.all(from p in Post, select: datetime_add(p.inserted_at, 90, "second"))
|
||||
assert [~N[2014-01-01 02:01:30]] =
|
||||
TestRepo.all(from p in Post, select: datetime_add(p.inserted_at, 90.0, "second"))
|
||||
assert [~N[2014-01-01 02:01:30]] =
|
||||
TestRepo.all(from p in Post, select: datetime_add(p.inserted_at, ^90, "second"))
|
||||
assert [~N[2014-01-01 02:01:30]] =
|
||||
TestRepo.all(from p in Post, select: datetime_add(p.inserted_at, ^90.0, "second"))
|
||||
assert [~N[2014-01-01 02:01:30]] =
|
||||
TestRepo.all(from p in Post, select: datetime_add(p.inserted_at, ^dec, "second"))
|
||||
end
|
||||
|
||||
@tag :uses_msec
|
||||
test "datetime_add with millisecond" do
|
||||
dec = Decimal.new(1500)
|
||||
assert [~N[2014-01-01 02:00:01]] =
|
||||
TestRepo.all(from p in Post, select: datetime_add(p.inserted_at, 1500, "millisecond"))
|
||||
assert [~N[2014-01-01 02:00:01]] =
|
||||
TestRepo.all(from p in Post, select: datetime_add(p.inserted_at, 1500.0, "millisecond"))
|
||||
assert [~N[2014-01-01 02:00:01]] =
|
||||
TestRepo.all(from p in Post, select: datetime_add(p.inserted_at, ^1500, "millisecond"))
|
||||
assert [~N[2014-01-01 02:00:01]] =
|
||||
TestRepo.all(from p in Post, select: datetime_add(p.inserted_at, ^1500.0, "millisecond"))
|
||||
assert [~N[2014-01-01 02:00:01]] =
|
||||
TestRepo.all(from p in Post, select: datetime_add(p.inserted_at, ^dec, "millisecond"))
|
||||
end
|
||||
|
||||
@tag :microsecond_precision
|
||||
@tag :uses_usec
|
||||
test "datetime_add with microsecond" do
|
||||
dec = Decimal.new(1500)
|
||||
assert [~N[2014-01-01 02:00:00]] =
|
||||
TestRepo.all(from p in Post, select: datetime_add(p.inserted_at, 1500, "microsecond"))
|
||||
assert [~N[2014-01-01 02:00:00]] =
|
||||
TestRepo.all(from p in Post, select: datetime_add(p.inserted_at, 1500.0, "microsecond"))
|
||||
assert [~N[2014-01-01 02:00:00]] =
|
||||
TestRepo.all(from p in Post, select: datetime_add(p.inserted_at, ^1500, "microsecond"))
|
||||
assert [~N[2014-01-01 02:00:00]] =
|
||||
TestRepo.all(from p in Post, select: datetime_add(p.inserted_at, ^1500.0, "microsecond"))
|
||||
assert [~N[2014-01-01 02:00:00]] =
|
||||
TestRepo.all(from p in Post, select: datetime_add(p.inserted_at, ^dec, "microsecond"))
|
||||
end
|
||||
|
||||
test "datetime_add with dynamic" do
|
||||
inserted_at = @inserted_at
|
||||
assert [~N[2015-01-01 02:00:00]] =
|
||||
TestRepo.all(from p in Post, select: datetime_add(^inserted_at, ^1, ^"year"))
|
||||
assert [~N[2014-04-01 02:00:00]] =
|
||||
TestRepo.all(from p in Post, select: datetime_add(^inserted_at, ^3, ^"month"))
|
||||
assert [~N[2014-01-22 02:00:00]] =
|
||||
TestRepo.all(from p in Post, select: datetime_add(^inserted_at, ^3, ^"week"))
|
||||
assert [~N[2014-01-06 02:00:00]] =
|
||||
TestRepo.all(from p in Post, select: datetime_add(^inserted_at, ^5, ^"day"))
|
||||
assert [~N[2014-01-03 14:00:00]] =
|
||||
TestRepo.all(from p in Post, select: datetime_add(^inserted_at, ^60, ^"hour"))
|
||||
assert [~N[2014-01-01 03:30:00]] =
|
||||
TestRepo.all(from p in Post, select: datetime_add(^inserted_at, ^90, ^"minute"))
|
||||
assert [~N[2014-01-01 02:01:30]] =
|
||||
TestRepo.all(from p in Post, select: datetime_add(^inserted_at, ^90, ^"second"))
|
||||
end
|
||||
|
||||
test "datetime_add with dynamic in filters" do
|
||||
inserted_at = @inserted_at
|
||||
field = :inserted_at
|
||||
assert [_] =
|
||||
TestRepo.all(from p in Post, where: p.inserted_at > datetime_add(^inserted_at, ^-1, "year"))
|
||||
assert [_] =
|
||||
TestRepo.all(from p in Post, where: p.inserted_at > datetime_add(^inserted_at, -3, "month"))
|
||||
assert [_] =
|
||||
TestRepo.all(from p in Post, where: field(p, ^field) > datetime_add(^inserted_at, ^-3, ^"week"))
|
||||
assert [_] =
|
||||
TestRepo.all(from p in Post, where: field(p, ^field) > datetime_add(^inserted_at, -5, ^"day"))
|
||||
end
|
||||
|
||||
test "datetime_add with negative interval" do
|
||||
dec = Decimal.new(-1)
|
||||
assert [~N[2013-01-01 02:00:00]] =
|
||||
TestRepo.all(from p in Post, select: datetime_add(p.inserted_at, -1, "year"))
|
||||
assert [~N[2013-01-01 02:00:00]] =
|
||||
TestRepo.all(from p in Post, select: datetime_add(p.inserted_at, -1.0, "year"))
|
||||
assert [~N[2013-01-01 02:00:00]] =
|
||||
TestRepo.all(from p in Post, select: datetime_add(p.inserted_at, ^-1, "year"))
|
||||
assert [~N[2013-01-01 02:00:00]] =
|
||||
TestRepo.all(from p in Post, select: datetime_add(p.inserted_at, ^-1.0, "year"))
|
||||
assert [~N[2013-01-01 02:00:00]] =
|
||||
TestRepo.all(from p in Post, select: datetime_add(p.inserted_at, ^dec, "year"))
|
||||
end
|
||||
|
||||
test "from_now" do
|
||||
current = DateTime.utc_now().year
|
||||
dec = Decimal.new(5)
|
||||
assert [%{year: year}] = TestRepo.all(from p in Post, select: from_now(5, "year"))
|
||||
assert year > current
|
||||
assert [%{year: year}] = TestRepo.all(from p in Post, select: from_now(5.0, "year"))
|
||||
assert year > current
|
||||
assert [%{year: year}] = TestRepo.all(from p in Post, select: from_now(^5, "year"))
|
||||
assert year > current
|
||||
assert [%{year: year}] = TestRepo.all(from p in Post, select: from_now(^5.0, "year"))
|
||||
assert year > current
|
||||
assert [%{year: year}] = TestRepo.all(from p in Post, select: from_now(^dec, "year"))
|
||||
assert year > current
|
||||
end
|
||||
|
||||
test "ago" do
|
||||
current = DateTime.utc_now().year
|
||||
dec = Decimal.new(5)
|
||||
assert [%{year: year}] = TestRepo.all(from p in Post, select: ago(5, "year"))
|
||||
assert year < current
|
||||
assert [%{year: year}] = TestRepo.all(from p in Post, select: ago(5.0, "year"))
|
||||
assert year < current
|
||||
assert [%{year: year}] = TestRepo.all(from p in Post, select: ago(^5, "year"))
|
||||
assert year < current
|
||||
assert [%{year: year}] = TestRepo.all(from p in Post, select: ago(^5.0, "year"))
|
||||
assert year < current
|
||||
assert [%{year: year}] = TestRepo.all(from p in Post, select: ago(^dec, "year"))
|
||||
assert year < current
|
||||
end
|
||||
|
||||
test "datetime_add with utc_datetime" do
|
||||
{:ok, datetime} = DateTime.from_naive(@inserted_at, "Etc/UTC")
|
||||
TestRepo.insert!(%User{inserted_at: datetime})
|
||||
|
||||
{:ok, datetime} = DateTime.from_naive(~N[2015-01-01 02:00:00], "Etc/UTC")
|
||||
dec = Decimal.new(1)
|
||||
|
||||
assert [^datetime] =
|
||||
TestRepo.all(from p in User, select: datetime_add(type(^datetime, :utc_datetime), 0, "year"))
|
||||
assert [^datetime] =
|
||||
TestRepo.all(from p in User, select: datetime_add(p.inserted_at, 1, "year"))
|
||||
assert [^datetime] =
|
||||
TestRepo.all(from p in User, select: datetime_add(p.inserted_at, 1.0, "year"))
|
||||
assert [^datetime] =
|
||||
TestRepo.all(from p in User, select: datetime_add(p.inserted_at, ^1, "year"))
|
||||
assert [^datetime] =
|
||||
TestRepo.all(from p in User, select: datetime_add(p.inserted_at, ^1.0, "year"))
|
||||
assert [^datetime] =
|
||||
TestRepo.all(from p in User, select: datetime_add(p.inserted_at, ^dec, "year"))
|
||||
end
|
||||
|
||||
@tag :microsecond_precision
|
||||
test "datetime_add with naive_datetime_usec" do
|
||||
TestRepo.insert!(%Usec{naive_datetime_usec: ~N[2014-01-01 02:00:00.000001]})
|
||||
datetime = ~N[2014-01-01 02:00:00.001501]
|
||||
|
||||
assert [^datetime] =
|
||||
TestRepo.all(from u in Usec, select: datetime_add(type(^datetime, :naive_datetime_usec), 0, "microsecond"))
|
||||
assert [^datetime] =
|
||||
TestRepo.all(from u in Usec, select: datetime_add(u.naive_datetime_usec, 1500, "microsecond"))
|
||||
assert [^datetime] =
|
||||
TestRepo.all(from u in Usec, select: datetime_add(u.naive_datetime_usec, 1500.0, "microsecond"))
|
||||
assert [^datetime] =
|
||||
TestRepo.all(from u in Usec, select: datetime_add(u.naive_datetime_usec, ^1500, "microsecond"))
|
||||
end
|
||||
|
||||
@tag :microsecond_precision
|
||||
@tag :decimal_precision
|
||||
test "datetime_add with naive_datetime_usec and decimal increment" do
|
||||
TestRepo.insert!(%Usec{naive_datetime_usec: ~N[2014-01-01 02:00:00.000001]})
|
||||
dec = Decimal.new(1500)
|
||||
datetime = ~N[2014-01-01 02:00:00.001501]
|
||||
|
||||
assert [^datetime] =
|
||||
TestRepo.all(from u in Usec, select: datetime_add(u.naive_datetime_usec, ^1500.0, "microsecond"))
|
||||
assert [^datetime] =
|
||||
TestRepo.all(from u in Usec, select: datetime_add(u.naive_datetime_usec, ^dec, "microsecond"))
|
||||
end
|
||||
|
||||
@tag :microsecond_precision
|
||||
test "datetime_add with utc_datetime_usec" do
|
||||
{:ok, datetime} = DateTime.from_naive(~N[2014-01-01 02:00:00.000001], "Etc/UTC")
|
||||
TestRepo.insert!(%Usec{utc_datetime_usec: datetime})
|
||||
|
||||
{:ok, datetime} = DateTime.from_naive(~N[2014-01-01 02:00:00.001501], "Etc/UTC")
|
||||
|
||||
assert [^datetime] =
|
||||
TestRepo.all(from u in Usec, select: datetime_add(type(^datetime, :utc_datetime_usec), 0, "microsecond"))
|
||||
assert [^datetime] =
|
||||
TestRepo.all(from u in Usec, select: datetime_add(u.utc_datetime_usec, 1500, "microsecond"))
|
||||
assert [^datetime] =
|
||||
TestRepo.all(from u in Usec, select: datetime_add(u.utc_datetime_usec, 1500.0, "microsecond"))
|
||||
assert [^datetime] =
|
||||
TestRepo.all(from u in Usec, select: datetime_add(u.utc_datetime_usec, ^1500, "microsecond"))
|
||||
end
|
||||
|
||||
@tag :microsecond_precision
|
||||
@tag :decimal_precision
|
||||
test "datetime_add uses utc_datetime_usec with decimal increment" do
|
||||
{:ok, datetime} = DateTime.from_naive(~N[2014-01-01 02:00:00.000001], "Etc/UTC")
|
||||
TestRepo.insert!(%Usec{utc_datetime_usec: datetime})
|
||||
|
||||
{:ok, datetime} = DateTime.from_naive(~N[2014-01-01 02:00:00.001501], "Etc/UTC")
|
||||
dec = Decimal.new(1500)
|
||||
|
||||
assert [^datetime] =
|
||||
TestRepo.all(from u in Usec, select: datetime_add(u.utc_datetime_usec, ^1500.0, "microsecond"))
|
||||
assert [^datetime] =
|
||||
TestRepo.all(from u in Usec, select: datetime_add(u.utc_datetime_usec, ^dec, "microsecond"))
|
||||
end
|
||||
|
||||
test "datetime_add with utc_datetime_usec in milliseconds" do
|
||||
{:ok, datetime} = DateTime.from_naive(~N[2014-01-01 02:00:00.001000], "Etc/UTC")
|
||||
TestRepo.insert!(%Usec{utc_datetime_usec: datetime})
|
||||
|
||||
{:ok, datetime} = DateTime.from_naive(~N[2014-01-01 02:00:00.151000], "Etc/UTC")
|
||||
|
||||
assert [^datetime] =
|
||||
TestRepo.all(from u in Usec, select: datetime_add(type(^datetime, :utc_datetime_usec), 0, "millisecond"))
|
||||
assert [^datetime] =
|
||||
TestRepo.all(from u in Usec, select: datetime_add(u.utc_datetime_usec, 150, "millisecond"))
|
||||
assert [^datetime] =
|
||||
TestRepo.all(from u in Usec, select: datetime_add(u.utc_datetime_usec, 150, "millisecond"))
|
||||
assert [^datetime] =
|
||||
TestRepo.all(from u in Usec, select: datetime_add(u.utc_datetime_usec, ^150, "millisecond"))
|
||||
end
|
||||
|
||||
@tag :decimal_precision
|
||||
test "datetime_add uses utc_datetime_usec with decimal increment in milliseconds" do
|
||||
{:ok, datetime} = DateTime.from_naive(~N[2014-01-01 02:00:00.001000], "Etc/UTC")
|
||||
TestRepo.insert!(%Usec{utc_datetime_usec: datetime})
|
||||
|
||||
{:ok, datetime} = DateTime.from_naive(~N[2014-01-01 02:00:00.151000], "Etc/UTC")
|
||||
dec = Decimal.new(150)
|
||||
|
||||
assert [^datetime] =
|
||||
TestRepo.all(from u in Usec, select: datetime_add(u.utc_datetime_usec, ^150.0, "millisecond"))
|
||||
assert [^datetime] =
|
||||
TestRepo.all(from u in Usec, select: datetime_add(u.utc_datetime_usec, ^dec, "millisecond"))
|
||||
end
|
||||
|
||||
test "datetime_add with naive_datetime_usec in milliseconds" do
|
||||
TestRepo.insert!(%Usec{naive_datetime_usec: ~N[2014-01-01 02:00:00.001000]})
|
||||
datetime = ~N[2014-01-01 02:00:00.151000]
|
||||
|
||||
assert [^datetime] =
|
||||
TestRepo.all(from u in Usec, select: datetime_add(type(^datetime, :naive_datetime_usec), 0, "millisecond"))
|
||||
assert [^datetime] =
|
||||
TestRepo.all(from u in Usec, select: datetime_add(u.naive_datetime_usec, 150, "millisecond"))
|
||||
assert [^datetime] =
|
||||
TestRepo.all(from u in Usec, select: datetime_add(u.naive_datetime_usec, 150.0, "millisecond"))
|
||||
assert [^datetime] =
|
||||
TestRepo.all(from u in Usec, select: datetime_add(u.naive_datetime_usec, ^150, "millisecond"))
|
||||
end
|
||||
|
||||
@tag :decimal_precision
|
||||
test "datetime_add with naive_datetime_usec and decimal increment in milliseconds" do
|
||||
TestRepo.insert!(%Usec{naive_datetime_usec: ~N[2014-01-01 02:00:00.001000]})
|
||||
dec = Decimal.new(150)
|
||||
datetime = ~N[2014-01-01 02:00:00.151000]
|
||||
|
||||
assert [^datetime] =
|
||||
TestRepo.all(from u in Usec, select: datetime_add(u.naive_datetime_usec, ^150.0, "millisecond"))
|
||||
assert [^datetime] =
|
||||
TestRepo.all(from u in Usec, select: datetime_add(u.naive_datetime_usec, ^dec, "millisecond"))
|
||||
end
|
||||
end
|
||||
672
phoenix/deps/ecto/integration_test/cases/joins.exs
Normal file
672
phoenix/deps/ecto/integration_test/cases/joins.exs
Normal file
@@ -0,0 +1,672 @@
|
||||
defmodule Ecto.Integration.JoinsTest do
|
||||
use Ecto.Integration.Case, async: Application.compile_env(:ecto, :async_integration_tests, true)
|
||||
|
||||
alias Ecto.Integration.TestRepo
|
||||
import Ecto.Query
|
||||
|
||||
alias Ecto.Integration.Post
|
||||
alias Ecto.Integration.Comment
|
||||
alias Ecto.Integration.Permalink
|
||||
alias Ecto.Integration.User
|
||||
alias Ecto.Integration.PostUserCompositePk
|
||||
|
||||
@tag :update_with_join
|
||||
test "update all with joins" do
|
||||
user = TestRepo.insert!(%User{name: "Tester"})
|
||||
post = TestRepo.insert!(%Post{title: "foo"})
|
||||
comment = TestRepo.insert!(%Comment{text: "hey", author_id: user.id, post_id: post.id})
|
||||
|
||||
another_post = TestRepo.insert!(%Post{title: "bar"})
|
||||
another_comment = TestRepo.insert!(%Comment{text: "another", author_id: user.id, post_id: another_post.id})
|
||||
|
||||
query = from(c in Comment, join: u in User, on: u.id == c.author_id,
|
||||
where: c.post_id in ^[post.id])
|
||||
|
||||
assert {1, nil} = TestRepo.update_all(query, set: [text: "hoo"])
|
||||
assert %Comment{text: "hoo"} = TestRepo.get(Comment, comment.id)
|
||||
assert %Comment{text: "another"} = TestRepo.get(Comment, another_comment.id)
|
||||
end
|
||||
|
||||
@tag :delete_with_join
|
||||
test "delete all with joins" do
|
||||
user = TestRepo.insert!(%User{name: "Tester"})
|
||||
post = TestRepo.insert!(%Post{title: "foo"})
|
||||
TestRepo.insert!(%Comment{text: "hey", author_id: user.id, post_id: post.id})
|
||||
TestRepo.insert!(%Comment{text: "foo", author_id: user.id, post_id: post.id})
|
||||
TestRepo.insert!(%Comment{text: "bar", author_id: user.id})
|
||||
|
||||
query = from(c in Comment, join: u in User, on: u.id == c.author_id,
|
||||
where: is_nil(c.post_id))
|
||||
assert {1, nil} = TestRepo.delete_all(query)
|
||||
assert [%Comment{}, %Comment{}] = TestRepo.all(Comment)
|
||||
|
||||
query = from(c in Comment, join: u in assoc(c, :author),
|
||||
join: p in assoc(c, :post),
|
||||
where: p.id in ^[post.id])
|
||||
assert {2, nil} = TestRepo.delete_all(query)
|
||||
assert [] = TestRepo.all(Comment)
|
||||
end
|
||||
|
||||
test "joins" do
|
||||
_p = TestRepo.insert!(%Post{title: "1"})
|
||||
p2 = TestRepo.insert!(%Post{title: "2"})
|
||||
c1 = TestRepo.insert!(%Permalink{url: "1", post_id: p2.id})
|
||||
|
||||
query = from(p in Post, join: c in assoc(p, :permalink), order_by: p.id, select: {p, c})
|
||||
assert [{^p2, ^c1}] = TestRepo.all(query)
|
||||
|
||||
query = from(p in Post, join: c in assoc(p, :permalink), on: c.id == ^c1.id, select: {p, c})
|
||||
assert [{^p2, ^c1}] = TestRepo.all(query)
|
||||
end
|
||||
|
||||
test "joins with queries" do
|
||||
p1 = TestRepo.insert!(%Post{title: "1"})
|
||||
p2 = TestRepo.insert!(%Post{title: "2"})
|
||||
c1 = TestRepo.insert!(%Permalink{url: "1", post_id: p2.id})
|
||||
|
||||
# Joined query without parameter
|
||||
permalink = from c in Permalink, where: c.url == "1"
|
||||
|
||||
query = from(p in Post, join: c in ^permalink, on: c.post_id == p.id, select: {p, c})
|
||||
assert [{^p2, ^c1}] = TestRepo.all(query)
|
||||
|
||||
# Joined query with parameter
|
||||
permalink = from c in Permalink, where: c.url == "1"
|
||||
|
||||
query = from(p in Post, join: c in ^permalink, on: c.id == ^c1.id, order_by: p.title, select: {p, c})
|
||||
assert [{^p1, ^c1}, {^p2, ^c1}] = TestRepo.all(query)
|
||||
end
|
||||
|
||||
test "named joins" do
|
||||
_p = TestRepo.insert!(%Post{title: "1"})
|
||||
p2 = TestRepo.insert!(%Post{title: "2"})
|
||||
c1 = TestRepo.insert!(%Permalink{url: "1", post_id: p2.id})
|
||||
|
||||
query =
|
||||
from(p in Post, join: c in assoc(p, :permalink), as: :permalink, order_by: p.id)
|
||||
|> select([p, permalink: c], {p, c})
|
||||
|
||||
assert [{^p2, ^c1}] = TestRepo.all(query)
|
||||
end
|
||||
|
||||
test "joins with dynamic in :on" do
|
||||
p = TestRepo.insert!(%Post{title: "1"})
|
||||
c = TestRepo.insert!(%Permalink{url: "1", post_id: p.id})
|
||||
|
||||
join_on = dynamic([p, ..., c], c.id == ^c.id)
|
||||
|
||||
query =
|
||||
from(p in Post, join: c in Permalink, on: ^join_on)
|
||||
|> select([p, c], {p, c})
|
||||
|
||||
assert [{^p, ^c}] = TestRepo.all(query)
|
||||
|
||||
join_on = dynamic([p, permalink: c], c.id == ^c.id)
|
||||
|
||||
query =
|
||||
from(p in Post, join: c in Permalink, as: :permalink, on: ^join_on)
|
||||
|> select([p, c], {p, c})
|
||||
|
||||
assert [{^p, ^c}] = TestRepo.all(query)
|
||||
end
|
||||
|
||||
@tag :cross_join
|
||||
test "cross joins with missing entries" do
|
||||
p1 = TestRepo.insert!(%Post{title: "1"})
|
||||
p2 = TestRepo.insert!(%Post{title: "2"})
|
||||
c1 = TestRepo.insert!(%Permalink{url: "1", post_id: p2.id})
|
||||
|
||||
query = from(p in Post, cross_join: c in Permalink, order_by: p.id, select: {p, c})
|
||||
assert [{^p1, ^c1}, {^p2, ^c1}] = TestRepo.all(query)
|
||||
end
|
||||
|
||||
@tag :left_join
|
||||
test "left joins with missing entries" do
|
||||
p1 = TestRepo.insert!(%Post{title: "1"})
|
||||
p2 = TestRepo.insert!(%Post{title: "2"})
|
||||
c1 = TestRepo.insert!(%Permalink{url: "1", post_id: p2.id})
|
||||
|
||||
query = from(p in Post, left_join: c in assoc(p, :permalink), order_by: p.id, select: {p, c})
|
||||
assert [{^p1, nil}, {^p2, ^c1}] = TestRepo.all(query)
|
||||
end
|
||||
|
||||
@tag :left_join
|
||||
test "left join with missing entries from subquery" do
|
||||
p1 = TestRepo.insert!(%Post{title: "1"})
|
||||
p2 = TestRepo.insert!(%Post{title: "2"})
|
||||
c1 = TestRepo.insert!(%Permalink{url: "1", post_id: p2.id})
|
||||
|
||||
query = from(p in Post, left_join: c in subquery(Permalink), on: p.id == c.post_id, order_by: p.id, select: {p, c})
|
||||
assert [{^p1, nil}, {^p2, ^c1}] = TestRepo.all(query)
|
||||
end
|
||||
|
||||
@tag :right_join
|
||||
test "right joins with missing entries" do
|
||||
%Post{id: pid1} = TestRepo.insert!(%Post{title: "1"})
|
||||
%Post{id: pid2} = TestRepo.insert!(%Post{title: "2"})
|
||||
|
||||
%Permalink{id: plid1} = TestRepo.insert!(%Permalink{url: "1", post_id: pid2})
|
||||
|
||||
TestRepo.insert!(%Comment{text: "1", post_id: pid1})
|
||||
TestRepo.insert!(%Comment{text: "2", post_id: pid2})
|
||||
TestRepo.insert!(%Comment{text: "3", post_id: nil})
|
||||
|
||||
query = from(p in Post, right_join: c in assoc(p, :comments),
|
||||
preload: :permalink, order_by: c.id)
|
||||
assert [p1, p2, p3] = TestRepo.all(query)
|
||||
assert p1.id == pid1
|
||||
assert p2.id == pid2
|
||||
assert is_nil(p3.id)
|
||||
|
||||
assert p1.permalink == nil
|
||||
assert p2.permalink.id == plid1
|
||||
end
|
||||
|
||||
## Associations joins
|
||||
|
||||
test "has_many association join" do
|
||||
post = TestRepo.insert!(%Post{title: "1"})
|
||||
c1 = TestRepo.insert!(%Comment{text: "hey", post_id: post.id})
|
||||
c2 = TestRepo.insert!(%Comment{text: "heya", post_id: post.id})
|
||||
|
||||
query = from(p in Post, join: c in assoc(p, :comments), select: {p, c}, order_by: p.id)
|
||||
[{^post, ^c1}, {^post, ^c2}] = TestRepo.all(query)
|
||||
end
|
||||
|
||||
test "has_one association join" do
|
||||
post1 = TestRepo.insert!(%Post{title: "1"})
|
||||
post2 = TestRepo.insert!(%Post{title: "1"})
|
||||
user = TestRepo.insert!(%User{})
|
||||
p1 = TestRepo.insert!(%Permalink{url: "hey", user_id: user.id, post_id: post1.id})
|
||||
p2 = TestRepo.insert!(%Permalink{url: "heya", user_id: user.id, post_id: post2.id})
|
||||
|
||||
query = from(p in User, join: c in assoc(p, :permalink), select: {p, c}, order_by: c.id)
|
||||
[{^user, ^p1}, {^user, ^p2}] = TestRepo.all(query)
|
||||
end
|
||||
|
||||
test "belongs_to association join" do
|
||||
post1 = TestRepo.insert!(%Post{title: "1"})
|
||||
post2 = TestRepo.insert!(%Post{title: "1"})
|
||||
user = TestRepo.insert!(%User{})
|
||||
p1 = TestRepo.insert!(%Permalink{url: "hey", user_id: user.id, post_id: post1.id})
|
||||
p2 = TestRepo.insert!(%Permalink{url: "heya", user_id: user.id, post_id: post2.id})
|
||||
|
||||
query = from(p in Permalink, join: c in assoc(p, :user), select: {p, c}, order_by: p.id)
|
||||
[{^p1, ^user}, {^p2, ^user}] = TestRepo.all(query)
|
||||
end
|
||||
|
||||
test "has_many through association join" do
|
||||
p1 = TestRepo.insert!(%Post{})
|
||||
p2 = TestRepo.insert!(%Post{})
|
||||
|
||||
u1 = TestRepo.insert!(%User{name: "zzz"})
|
||||
u2 = TestRepo.insert!(%User{name: "aaa"})
|
||||
|
||||
%Comment{} = TestRepo.insert!(%Comment{post_id: p1.id, author_id: u1.id})
|
||||
%Comment{} = TestRepo.insert!(%Comment{post_id: p1.id, author_id: u1.id})
|
||||
%Comment{} = TestRepo.insert!(%Comment{post_id: p1.id, author_id: u2.id})
|
||||
%Comment{} = TestRepo.insert!(%Comment{post_id: p2.id, author_id: u2.id})
|
||||
|
||||
query = from p in Post, join: a in assoc(p, :comments_authors), select: {p, a}, order_by: [p.id, a.name]
|
||||
assert [{^p1, ^u2}, {^p1, ^u1}, {^p1, ^u1}, {^p2, ^u2}] = TestRepo.all(query)
|
||||
end
|
||||
|
||||
test "has_many through nested association joins" do
|
||||
u1 = TestRepo.insert!(%User{name: "Alice"})
|
||||
u2 = TestRepo.insert!(%User{name: "John"})
|
||||
|
||||
p1 = TestRepo.insert!(%Post{title: "p1", author_id: u1.id})
|
||||
p2 = TestRepo.insert!(%Post{title: "p2", author_id: u1.id})
|
||||
|
||||
TestRepo.insert!(%Comment{text: "c1", author_id: u1.id, post_id: p1.id})
|
||||
TestRepo.insert!(%Comment{text: "c2", author_id: u2.id, post_id: p1.id})
|
||||
TestRepo.insert!(%Comment{text: "c3", author_id: u2.id, post_id: p2.id})
|
||||
TestRepo.insert!(%Comment{text: "c4", post_id: p2.id})
|
||||
TestRepo.insert!(%Comment{text: "c5", author_id: u1.id, post_id: p2.id})
|
||||
|
||||
assert %{
|
||||
comments: [
|
||||
%{text: "c1"},
|
||||
%{text: "c5"}
|
||||
],
|
||||
posts: [
|
||||
%{title: "p1"} = p1,
|
||||
%{title: "p2"} = p2
|
||||
]
|
||||
} =
|
||||
from(u in User)
|
||||
|> join(:left, [u], p in assoc(u, :posts))
|
||||
|> join(:left, [u], c in assoc(u, :comments))
|
||||
|> join(:left, [_, p], c in assoc(p, :comments))
|
||||
|> preload(
|
||||
[user, posts, comments, post_comments],
|
||||
comments: comments,
|
||||
posts: {posts, comments: {post_comments, :author}}
|
||||
)
|
||||
|> TestRepo.get(u1.id)
|
||||
|
||||
assert [
|
||||
%{text: "c1", author: %{name: "Alice"}},
|
||||
%{text: "c2", author: %{name: "John"}}
|
||||
] = Enum.sort_by(p1.comments, & &1.text)
|
||||
|
||||
assert [
|
||||
%{text: "c3", author: %{name: "John"}},
|
||||
%{text: "c4", author: nil},
|
||||
%{text: "c5", author: %{name: "Alice"}}
|
||||
] = Enum.sort_by(p2.comments, & &1.text)
|
||||
end
|
||||
|
||||
test "many_to_many association join" do
|
||||
p1 = TestRepo.insert!(%Post{title: "1"})
|
||||
p2 = TestRepo.insert!(%Post{title: "2"})
|
||||
_p = TestRepo.insert!(%Post{title: "3"})
|
||||
u1 = TestRepo.insert!(%User{name: "john"})
|
||||
u2 = TestRepo.insert!(%User{name: "mary"})
|
||||
|
||||
TestRepo.insert_all "posts_users", [[post_id: p1.id, user_id: u1.id],
|
||||
[post_id: p1.id, user_id: u2.id],
|
||||
[post_id: p2.id, user_id: u2.id]]
|
||||
|
||||
query = from(p in Post, join: u in assoc(p, :users), select: {p, u}, order_by: p.id)
|
||||
[{^p1, ^u1}, {^p1, ^u2}, {^p2, ^u2}] = TestRepo.all(query)
|
||||
end
|
||||
|
||||
## Association preload
|
||||
|
||||
test "has_many assoc selector" do
|
||||
p1 = TestRepo.insert!(%Post{title: "1"})
|
||||
p2 = TestRepo.insert!(%Post{title: "2"})
|
||||
|
||||
c1 = TestRepo.insert!(%Comment{text: "1", post_id: p1.id})
|
||||
c2 = TestRepo.insert!(%Comment{text: "2", post_id: p1.id})
|
||||
c3 = TestRepo.insert!(%Comment{text: "3", post_id: p2.id})
|
||||
|
||||
# Without on
|
||||
query = from(p in Post, join: c in assoc(p, :comments), preload: [comments: c])
|
||||
[p1, p2] = TestRepo.all(query)
|
||||
assert p1.comments == [c1, c2]
|
||||
assert p2.comments == [c3]
|
||||
|
||||
# With on
|
||||
query = from(p in Post, left_join: c in assoc(p, :comments),
|
||||
on: p.title == c.text, preload: [comments: c])
|
||||
[p1, p2] = TestRepo.all(query)
|
||||
assert p1.comments == [c1]
|
||||
assert p2.comments == []
|
||||
end
|
||||
|
||||
test "has_one assoc selector" do
|
||||
p1 = TestRepo.insert!(%Post{title: "1"})
|
||||
p2 = TestRepo.insert!(%Post{title: "2"})
|
||||
|
||||
pl1 = TestRepo.insert!(%Permalink{url: "1", post_id: p1.id})
|
||||
_pl = TestRepo.insert!(%Permalink{url: "2"})
|
||||
pl3 = TestRepo.insert!(%Permalink{url: "3", post_id: p2.id})
|
||||
|
||||
query = from(p in Post, join: pl in assoc(p, :permalink), preload: [permalink: pl])
|
||||
assert [post1, post3] = TestRepo.all(query)
|
||||
|
||||
assert post1.permalink == pl1
|
||||
assert post3.permalink == pl3
|
||||
end
|
||||
|
||||
test "belongs_to assoc selector" do
|
||||
p1 = TestRepo.insert!(%Post{title: "1"})
|
||||
p2 = TestRepo.insert!(%Post{title: "2"})
|
||||
|
||||
TestRepo.insert!(%Permalink{url: "1", post_id: p1.id})
|
||||
TestRepo.insert!(%Permalink{url: "2"})
|
||||
TestRepo.insert!(%Permalink{url: "3", post_id: p2.id})
|
||||
|
||||
query = from(pl in Permalink, left_join: p in assoc(pl, :post), preload: [post: p], order_by: pl.id)
|
||||
assert [pl1, pl2, pl3] = TestRepo.all(query)
|
||||
|
||||
assert pl1.post == p1
|
||||
refute pl2.post
|
||||
assert pl3.post == p2
|
||||
end
|
||||
|
||||
test "many_to_many assoc selector" do
|
||||
p1 = TestRepo.insert!(%Post{title: "1"})
|
||||
p2 = TestRepo.insert!(%Post{title: "2"})
|
||||
_p = TestRepo.insert!(%Post{title: "3"})
|
||||
u1 = TestRepo.insert!(%User{name: "1"})
|
||||
u2 = TestRepo.insert!(%User{name: "2"})
|
||||
|
||||
TestRepo.insert_all "posts_users", [[post_id: p1.id, user_id: u1.id],
|
||||
[post_id: p1.id, user_id: u2.id],
|
||||
[post_id: p2.id, user_id: u2.id]]
|
||||
|
||||
# Without on
|
||||
query = from(p in Post, left_join: u in assoc(p, :users), preload: [users: u], order_by: p.id)
|
||||
[p1, p2, p3] = TestRepo.all(query)
|
||||
assert Enum.sort_by(p1.users, & &1.name) == [u1, u2]
|
||||
assert p2.users == [u2]
|
||||
assert p3.users == []
|
||||
|
||||
# With on
|
||||
query = from(p in Post, left_join: u in assoc(p, :users), on: p.title == u.name,
|
||||
preload: [users: u], order_by: p.id)
|
||||
[p1, p2, p3] = TestRepo.all(query)
|
||||
assert p1.users == [u1]
|
||||
assert p2.users == [u2]
|
||||
assert p3.users == []
|
||||
end
|
||||
|
||||
test "has_many through assoc selector" do
|
||||
p1 = TestRepo.insert!(%Post{title: "1"})
|
||||
p2 = TestRepo.insert!(%Post{title: "2"})
|
||||
|
||||
u1 = TestRepo.insert!(%User{name: "1"})
|
||||
u2 = TestRepo.insert!(%User{name: "2"})
|
||||
|
||||
TestRepo.insert!(%Comment{post_id: p1.id, author_id: u1.id})
|
||||
TestRepo.insert!(%Comment{post_id: p1.id, author_id: u1.id})
|
||||
TestRepo.insert!(%Comment{post_id: p1.id, author_id: u2.id})
|
||||
TestRepo.insert!(%Comment{post_id: p2.id, author_id: u2.id})
|
||||
|
||||
# Without on
|
||||
query = from(p in Post, left_join: ca in assoc(p, :comments_authors),
|
||||
preload: [comments_authors: ca])
|
||||
[p1, p2] = TestRepo.all(query)
|
||||
assert Enum.sort_by(p1.comments_authors, & &1.id) == [u1, u2]
|
||||
assert p2.comments_authors == [u2]
|
||||
|
||||
# With on
|
||||
query = from(p in Post, left_join: ca in assoc(p, :comments_authors),
|
||||
on: ca.name == p.title, preload: [comments_authors: ca])
|
||||
[p1, p2] = TestRepo.all(query)
|
||||
assert p1.comments_authors == [u1]
|
||||
assert p2.comments_authors == [u2]
|
||||
end
|
||||
|
||||
test "has_many through-through assoc selector" do
|
||||
%Post{id: pid1} = TestRepo.insert!(%Post{})
|
||||
%Post{id: pid2} = TestRepo.insert!(%Post{})
|
||||
|
||||
%Permalink{} = TestRepo.insert!(%Permalink{post_id: pid1, url: "1"})
|
||||
%Permalink{} = TestRepo.insert!(%Permalink{post_id: pid2, url: "2"})
|
||||
|
||||
%User{id: uid1} = TestRepo.insert!(%User{})
|
||||
%User{id: uid2} = TestRepo.insert!(%User{})
|
||||
|
||||
%Comment{} = TestRepo.insert!(%Comment{post_id: pid1, author_id: uid1})
|
||||
%Comment{} = TestRepo.insert!(%Comment{post_id: pid1, author_id: uid1})
|
||||
%Comment{} = TestRepo.insert!(%Comment{post_id: pid1, author_id: uid2})
|
||||
%Comment{} = TestRepo.insert!(%Comment{post_id: pid2, author_id: uid2})
|
||||
|
||||
query = from(p in Permalink, left_join: ca in assoc(p, :post_comments_authors),
|
||||
preload: [post_comments_authors: ca], order_by: ca.id)
|
||||
|
||||
[l1, l2] = TestRepo.all(query)
|
||||
[u1, u2] = l1.post_comments_authors
|
||||
assert u1.id == uid1
|
||||
assert u2.id == uid2
|
||||
|
||||
[u2] = l2.post_comments_authors
|
||||
assert u2.id == uid2
|
||||
|
||||
# Insert some intermediary joins to check indexes won't be shuffled
|
||||
query = from(p in Permalink,
|
||||
left_join: assoc(p, :post),
|
||||
left_join: ca in assoc(p, :post_comments_authors),
|
||||
left_join: assoc(p, :post),
|
||||
left_join: assoc(p, :post),
|
||||
preload: [post_comments_authors: ca], order_by: ca.id)
|
||||
|
||||
[l1, l2] = TestRepo.all(query)
|
||||
[u1, u2] = l1.post_comments_authors
|
||||
assert u1.id == uid1
|
||||
assert u2.id == uid2
|
||||
|
||||
[u2] = l2.post_comments_authors
|
||||
assert u2.id == uid2
|
||||
end
|
||||
|
||||
## Nested
|
||||
|
||||
test "nested assoc" do
|
||||
%Post{id: pid1} = TestRepo.insert!(%Post{title: "1"})
|
||||
%Post{id: pid2} = TestRepo.insert!(%Post{title: "2"})
|
||||
|
||||
%User{id: uid1} = TestRepo.insert!(%User{name: "1"})
|
||||
%User{id: uid2} = TestRepo.insert!(%User{name: "2"})
|
||||
|
||||
%Comment{id: cid1} = TestRepo.insert!(%Comment{text: "1", post_id: pid1, author_id: uid1})
|
||||
%Comment{id: cid2} = TestRepo.insert!(%Comment{text: "2", post_id: pid1, author_id: uid2})
|
||||
%Comment{id: cid3} = TestRepo.insert!(%Comment{text: "3", post_id: pid2, author_id: uid2})
|
||||
|
||||
# use multiple associations to force parallel preloader
|
||||
query = from p in Post,
|
||||
left_join: c in assoc(p, :comments),
|
||||
left_join: u in assoc(c, :author),
|
||||
order_by: [p.id, c.id, u.id],
|
||||
preload: [:permalink, comments: {c, author: {u, [:comments, :custom]}}],
|
||||
select: {0, [p], 1, 2}
|
||||
|
||||
posts = TestRepo.all(query)
|
||||
assert [p1, p2] = Enum.map(posts, fn {0, [p], 1, 2} -> p end)
|
||||
assert p1.id == pid1
|
||||
assert p2.id == pid2
|
||||
|
||||
assert [c1, c2] = p1.comments
|
||||
assert [c3] = p2.comments
|
||||
assert c1.id == cid1
|
||||
assert c2.id == cid2
|
||||
assert c3.id == cid3
|
||||
|
||||
assert c1.author.id == uid1
|
||||
assert c2.author.id == uid2
|
||||
assert c3.author.id == uid2
|
||||
end
|
||||
|
||||
test "nested assoc with missing entries" do
|
||||
%Post{id: pid1} = TestRepo.insert!(%Post{title: "1"})
|
||||
%Post{id: pid2} = TestRepo.insert!(%Post{title: "2"})
|
||||
%Post{id: pid3} = TestRepo.insert!(%Post{title: "2"})
|
||||
|
||||
%User{id: uid1} = TestRepo.insert!(%User{name: "1"})
|
||||
%User{id: uid2} = TestRepo.insert!(%User{name: "2"})
|
||||
|
||||
%Comment{id: cid1} = TestRepo.insert!(%Comment{text: "1", post_id: pid1, author_id: uid1})
|
||||
%Comment{id: cid2} = TestRepo.insert!(%Comment{text: "2", post_id: pid1, author_id: nil})
|
||||
%Comment{id: cid3} = TestRepo.insert!(%Comment{text: "3", post_id: pid3, author_id: uid2})
|
||||
|
||||
query = from p in Post,
|
||||
left_join: c in assoc(p, :comments),
|
||||
left_join: u in assoc(c, :author),
|
||||
order_by: [p.id, c.id, u.id],
|
||||
preload: [comments: {c, author: u}]
|
||||
|
||||
assert [p1, p2, p3] = TestRepo.all(query)
|
||||
assert p1.id == pid1
|
||||
assert p2.id == pid2
|
||||
assert p3.id == pid3
|
||||
|
||||
assert [c1, c2] = p1.comments
|
||||
assert [] = p2.comments
|
||||
assert [c3] = p3.comments
|
||||
assert c1.id == cid1
|
||||
assert c2.id == cid2
|
||||
assert c3.id == cid3
|
||||
|
||||
assert c1.author.id == uid1
|
||||
assert c2.author == nil
|
||||
assert c3.author.id == uid2
|
||||
end
|
||||
|
||||
test "nested assoc with child preload" do
|
||||
%Post{id: pid1} = TestRepo.insert!(%Post{title: "1"})
|
||||
%Post{id: pid2} = TestRepo.insert!(%Post{title: "2"})
|
||||
|
||||
%User{id: uid1} = TestRepo.insert!(%User{name: "1"})
|
||||
%User{id: uid2} = TestRepo.insert!(%User{name: "2"})
|
||||
|
||||
%Comment{id: cid1} = TestRepo.insert!(%Comment{text: "1", post_id: pid1, author_id: uid1})
|
||||
%Comment{id: cid2} = TestRepo.insert!(%Comment{text: "2", post_id: pid1, author_id: uid2})
|
||||
%Comment{id: cid3} = TestRepo.insert!(%Comment{text: "3", post_id: pid2, author_id: uid2})
|
||||
|
||||
query = from p in Post,
|
||||
left_join: c in assoc(p, :comments),
|
||||
order_by: [p.id, c.id],
|
||||
preload: [comments: {c, :author}],
|
||||
select: p
|
||||
|
||||
assert [p1, p2] = TestRepo.all(query)
|
||||
assert p1.id == pid1
|
||||
assert p2.id == pid2
|
||||
|
||||
assert [c1, c2] = p1.comments
|
||||
assert [c3] = p2.comments
|
||||
assert c1.id == cid1
|
||||
assert c2.id == cid2
|
||||
assert c3.id == cid3
|
||||
|
||||
assert c1.author.id == uid1
|
||||
assert c2.author.id == uid2
|
||||
assert c3.author.id == uid2
|
||||
end
|
||||
|
||||
test "nested assoc with sibling preload" do
|
||||
%Post{id: pid1} = TestRepo.insert!(%Post{title: "1"})
|
||||
%Post{id: pid2} = TestRepo.insert!(%Post{title: "2"})
|
||||
|
||||
%Permalink{id: plid1} = TestRepo.insert!(%Permalink{url: "1", post_id: pid2})
|
||||
|
||||
%Comment{id: cid1} = TestRepo.insert!(%Comment{text: "1", post_id: pid1})
|
||||
%Comment{id: cid2} = TestRepo.insert!(%Comment{text: "2", post_id: pid2})
|
||||
%Comment{id: _} = TestRepo.insert!(%Comment{text: "3", post_id: pid2})
|
||||
|
||||
query = from p in Post,
|
||||
left_join: c in assoc(p, :comments),
|
||||
where: c.text in ~w(1 2),
|
||||
preload: [:permalink, comments: c],
|
||||
select: {0, [p], 1, 2}
|
||||
|
||||
posts = TestRepo.all(query)
|
||||
assert [p1, p2] = Enum.map(posts, fn {0, [p], 1, 2} -> p end)
|
||||
assert p1.id == pid1
|
||||
assert p2.id == pid2
|
||||
|
||||
assert p2.permalink.id == plid1
|
||||
|
||||
assert [c1] = p1.comments
|
||||
assert [c2] = p2.comments
|
||||
assert c1.id == cid1
|
||||
assert c2.id == cid2
|
||||
end
|
||||
|
||||
test "mixing regular join and assoc selector" do
|
||||
p1 = TestRepo.insert!(%Post{title: "1"})
|
||||
p2 = TestRepo.insert!(%Post{title: "2"})
|
||||
|
||||
c1 = TestRepo.insert!(%Comment{text: "1", post_id: p1.id})
|
||||
c2 = TestRepo.insert!(%Comment{text: "2", post_id: p1.id})
|
||||
c3 = TestRepo.insert!(%Comment{text: "3", post_id: p2.id})
|
||||
|
||||
pl1 = TestRepo.insert!(%Permalink{url: "1", post_id: p1.id})
|
||||
_pl = TestRepo.insert!(%Permalink{url: "2"})
|
||||
pl3 = TestRepo.insert!(%Permalink{url: "3", post_id: p2.id})
|
||||
|
||||
# Without on
|
||||
query = from(p in Post, join: pl in assoc(p, :permalink),
|
||||
join: c in assoc(p, :comments),
|
||||
preload: [permalink: pl],
|
||||
select: {p, c})
|
||||
[{p1, ^c1}, {p1, ^c2}, {p2, ^c3}] = TestRepo.all(query)
|
||||
assert p1.permalink == pl1
|
||||
assert p2.permalink == pl3
|
||||
end
|
||||
|
||||
test "association with composite pk join" do
|
||||
post = TestRepo.insert!(%Post{title: "1"})
|
||||
user = TestRepo.insert!(%User{name: "1"})
|
||||
TestRepo.insert!(%PostUserCompositePk{post_id: post.id, user_id: user.id})
|
||||
|
||||
query = from(p in Post, join: a in assoc(p, :post_user_composite_pk),
|
||||
preload: [post_user_composite_pk: a], select: p)
|
||||
assert [post] = TestRepo.all(query)
|
||||
assert post.post_user_composite_pk
|
||||
end
|
||||
|
||||
test "joining a through association with a nested preloads" do
|
||||
post = TestRepo.insert!(%Post{title: "1"})
|
||||
user = TestRepo.insert!(%User{name: "1"})
|
||||
TestRepo.insert!(%Comment{text: "1", post_id: post.id})
|
||||
TestRepo.insert!(%Permalink{post_id: post.id, user_id: user.id})
|
||||
|
||||
query =
|
||||
from c in Comment,
|
||||
join: pp in assoc(c, :post_permalink),
|
||||
join: u in assoc(pp, :user),
|
||||
preload: [post_permalink: {pp, [:post, user: u]}]
|
||||
|
||||
[comment] = TestRepo.all(query)
|
||||
|
||||
assert not Ecto.assoc_loaded?(comment.post)
|
||||
assert %Permalink{user: %User{}, post: %Post{}} = comment.post_permalink
|
||||
end
|
||||
|
||||
test "joining multiple through associations with a nested preloads" do
|
||||
post = TestRepo.insert!(%Post{title: "1"})
|
||||
user = TestRepo.insert!(%User{name: "1"})
|
||||
TestRepo.insert!(%Comment{text: "1", post_id: post.id, author_id: user.id})
|
||||
TestRepo.insert!(%Permalink{post_id: post.id, user_id: user.id})
|
||||
|
||||
query =
|
||||
from c in Comment,
|
||||
join: pp in assoc(c, :post_permalink),
|
||||
join: ap in assoc(c, :author_permalink),
|
||||
join: u1 in assoc(pp, :user),
|
||||
join: u2 in assoc(ap, :user),
|
||||
preload: [post_permalink: {pp, [:post, user: u1]}, author_permalink: {ap, [:post, user: u2]}]
|
||||
|
||||
[comment] = TestRepo.all(query)
|
||||
|
||||
assert not Ecto.assoc_loaded?(comment.post)
|
||||
assert not Ecto.assoc_loaded?(comment.author)
|
||||
assert %Permalink{user: %User{}, post: %Post{}} = comment.post_permalink
|
||||
assert %Permalink{user: %User{}, post: %Post{}} = comment.author_permalink
|
||||
end
|
||||
|
||||
test "joining nested through associations with a nested preloads" do
|
||||
user = TestRepo.insert!(%User{name: "1"})
|
||||
post = TestRepo.insert!(%Post{title: "1", author_id: user.id})
|
||||
TestRepo.insert!(%Comment{text: "1", post_id: post.id})
|
||||
TestRepo.insert!(%Permalink{post_id: post.id, user_id: user.id})
|
||||
|
||||
query =
|
||||
from c in Comment,
|
||||
join: pp in assoc(c, :post_permalink),
|
||||
join: up in assoc(pp, :user_posts),
|
||||
preload: [post_permalink: {pp, [:post, user_posts: {up, :comments}]}]
|
||||
|
||||
[comment] = TestRepo.all(query)
|
||||
|
||||
assert not Ecto.assoc_loaded?(comment.post)
|
||||
assert %Permalink{post: %Post{}, user_posts: [%Post{}]} = comment.post_permalink
|
||||
assert not Ecto.assoc_loaded?(comment.post_permalink.user)
|
||||
end
|
||||
|
||||
test "joining and preloading through a subquery" do
|
||||
%{id: p_id} = TestRepo.insert!(%Post{})
|
||||
%{id: c1_id} = TestRepo.insert!(%Comment{post_id: p_id})
|
||||
%{id: c2_id} = TestRepo.insert!(%Comment{post_id: p_id})
|
||||
|
||||
q =
|
||||
from p1 in Post,
|
||||
left_join: u in User,
|
||||
on: p1.author_id == u.id,
|
||||
inner_join: c in subquery(from c in Comment),
|
||||
on: p1.id == c.post_id,
|
||||
join: p2 in Post,
|
||||
on: c.post_id == p2.id,
|
||||
preload: [author: u, force_comments: {c, post: p2}]
|
||||
|
||||
assert [%Post{id: ^p_id, force_comments: comments}] = TestRepo.all(q)
|
||||
[comment1, comment2] = Enum.sort_by(comments, & &1.id)
|
||||
assert %Comment{id: ^c1_id, post: %Post{id: ^p_id}} = comment1
|
||||
assert %Comment{id: ^c2_id, post: %Post{id: ^p_id}} = comment2
|
||||
end
|
||||
end
|
||||
866
phoenix/deps/ecto/integration_test/cases/preload.exs
Normal file
866
phoenix/deps/ecto/integration_test/cases/preload.exs
Normal file
@@ -0,0 +1,866 @@
|
||||
defmodule Ecto.Integration.PreloadTest do
|
||||
use Ecto.Integration.Case, async: Application.compile_env(:ecto, :async_integration_tests, true)
|
||||
|
||||
alias Ecto.Integration.TestRepo
|
||||
import Ecto.Query
|
||||
|
||||
alias Ecto.Integration.Post
|
||||
alias Ecto.Integration.Comment
|
||||
alias Ecto.Integration.Item
|
||||
alias Ecto.Integration.Permalink
|
||||
alias Ecto.Integration.User
|
||||
alias Ecto.Integration.Custom
|
||||
alias Ecto.Integration.Order
|
||||
|
||||
test "preload with parameter from select_merge" do
|
||||
p1 = TestRepo.insert!(%Post{title: "p1"})
|
||||
TestRepo.insert!(%Comment{text: "c1", post: p1})
|
||||
|
||||
comments =
|
||||
from(c in Comment, select: struct(c, [:text]))
|
||||
|> select_merge([c], %{post_id: c.post_id})
|
||||
|> preload(:post)
|
||||
|> TestRepo.all()
|
||||
|
||||
assert [%{text: "c1", post: %{title: "p1"}}] = comments
|
||||
end
|
||||
|
||||
test "preload has_many" do
|
||||
p1 = TestRepo.insert!(%Post{title: "1"})
|
||||
p2 = TestRepo.insert!(%Post{title: "2"})
|
||||
p3 = TestRepo.insert!(%Post{title: "3"})
|
||||
|
||||
# We use the same text to expose bugs in preload sorting
|
||||
%Comment{id: cid1} = TestRepo.insert!(%Comment{text: "1", post_id: p1.id})
|
||||
%Comment{id: cid3} = TestRepo.insert!(%Comment{text: "2", post_id: p2.id})
|
||||
%Comment{id: cid2} = TestRepo.insert!(%Comment{text: "2", post_id: p1.id})
|
||||
%Comment{id: cid4} = TestRepo.insert!(%Comment{text: "3", post_id: p2.id})
|
||||
|
||||
assert %Ecto.Association.NotLoaded{} = p1.comments
|
||||
|
||||
[p3, p1, p2] = TestRepo.preload([p3, p1, p2], :comments)
|
||||
assert [%Comment{id: ^cid1}, %Comment{id: ^cid2}] = p1.comments |> sort_by_id()
|
||||
assert [%Comment{id: ^cid3}, %Comment{id: ^cid4}] = p2.comments |> sort_by_id()
|
||||
assert [] = p3.comments
|
||||
end
|
||||
|
||||
test "preload has_many multiple times" do
|
||||
p1 = TestRepo.insert!(%Post{title: "1"})
|
||||
%Comment{id: cid1} = TestRepo.insert!(%Comment{text: "1", post_id: p1.id})
|
||||
%Comment{id: cid2} = TestRepo.insert!(%Comment{text: "2", post_id: p1.id})
|
||||
|
||||
[p1, p1] = TestRepo.preload([p1, p1], :comments)
|
||||
assert [%Comment{id: ^cid1}, %Comment{id: ^cid2}] = p1.comments |> sort_by_id()
|
||||
|
||||
[p1, p1] = TestRepo.preload([p1, p1], :comments)
|
||||
assert [%Comment{id: ^cid1}, %Comment{id: ^cid2}] = p1.comments |> sort_by_id()
|
||||
end
|
||||
|
||||
test "preload has_one" do
|
||||
p1 = TestRepo.insert!(%Post{title: "1"})
|
||||
p2 = TestRepo.insert!(%Post{title: "2"})
|
||||
p3 = TestRepo.insert!(%Post{title: "3"})
|
||||
|
||||
%Permalink{id: pid1} = TestRepo.insert!(%Permalink{url: "1", post_id: p1.id})
|
||||
%Permalink{} = TestRepo.insert!(%Permalink{url: "2", post_id: nil})
|
||||
%Permalink{id: pid3} = TestRepo.insert!(%Permalink{url: "3", post_id: p3.id})
|
||||
|
||||
assert %Ecto.Association.NotLoaded{} = p1.permalink
|
||||
assert %Ecto.Association.NotLoaded{} = p2.permalink
|
||||
|
||||
[p3, p1, p2] = TestRepo.preload([p3, p1, p2], :permalink)
|
||||
assert %Permalink{id: ^pid1} = p1.permalink
|
||||
refute p2.permalink
|
||||
assert %Permalink{id: ^pid3} = p3.permalink
|
||||
end
|
||||
|
||||
test "preload belongs_to" do
|
||||
%Post{id: pid1} = TestRepo.insert!(%Post{title: "1"})
|
||||
TestRepo.insert!(%Post{title: "2"})
|
||||
%Post{id: pid3} = TestRepo.insert!(%Post{title: "3"})
|
||||
|
||||
pl1 = TestRepo.insert!(%Permalink{url: "1", post_id: pid1})
|
||||
pl2 = TestRepo.insert!(%Permalink{url: "2", post_id: nil})
|
||||
pl3 = TestRepo.insert!(%Permalink{url: "3", post_id: pid3})
|
||||
assert %Ecto.Association.NotLoaded{} = pl1.post
|
||||
|
||||
[pl3, pl1, pl2] = TestRepo.preload([pl3, pl1, pl2], :post)
|
||||
assert %Post{id: ^pid1} = pl1.post
|
||||
refute pl2.post
|
||||
assert %Post{id: ^pid3} = pl3.post
|
||||
end
|
||||
|
||||
test "preload multiple belongs_to" do
|
||||
%User{id: uid} = TestRepo.insert!(%User{name: "foo"})
|
||||
%Post{id: pid} = TestRepo.insert!(%Post{title: "1"})
|
||||
%Comment{id: cid} = TestRepo.insert!(%Comment{post_id: pid, author_id: uid})
|
||||
|
||||
comment = TestRepo.get!(Comment, cid)
|
||||
comment = TestRepo.preload(comment, [:author, :post])
|
||||
assert comment.author.id == uid
|
||||
assert comment.post.id == pid
|
||||
end
|
||||
|
||||
test "preload belongs_to with shared parent" do
|
||||
%Post{id: pid1} = TestRepo.insert!(%Post{title: "1"})
|
||||
%Post{id: pid2} = TestRepo.insert!(%Post{title: "2"})
|
||||
|
||||
c1 = TestRepo.insert!(%Comment{text: "1", post_id: pid1})
|
||||
c2 = TestRepo.insert!(%Comment{text: "2", post_id: pid1})
|
||||
c3 = TestRepo.insert!(%Comment{text: "3", post_id: pid2})
|
||||
|
||||
[c3, c1, c2] = TestRepo.preload([c3, c1, c2], :post)
|
||||
assert %Post{id: ^pid1} = c1.post
|
||||
assert %Post{id: ^pid1} = c2.post
|
||||
assert %Post{id: ^pid2} = c3.post
|
||||
end
|
||||
|
||||
test "preload many_to_many" do
|
||||
p1 = TestRepo.insert!(%Post{title: "1"})
|
||||
p2 = TestRepo.insert!(%Post{title: "2"})
|
||||
p3 = TestRepo.insert!(%Post{title: "3"})
|
||||
|
||||
# We use the same name to expose bugs in preload sorting
|
||||
%User{id: uid1} = TestRepo.insert!(%User{name: "1"})
|
||||
%User{id: uid3} = TestRepo.insert!(%User{name: "2"})
|
||||
%User{id: uid2} = TestRepo.insert!(%User{name: "2"})
|
||||
%User{id: uid4} = TestRepo.insert!(%User{name: "3"})
|
||||
|
||||
TestRepo.insert_all "posts_users", [[post_id: p1.id, user_id: uid1],
|
||||
[post_id: p1.id, user_id: uid2],
|
||||
[post_id: p2.id, user_id: uid3],
|
||||
[post_id: p2.id, user_id: uid4],
|
||||
[post_id: p3.id, user_id: uid1],
|
||||
[post_id: p3.id, user_id: uid4]]
|
||||
|
||||
assert %Ecto.Association.NotLoaded{} = p1.users
|
||||
|
||||
[p1, p2, p3] = TestRepo.preload([p1, p2, p3], :users)
|
||||
assert [%User{id: ^uid1}, %User{id: ^uid2}] = p1.users |> sort_by_id
|
||||
assert [%User{id: ^uid3}, %User{id: ^uid4}] = p2.users |> sort_by_id
|
||||
assert [%User{id: ^uid1}, %User{id: ^uid4}] = p3.users |> sort_by_id
|
||||
end
|
||||
|
||||
test "preload has_many through" do
|
||||
%Post{id: pid1} = p1 = TestRepo.insert!(%Post{})
|
||||
%Post{id: pid2} = p2 = TestRepo.insert!(%Post{})
|
||||
|
||||
%User{id: uid1} = TestRepo.insert!(%User{name: "foo"})
|
||||
%User{id: uid2} = TestRepo.insert!(%User{name: "bar"})
|
||||
|
||||
%Comment{} = TestRepo.insert!(%Comment{post_id: pid1, author_id: uid1})
|
||||
%Comment{} = TestRepo.insert!(%Comment{post_id: pid1, author_id: uid1})
|
||||
%Comment{} = TestRepo.insert!(%Comment{post_id: pid1, author_id: uid2})
|
||||
%Comment{} = TestRepo.insert!(%Comment{post_id: pid2, author_id: uid2})
|
||||
|
||||
[p1, p2] = TestRepo.preload([p1, p2], :comments_authors)
|
||||
|
||||
# Through was preloaded
|
||||
[u1, u2] = p1.comments_authors |> sort_by_id
|
||||
assert u1.id == uid1
|
||||
assert u2.id == uid2
|
||||
|
||||
[u2] = p2.comments_authors
|
||||
assert u2.id == uid2
|
||||
|
||||
# But we also preloaded everything along the way
|
||||
assert [c1, c2, c3] = p1.comments |> sort_by_id
|
||||
assert c1.author.id == uid1
|
||||
assert c2.author.id == uid1
|
||||
assert c3.author.id == uid2
|
||||
|
||||
assert [c4] = p2.comments
|
||||
assert c4.author.id == uid2
|
||||
end
|
||||
|
||||
test "preload has_one through" do
|
||||
%Post{id: pid1} = TestRepo.insert!(%Post{})
|
||||
%Post{id: pid2} = TestRepo.insert!(%Post{})
|
||||
|
||||
%Permalink{id: lid1} = TestRepo.insert!(%Permalink{post_id: pid1, url: "1"})
|
||||
%Permalink{id: lid2} = TestRepo.insert!(%Permalink{post_id: pid2, url: "2"})
|
||||
|
||||
%Comment{} = c1 = TestRepo.insert!(%Comment{post_id: pid1})
|
||||
%Comment{} = c2 = TestRepo.insert!(%Comment{post_id: pid1})
|
||||
%Comment{} = c3 = TestRepo.insert!(%Comment{post_id: pid2})
|
||||
|
||||
[c1, c2, c3] = TestRepo.preload([c1, c2, c3], :post_permalink)
|
||||
|
||||
# Through was preloaded
|
||||
assert c1.post.id == pid1
|
||||
assert c1.post.permalink.id == lid1
|
||||
assert c1.post_permalink.id == lid1
|
||||
|
||||
assert c2.post.id == pid1
|
||||
assert c2.post.permalink.id == lid1
|
||||
assert c2.post_permalink.id == lid1
|
||||
|
||||
assert c3.post.id == pid2
|
||||
assert c3.post.permalink.id == lid2
|
||||
assert c3.post_permalink.id == lid2
|
||||
end
|
||||
|
||||
test "preload through with nil association" do
|
||||
%Comment{} = c = TestRepo.insert!(%Comment{post_id: nil})
|
||||
|
||||
c = TestRepo.preload(c, [:post, :post_permalink])
|
||||
assert c.post == nil
|
||||
assert c.post_permalink == nil
|
||||
|
||||
c = TestRepo.preload(c, [:post, :post_permalink])
|
||||
assert c.post == nil
|
||||
assert c.post_permalink == nil
|
||||
end
|
||||
|
||||
test "preload through with nil struct" do
|
||||
%Comment{} = c = TestRepo.insert!(%Comment{})
|
||||
[%Comment{}, nil] = TestRepo.preload([c, nil], [:post, :post_permalink])
|
||||
end
|
||||
|
||||
test "preload has_many through-through" do
|
||||
%Post{id: pid1} = TestRepo.insert!(%Post{})
|
||||
%Post{id: pid2} = TestRepo.insert!(%Post{})
|
||||
|
||||
%Permalink{} = l1 = TestRepo.insert!(%Permalink{post_id: pid1, url: "1"})
|
||||
%Permalink{} = l2 = TestRepo.insert!(%Permalink{post_id: pid2, url: "2"})
|
||||
|
||||
%User{id: uid1} = TestRepo.insert!(%User{name: "foo"})
|
||||
%User{id: uid2} = TestRepo.insert!(%User{name: "bar"})
|
||||
|
||||
%Comment{} = TestRepo.insert!(%Comment{post_id: pid1, author_id: uid1})
|
||||
%Comment{} = TestRepo.insert!(%Comment{post_id: pid1, author_id: uid1})
|
||||
%Comment{} = TestRepo.insert!(%Comment{post_id: pid1, author_id: uid2})
|
||||
%Comment{} = TestRepo.insert!(%Comment{post_id: pid2, author_id: uid2})
|
||||
|
||||
# With assoc query
|
||||
[l1, l2] = TestRepo.preload([l1, l2], :post_comments_authors)
|
||||
|
||||
# Through was preloaded
|
||||
[u1, u2] = l1.post_comments_authors |> sort_by_id
|
||||
assert u1.id == uid1
|
||||
assert u2.id == uid2
|
||||
|
||||
[u2] = l2.post_comments_authors
|
||||
assert u2.id == uid2
|
||||
|
||||
# But we also preloaded everything along the way
|
||||
assert l1.post.id == pid1
|
||||
assert l1.post.comments != []
|
||||
|
||||
assert l2.post.id == pid2
|
||||
assert l2.post.comments != []
|
||||
end
|
||||
|
||||
test "preload has_many through many_to_many" do
|
||||
%Post{} = p1 = TestRepo.insert!(%Post{})
|
||||
%Post{} = p2 = TestRepo.insert!(%Post{})
|
||||
|
||||
%User{id: uid1} = TestRepo.insert!(%User{name: "foo"})
|
||||
%User{id: uid2} = TestRepo.insert!(%User{name: "bar"})
|
||||
|
||||
TestRepo.insert_all "posts_users", [[post_id: p1.id, user_id: uid1],
|
||||
[post_id: p1.id, user_id: uid2],
|
||||
[post_id: p2.id, user_id: uid2]]
|
||||
|
||||
%Comment{id: cid1} = TestRepo.insert!(%Comment{author_id: uid1})
|
||||
%Comment{id: cid2} = TestRepo.insert!(%Comment{author_id: uid1})
|
||||
%Comment{id: cid3} = TestRepo.insert!(%Comment{author_id: uid2})
|
||||
%Comment{id: cid4} = TestRepo.insert!(%Comment{author_id: uid2})
|
||||
|
||||
[p1, p2] = TestRepo.preload([p1, p2], :users_comments)
|
||||
|
||||
# Through was preloaded
|
||||
[c1, c2, c3, c4] = p1.users_comments |> sort_by_id
|
||||
assert c1.id == cid1
|
||||
assert c2.id == cid2
|
||||
assert c3.id == cid3
|
||||
assert c4.id == cid4
|
||||
|
||||
[c3, c4] = p2.users_comments |> sort_by_id
|
||||
assert c3.id == cid3
|
||||
assert c4.id == cid4
|
||||
|
||||
# But we also preloaded everything along the way
|
||||
assert [u1, u2] = p1.users |> sort_by_id
|
||||
assert u1.id == uid1
|
||||
assert u2.id == uid2
|
||||
|
||||
assert [u2] = p2.users
|
||||
assert u2.id == uid2
|
||||
end
|
||||
|
||||
## Empties
|
||||
|
||||
test "preload empty" do
|
||||
assert TestRepo.preload([], :anything_goes) == []
|
||||
end
|
||||
|
||||
test "preload has_many with no associated entries" do
|
||||
p = TestRepo.insert!(%Post{title: "1"})
|
||||
p = TestRepo.preload(p, :comments)
|
||||
|
||||
assert p.title == "1"
|
||||
assert p.comments == []
|
||||
end
|
||||
|
||||
test "preload has_one with no associated entries" do
|
||||
p = TestRepo.insert!(%Post{title: "1"})
|
||||
p = TestRepo.preload(p, :permalink)
|
||||
|
||||
assert p.title == "1"
|
||||
assert p.permalink == nil
|
||||
end
|
||||
|
||||
test "preload belongs_to with no associated entry" do
|
||||
c = TestRepo.insert!(%Comment{text: "1"})
|
||||
c = TestRepo.preload(c, :post)
|
||||
|
||||
assert c.text == "1"
|
||||
assert c.post == nil
|
||||
end
|
||||
|
||||
test "preload many_to_many with no associated entries" do
|
||||
p = TestRepo.insert!(%Post{title: "1"})
|
||||
p = TestRepo.preload(p, :users)
|
||||
|
||||
assert p.title == "1"
|
||||
assert p.users == []
|
||||
end
|
||||
|
||||
## With queries
|
||||
|
||||
test "preload with 1-arity function" do
|
||||
p1 = TestRepo.insert!(%Post{title: "1"})
|
||||
p2 = TestRepo.insert!(%Post{title: "2"})
|
||||
p3 = TestRepo.insert!(%Post{title: "3"})
|
||||
|
||||
# We use the same text to expose bugs in preload sorting
|
||||
%Comment{id: cid1} = TestRepo.insert!(%Comment{text: "1", post_id: p1.id})
|
||||
%Comment{id: cid3} = TestRepo.insert!(%Comment{text: "2", post_id: p2.id})
|
||||
%Comment{id: cid2} = TestRepo.insert!(%Comment{text: "2", post_id: p1.id})
|
||||
%Comment{id: cid4} = TestRepo.insert!(%Comment{text: "3", post_id: p2.id})
|
||||
|
||||
assert [pe3, pe1, pe2] = TestRepo.preload([p3, p1, p2],
|
||||
comments: fn _ -> TestRepo.all(Comment) end)
|
||||
assert [%Comment{id: ^cid1}, %Comment{id: ^cid2}] = pe1.comments
|
||||
assert [%Comment{id: ^cid3}, %Comment{id: ^cid4}] = pe2.comments
|
||||
assert [] = pe3.comments
|
||||
end
|
||||
|
||||
test "preload with 2-arity function" do
|
||||
p = TestRepo.insert!(%Post{title: "1"})
|
||||
c1 = TestRepo.insert!(%Comment{post_id: p.id})
|
||||
c2 = TestRepo.insert!(%Comment{post_id: p.id})
|
||||
|
||||
# making a simple preloader so that it works across all adapters
|
||||
preloader = fn parent_ids, assoc ->
|
||||
%{related_key: related_key, queryable: queryable} = assoc
|
||||
|
||||
from(q in queryable, where: field(q, ^related_key) in ^parent_ids, order_by: q.id)
|
||||
|> TestRepo.all()
|
||||
end
|
||||
|
||||
assert p = TestRepo.preload(p, comments: preloader)
|
||||
assert [^c1, ^c2] = p.comments
|
||||
end
|
||||
|
||||
test "preload many_to_many with function" do
|
||||
p1 = TestRepo.insert!(%Post{title: "1"})
|
||||
p2 = TestRepo.insert!(%Post{title: "2"})
|
||||
p3 = TestRepo.insert!(%Post{title: "3"})
|
||||
|
||||
# We use the same name to expose bugs in preload sorting
|
||||
%User{id: uid1} = TestRepo.insert!(%User{name: "1"})
|
||||
%User{id: uid3} = TestRepo.insert!(%User{name: "2"})
|
||||
%User{id: uid2} = TestRepo.insert!(%User{name: "2"})
|
||||
%User{id: uid4} = TestRepo.insert!(%User{name: "3"})
|
||||
|
||||
TestRepo.insert_all "posts_users", [[post_id: p1.id, user_id: uid1],
|
||||
[post_id: p1.id, user_id: uid2],
|
||||
[post_id: p2.id, user_id: uid3],
|
||||
[post_id: p2.id, user_id: uid4],
|
||||
[post_id: p3.id, user_id: uid1],
|
||||
[post_id: p3.id, user_id: uid4]]
|
||||
|
||||
wrong_preloader = fn post_ids ->
|
||||
TestRepo.all(
|
||||
from u in User,
|
||||
join: pu in "posts_users",
|
||||
on: true,
|
||||
where: pu.post_id in ^post_ids and pu.user_id == u.id,
|
||||
order_by: u.id,
|
||||
select: map(u, [:id])
|
||||
)
|
||||
end
|
||||
|
||||
assert_raise RuntimeError, ~r/invalid custom preload for `users` on `Ecto.Integration.Post`/, fn ->
|
||||
TestRepo.preload([p1, p2, p3], users: wrong_preloader)
|
||||
end
|
||||
|
||||
right_preloader = fn post_ids ->
|
||||
TestRepo.all(
|
||||
from u in User,
|
||||
join: pu in "posts_users",
|
||||
on: true,
|
||||
where: pu.post_id in ^post_ids and pu.user_id == u.id,
|
||||
order_by: u.id,
|
||||
select: {pu.post_id, map(u, [:id])}
|
||||
)
|
||||
end
|
||||
|
||||
[p1, p2, p3] = TestRepo.preload([p1, p2, p3], users: right_preloader)
|
||||
assert p1.users == [%{id: uid1}, %{id: uid2}]
|
||||
assert p2.users == [%{id: uid3}, %{id: uid4}]
|
||||
assert p3.users == [%{id: uid1}, %{id: uid4}]
|
||||
end
|
||||
|
||||
test "preload with query" do
|
||||
p1 = TestRepo.insert!(%Post{title: "1"})
|
||||
p2 = TestRepo.insert!(%Post{title: "2"})
|
||||
p3 = TestRepo.insert!(%Post{title: "3"})
|
||||
|
||||
# We use the same text to expose bugs in preload sorting
|
||||
%Comment{id: cid1} = TestRepo.insert!(%Comment{text: "1", post_id: p1.id})
|
||||
%Comment{id: cid3} = TestRepo.insert!(%Comment{text: "2", post_id: p2.id})
|
||||
%Comment{id: cid2} = TestRepo.insert!(%Comment{text: "2", post_id: p1.id})
|
||||
%Comment{id: cid4} = TestRepo.insert!(%Comment{text: "3", post_id: p2.id})
|
||||
|
||||
assert %Ecto.Association.NotLoaded{} = p1.comments
|
||||
|
||||
# With empty query
|
||||
assert [pe3, pe1, pe2] = TestRepo.preload([p3, p1, p2],
|
||||
comments: from(c in Comment, where: false))
|
||||
assert [] = pe1.comments
|
||||
assert [] = pe2.comments
|
||||
assert [] = pe3.comments
|
||||
|
||||
# With custom select
|
||||
assert [pe3, pe1, pe2] = TestRepo.preload([p3, p1, p2],
|
||||
comments: from(c in Comment, select: c.id, order_by: c.id))
|
||||
assert [^cid1, ^cid2] = pe1.comments
|
||||
assert [^cid3, ^cid4] = pe2.comments
|
||||
assert [] = pe3.comments
|
||||
|
||||
# With custom ordered query
|
||||
assert [pe3, pe1, pe2] = TestRepo.preload([p3, p1, p2],
|
||||
comments: from(c in Comment, order_by: [desc: c.text]))
|
||||
assert [%Comment{id: ^cid2}, %Comment{id: ^cid1}] = pe1.comments
|
||||
assert [%Comment{id: ^cid4}, %Comment{id: ^cid3}] = pe2.comments
|
||||
assert [] = pe3.comments
|
||||
|
||||
# With custom ordered query with preload
|
||||
assert [pe3, pe1, pe2] = TestRepo.preload([p3, p1, p2],
|
||||
comments: {from(c in Comment, order_by: [desc: c.text]), :post})
|
||||
assert [%Comment{id: ^cid2} = c2, %Comment{id: ^cid1} = c1] = pe1.comments
|
||||
assert [%Comment{id: ^cid4} = c4, %Comment{id: ^cid3} = c3] = pe2.comments
|
||||
assert [] = pe3.comments
|
||||
|
||||
assert c1.post.title == "1"
|
||||
assert c2.post.title == "1"
|
||||
assert c3.post.title == "2"
|
||||
assert c4.post.title == "2"
|
||||
end
|
||||
|
||||
test "preload through with query" do
|
||||
%Post{id: pid1} = p1 = TestRepo.insert!(%Post{})
|
||||
|
||||
u1 = TestRepo.insert!(%User{name: "foo"})
|
||||
u2 = TestRepo.insert!(%User{name: "bar"})
|
||||
u3 = TestRepo.insert!(%User{name: "baz"})
|
||||
u4 = TestRepo.insert!(%User{name: "norf"})
|
||||
|
||||
%Comment{} = TestRepo.insert!(%Comment{post_id: pid1, author_id: u1.id})
|
||||
%Comment{} = TestRepo.insert!(%Comment{post_id: pid1, author_id: u1.id})
|
||||
%Comment{} = TestRepo.insert!(%Comment{post_id: pid1, author_id: u2.id})
|
||||
%Comment{} = TestRepo.insert!(%Comment{post_id: pid1, author_id: u3.id})
|
||||
%Comment{} = TestRepo.insert!(%Comment{post_id: pid1, author_id: u4.id})
|
||||
|
||||
np1 = TestRepo.preload(p1, comments_authors: from(u in User, where: u.name == "foo"))
|
||||
assert np1.comments_authors == [u1]
|
||||
|
||||
assert_raise ArgumentError, ~r/Ecto expected a map\/struct with the key `id` but got: \d+/, fn ->
|
||||
TestRepo.preload(p1, comments_authors: from(u in User, order_by: u.name, select: u.id))
|
||||
end
|
||||
|
||||
# The subpreload order does not matter because the result is dictated by comments
|
||||
np1 = TestRepo.preload(p1, comments_authors: from(u in User, order_by: u.name, select: %{id: u.id}))
|
||||
assert np1.comments_authors ==
|
||||
[%{id: u1.id}, %{id: u2.id}, %{id: u3.id}, %{id: u4.id}]
|
||||
end
|
||||
|
||||
test "preload into a subquery source" do
|
||||
%{id: p_id} = TestRepo.insert!(%Post{})
|
||||
%{id: c_id} = TestRepo.insert!(%Comment{post_id: p_id})
|
||||
|
||||
q =
|
||||
from c in subquery(from c in Comment),
|
||||
join: p in Post,
|
||||
on: c.post_id == p.id,
|
||||
preload: [post: p]
|
||||
|
||||
assert [%Comment{id: ^c_id, post: %Post{id: ^p_id}}] = TestRepo.all(q)
|
||||
end
|
||||
|
||||
## With take
|
||||
|
||||
test "preload with take" do
|
||||
p1 = TestRepo.insert!(%Post{title: "1"})
|
||||
p2 = TestRepo.insert!(%Post{title: "2"})
|
||||
_p = TestRepo.insert!(%Post{title: "3"})
|
||||
|
||||
%Comment{id: cid1} = TestRepo.insert!(%Comment{text: "1", post_id: p1.id})
|
||||
%Comment{id: cid3} = TestRepo.insert!(%Comment{text: "2", post_id: p2.id})
|
||||
%Comment{id: cid2} = TestRepo.insert!(%Comment{text: "2", post_id: p1.id})
|
||||
%Comment{id: cid4} = TestRepo.insert!(%Comment{text: "3", post_id: p2.id})
|
||||
|
||||
assert %Ecto.Association.NotLoaded{} = p1.comments
|
||||
|
||||
posts = TestRepo.all(from Post, preload: [:comments], select: [:id, comments: [:id, :post_id]])
|
||||
[p1, p2, p3] = sort_by_id(posts)
|
||||
assert p1.title == nil
|
||||
assert p2.title == nil
|
||||
assert p3.title == nil
|
||||
|
||||
assert [%{id: ^cid1, text: nil}, %{id: ^cid2, text: nil}] = sort_by_id(p1.comments)
|
||||
assert [%{id: ^cid3, text: nil}, %{id: ^cid4, text: nil}] = sort_by_id(p2.comments)
|
||||
assert [] = sort_by_id(p3.comments)
|
||||
end
|
||||
|
||||
test "take with join nil maps (many association)" do
|
||||
p = TestRepo.insert!(%Post{})
|
||||
|
||||
# many
|
||||
query =
|
||||
from p in Post,
|
||||
left_join: c in Comment,
|
||||
on: p.id == c.post_id,
|
||||
select: map(p, [:id, comments: [:id, :post_id]]),
|
||||
preload: [comments: c]
|
||||
|
||||
assert TestRepo.one(query) == %{id: p.id, comments: []}
|
||||
|
||||
query =
|
||||
from p in Post,
|
||||
left_join: c in Comment,
|
||||
on: p.id == c.post_id,
|
||||
select: map(p, [:id, comments: [:id, :post_id]]),
|
||||
preload: [:comments]
|
||||
|
||||
assert TestRepo.one(query) == %{id: p.id, comments: []}
|
||||
end
|
||||
|
||||
test "take with join nil maps (one association)" do
|
||||
p = TestRepo.insert!(%Post{})
|
||||
|
||||
query =
|
||||
from p in Post,
|
||||
left_join: u in User,
|
||||
on: p.author_id == u.id,
|
||||
select: map(p, [:id, author: [:id, :name]]),
|
||||
preload: [author: u]
|
||||
|
||||
assert TestRepo.one(query) == %{id: p.id, author: nil}
|
||||
|
||||
query =
|
||||
from p in Post,
|
||||
left_join: u in User,
|
||||
on: p.author_id == u.id,
|
||||
select: map(p, [:id, author: [:id, :name]]),
|
||||
preload: [:author]
|
||||
|
||||
assert TestRepo.one(query) == %{id: p.id, author: nil}
|
||||
end
|
||||
|
||||
test "preload through with take" do
|
||||
%Post{id: pid1} = TestRepo.insert!(%Post{})
|
||||
|
||||
%User{id: uid1} = TestRepo.insert!(%User{name: "foo"})
|
||||
%User{id: uid2} = TestRepo.insert!(%User{name: "bar"})
|
||||
|
||||
%Comment{} = TestRepo.insert!(%Comment{post_id: pid1, author_id: uid1})
|
||||
%Comment{} = TestRepo.insert!(%Comment{post_id: pid1, author_id: uid1})
|
||||
%Comment{} = TestRepo.insert!(%Comment{post_id: pid1, author_id: uid2})
|
||||
|
||||
[p1] = TestRepo.all from Post, preload: [:comments_authors], select: [:id, comments_authors: :id]
|
||||
[%{id: ^uid1, name: nil}, %{id: ^uid2, name: nil}] = p1.comments_authors |> sort_by_id
|
||||
end
|
||||
|
||||
## Nested
|
||||
|
||||
test "preload many assocs" do
|
||||
p1 = TestRepo.insert!(%Post{title: "1"})
|
||||
p2 = TestRepo.insert!(%Post{title: "2"})
|
||||
|
||||
assert [p2, p1] = TestRepo.preload([p2, p1], [:comments, :users])
|
||||
assert p1.comments == []
|
||||
assert p2.comments == []
|
||||
assert p1.users == []
|
||||
assert p2.users == []
|
||||
end
|
||||
|
||||
test "preload nested" do
|
||||
p1 = TestRepo.insert!(%Post{title: "1"})
|
||||
p2 = TestRepo.insert!(%Post{title: "2"})
|
||||
|
||||
TestRepo.insert!(%Comment{text: "1", post_id: p1.id})
|
||||
TestRepo.insert!(%Comment{text: "2", post_id: p1.id})
|
||||
TestRepo.insert!(%Comment{text: "3", post_id: p2.id})
|
||||
TestRepo.insert!(%Comment{text: "4", post_id: p2.id})
|
||||
|
||||
assert [p2, p1] = TestRepo.preload([p2, p1], [comments: :post])
|
||||
assert [c1, c2] = p1.comments
|
||||
assert [c3, c4] = p2.comments
|
||||
assert p1.id == c1.post.id
|
||||
assert p1.id == c2.post.id
|
||||
assert p2.id == c3.post.id
|
||||
assert p2.id == c4.post.id
|
||||
end
|
||||
|
||||
test "preload nested via custom query" do
|
||||
p1 = TestRepo.insert!(%Post{title: "1"})
|
||||
p2 = TestRepo.insert!(%Post{title: "2"})
|
||||
|
||||
TestRepo.insert!(%Comment{text: "1", post_id: p1.id})
|
||||
TestRepo.insert!(%Comment{text: "2", post_id: p1.id})
|
||||
TestRepo.insert!(%Comment{text: "3", post_id: p2.id})
|
||||
TestRepo.insert!(%Comment{text: "4", post_id: p2.id})
|
||||
|
||||
query = from(c in Comment, preload: :post, order_by: [desc: c.text])
|
||||
assert [p2, p1] = TestRepo.preload([p2, p1], comments: query)
|
||||
assert [c2, c1] = p1.comments
|
||||
assert [c4, c3] = p2.comments
|
||||
assert p1.id == c1.post.id
|
||||
assert p1.id == c2.post.id
|
||||
assert p2.id == c3.post.id
|
||||
assert p2.id == c4.post.id
|
||||
end
|
||||
|
||||
test "custom preload_order" do
|
||||
post = TestRepo.insert!(%Post{users: [%User{name: "bar"}, %User{name: "foo"}], title: "1"})
|
||||
|
||||
TestRepo.insert!(%Comment{text: "2", post_id: post.id})
|
||||
TestRepo.insert!(%Comment{text: "1", post_id: post.id})
|
||||
|
||||
post = TestRepo.preload(post, [:ordered_comments, :ordered_users])
|
||||
|
||||
# asc
|
||||
assert [%{text: "1"}, %{text: "2"}] = post.ordered_comments
|
||||
|
||||
# desc
|
||||
assert [%{name: "foo"}, %{name: "bar"}] = post.ordered_users
|
||||
end
|
||||
|
||||
test "custom preload_order with mfa" do
|
||||
post1 = TestRepo.insert!(%Post{users: [%User{name: "bar"}, %User{name: "foo"}], title: "1"})
|
||||
post2 = TestRepo.insert!(%Post{users: [%User{name: "baz"}, %User{name: "foz"}], title: "2"})
|
||||
|
||||
[post1, post2] = TestRepo.preload([post1, post2], [:ordered_users_by_join_table], log: :error)
|
||||
|
||||
assert [%{name: "foo"}, %{name: "bar"}] = post1.ordered_users_by_join_table
|
||||
assert [%{name: "foz"}, %{name: "baz"}] = post2.ordered_users_by_join_table
|
||||
end
|
||||
|
||||
## Others
|
||||
|
||||
@tag :invalid_prefix
|
||||
test "preload custom prefix from schema" do
|
||||
p = TestRepo.insert!(%Post{title: "1"})
|
||||
p = Ecto.put_meta(p, prefix: "this_surely_does_not_exist")
|
||||
# This preload should fail because it points to a prefix that does not exist
|
||||
assert catch_error(TestRepo.preload(p, [:comments]))
|
||||
end
|
||||
|
||||
@tag :invalid_prefix
|
||||
test "preload custom prefix from options" do
|
||||
p = TestRepo.insert!(%Post{title: "1"})
|
||||
# This preload should fail because it points to a prefix that does not exist
|
||||
assert catch_error(TestRepo.preload(p, [:comments], prefix: "this_surely_does_not_exist"))
|
||||
end
|
||||
|
||||
test "preload with binary_id" do
|
||||
c = TestRepo.insert!(%Custom{})
|
||||
u = TestRepo.insert!(%User{custom_id: c.bid})
|
||||
|
||||
u = TestRepo.preload(u, :custom)
|
||||
assert u.custom.bid == c.bid
|
||||
end
|
||||
|
||||
test "preload raises with association set but without id" do
|
||||
c1 = TestRepo.insert!(%Comment{text: "1"})
|
||||
u1 = TestRepo.insert!(%User{name: "name"})
|
||||
updated = %{c1 | author: u1, author_id: nil}
|
||||
|
||||
assert ExUnit.CaptureLog.capture_log(fn ->
|
||||
assert TestRepo.preload(updated, [:author]).author == u1
|
||||
end) =~ ~r/its association key `author_id` is nil/
|
||||
|
||||
assert TestRepo.preload(updated, [:author], force: true).author == nil
|
||||
end
|
||||
|
||||
test "preload skips already loaded for cardinality one" do
|
||||
%Post{id: pid} = TestRepo.insert!(%Post{title: "1"})
|
||||
|
||||
c1 = %Comment{id: cid1} = TestRepo.insert!(%Comment{text: "1", post_id: pid})
|
||||
c2 = %Comment{id: _cid} = TestRepo.insert!(%Comment{text: "2", post_id: nil})
|
||||
|
||||
[c1, c2] = TestRepo.preload([c1, c2], :post)
|
||||
assert %Post{id: ^pid} = c1.post
|
||||
assert c2.post == nil
|
||||
|
||||
[c1, c2] = TestRepo.preload([c1, c2], post: :comments)
|
||||
assert [%Comment{id: ^cid1}] = c1.post.comments
|
||||
|
||||
TestRepo.update_all Post, set: [title: "0"]
|
||||
TestRepo.update_all Comment, set: [post_id: pid]
|
||||
|
||||
# Preloading once again shouldn't change the result
|
||||
[c1, c2] = TestRepo.preload([c1, c2], :post)
|
||||
assert %Post{id: ^pid, title: "1", comments: [_|_]} = c1.post
|
||||
assert c2.post == nil
|
||||
|
||||
[c1, c2] = TestRepo.preload([c1, %{c2 | post_id: pid}], :post, force: true)
|
||||
assert %Post{id: ^pid, title: "0", comments: %Ecto.Association.NotLoaded{}} = c1.post
|
||||
assert %Post{id: ^pid, title: "0", comments: %Ecto.Association.NotLoaded{}} = c2.post
|
||||
end
|
||||
|
||||
test "preload skips already loaded for cardinality many" do
|
||||
p1 = TestRepo.insert!(%Post{title: "1"})
|
||||
p2 = TestRepo.insert!(%Post{title: "2"})
|
||||
|
||||
%Comment{id: cid1} = TestRepo.insert!(%Comment{text: "1", post_id: p1.id})
|
||||
%Comment{id: cid2} = TestRepo.insert!(%Comment{text: "2", post_id: p2.id})
|
||||
|
||||
[p1, p2] = TestRepo.preload([p1, p2], :comments)
|
||||
assert [%Comment{id: ^cid1}] = p1.comments
|
||||
assert [%Comment{id: ^cid2}] = p2.comments
|
||||
|
||||
[p1, p2] = TestRepo.preload([p1, p2], comments: :post)
|
||||
assert hd(p1.comments).post.id == p1.id
|
||||
assert hd(p2.comments).post.id == p2.id
|
||||
|
||||
TestRepo.update_all Comment, set: [text: "0"]
|
||||
|
||||
# Preloading once again shouldn't change the result
|
||||
[p1, p2] = TestRepo.preload([p1, p2], :comments)
|
||||
assert [%Comment{id: ^cid1, text: "1", post: %Post{}}] = p1.comments
|
||||
assert [%Comment{id: ^cid2, text: "2", post: %Post{}}] = p2.comments
|
||||
|
||||
[p1, p2] = TestRepo.preload([p1, p2], :comments, force: true)
|
||||
assert [%Comment{id: ^cid1, text: "0", post: %Ecto.Association.NotLoaded{}}] = p1.comments
|
||||
assert [%Comment{id: ^cid2, text: "0", post: %Ecto.Association.NotLoaded{}}] = p2.comments
|
||||
end
|
||||
|
||||
test "preload keyword query" do
|
||||
p1 = TestRepo.insert!(%Post{title: "1"})
|
||||
p2 = TestRepo.insert!(%Post{title: "2"})
|
||||
TestRepo.insert!(%Post{title: "3"})
|
||||
|
||||
%Comment{id: cid1} = TestRepo.insert!(%Comment{text: "1", post_id: p1.id})
|
||||
%Comment{id: cid2} = TestRepo.insert!(%Comment{text: "2", post_id: p1.id})
|
||||
%Comment{id: cid3} = TestRepo.insert!(%Comment{text: "3", post_id: p2.id})
|
||||
%Comment{id: cid4} = TestRepo.insert!(%Comment{text: "4", post_id: p2.id})
|
||||
|
||||
# Regular query
|
||||
query = from(p in Post, preload: [:comments], select: p)
|
||||
|
||||
assert [p1, p2, p3] = TestRepo.all(query) |> sort_by_id
|
||||
assert [%Comment{id: ^cid1}, %Comment{id: ^cid2}] = p1.comments |> sort_by_id
|
||||
assert [%Comment{id: ^cid3}, %Comment{id: ^cid4}] = p2.comments |> sort_by_id
|
||||
assert [] = p3.comments
|
||||
|
||||
# Query with interpolated preload query
|
||||
query = from(p in Post, preload: [comments: ^from(c in Comment, where: false)], select: p)
|
||||
|
||||
assert [p1, p2, p3] = TestRepo.all(query)
|
||||
assert [] = p1.comments
|
||||
assert [] = p2.comments
|
||||
assert [] = p3.comments
|
||||
|
||||
# Now let's use an interpolated preload too
|
||||
comments = [:comments]
|
||||
query = from(p in Post, preload: ^comments, select: {0, [p], 1, 2})
|
||||
|
||||
posts = TestRepo.all(query)
|
||||
[p1, p2, p3] = Enum.map(posts, fn {0, [p], 1, 2} -> p end) |> sort_by_id
|
||||
|
||||
assert [%Comment{id: ^cid1}, %Comment{id: ^cid2}] = p1.comments |> sort_by_id
|
||||
assert [%Comment{id: ^cid3}, %Comment{id: ^cid4}] = p2.comments |> sort_by_id
|
||||
assert [] = p3.comments
|
||||
end
|
||||
|
||||
|
||||
test "preload belongs_to in embedded_schema" do
|
||||
%User{id: uid1} = TestRepo.insert!(%User{name: "1"})
|
||||
item = %Item{user_id: uid1}
|
||||
|
||||
# Starts as not loaded
|
||||
assert %Ecto.Association.NotLoaded{} = item.user
|
||||
|
||||
# Now we preload it
|
||||
item = TestRepo.preload(item, :user)
|
||||
assert %User{id: ^uid1} = item.user
|
||||
end
|
||||
|
||||
describe "preload associations from nested embeds" do
|
||||
setup do
|
||||
%User{id: uid1} = TestRepo.insert!(%User{name: "1"})
|
||||
%User{id: uid2} = TestRepo.insert!(%User{name: "2"})
|
||||
%User{id: uid3} = TestRepo.insert!(%User{name: "3"})
|
||||
item1 = %Item{id: 1, user_id: uid1}
|
||||
item2 = %Item{id: 2, user_id: uid2}
|
||||
item3 = %Item{id: 3, user_id: uid3}
|
||||
order1 = %Order{items: [item1, item3, item2], item: item1}
|
||||
order2 = %Order{items: [], item: nil}
|
||||
order3 = %Order{items: nil, item: nil}
|
||||
order4 = %Order{items: [item1, item2], item: item2}
|
||||
|
||||
[orders: [order1, order2, order3, order4]]
|
||||
end
|
||||
|
||||
test "cannot preload embed without its associations", context do
|
||||
assert_raise ArgumentError, ~r/cannot preload embedded field/, fn ->
|
||||
TestRepo.preload(context.orders, :item)
|
||||
end
|
||||
end
|
||||
|
||||
test "embeds_one", context do
|
||||
[nil | preloaded_orders] = [nil | context.orders] |> TestRepo.preload(item: :user)
|
||||
|
||||
expected_item_user =
|
||||
Enum.map(context.orders, fn
|
||||
%{item: nil} -> {nil, nil}
|
||||
%{item: item} -> {item.id, item.user_id}
|
||||
end)
|
||||
|
||||
actual_item_user =
|
||||
Enum.map(preloaded_orders, fn
|
||||
%{item: nil} -> {nil, nil}
|
||||
%{item: item} -> {item.id, item.user.id}
|
||||
end)
|
||||
|
||||
assert expected_item_user == actual_item_user
|
||||
end
|
||||
|
||||
test "embeds_many", context do
|
||||
[nil | preloaded_orders] = [nil | context.orders] |> TestRepo.preload(items: :user)
|
||||
|
||||
expected_items_user =
|
||||
Enum.map(context.orders, fn
|
||||
%{items: nil} -> {nil, nil}
|
||||
%{items: items} -> Enum.map(items, & {&1.id, &1.user_id})
|
||||
end)
|
||||
|
||||
actual_items_user =
|
||||
Enum.map(preloaded_orders, fn
|
||||
%{items: nil} -> {nil, nil}
|
||||
%{items: items} -> Enum.map(items, & {&1.id, &1.user.id})
|
||||
end)
|
||||
|
||||
assert expected_items_user == actual_items_user
|
||||
end
|
||||
end
|
||||
|
||||
defp sort_by_id(values) do
|
||||
Enum.sort_by(values, &(&1.id))
|
||||
end
|
||||
end
|
||||
2397
phoenix/deps/ecto/integration_test/cases/repo.exs
Normal file
2397
phoenix/deps/ecto/integration_test/cases/repo.exs
Normal file
File diff suppressed because it is too large
Load Diff
636
phoenix/deps/ecto/integration_test/cases/type.exs
Normal file
636
phoenix/deps/ecto/integration_test/cases/type.exs
Normal file
@@ -0,0 +1,636 @@
|
||||
defmodule Ecto.Integration.TypeTest do
|
||||
use Ecto.Integration.Case, async: Application.compile_env(:ecto, :async_integration_tests, true)
|
||||
|
||||
alias Ecto.Integration.{Bitstring, Comment, Custom, Item, ItemColor, Order, Post, User, Tag, Usec}
|
||||
alias Ecto.Integration.TestRepo
|
||||
import Ecto.Query
|
||||
|
||||
@parameterized_type Ecto.ParameterizedType.init(Ecto.Enum, values: [:a, :b])
|
||||
|
||||
test "primitive types" do
|
||||
integer = 1
|
||||
float = 0.1
|
||||
blob = <<0, 1>>
|
||||
uuid = "00010203-0405-4607-8809-0a0b0c0d0e0f"
|
||||
datetime = ~N[2014-01-16 20:26:51]
|
||||
|
||||
TestRepo.insert!(%Post{blob: blob, public: true, visits: integer, uuid: uuid,
|
||||
counter: integer, inserted_at: datetime, intensity: float})
|
||||
|
||||
# nil
|
||||
assert [nil] = TestRepo.all(from Post, select: nil)
|
||||
|
||||
# ID
|
||||
assert [1] = TestRepo.all(from p in Post, where: p.counter == ^integer, select: p.counter)
|
||||
|
||||
# Integers
|
||||
assert [1] = TestRepo.all(from p in Post, where: p.visits == ^integer, select: p.visits)
|
||||
assert [1] = TestRepo.all(from p in Post, where: p.visits == 1, select: p.visits)
|
||||
assert [3] = TestRepo.all(from p in Post, select: p.visits + 2)
|
||||
|
||||
# Floats
|
||||
assert [0.1] = TestRepo.all(from p in Post, where: p.intensity == ^float, select: p.intensity)
|
||||
assert [0.1] = TestRepo.all(from p in Post, where: p.intensity == 0.1, select: p.intensity)
|
||||
assert [1500.0] = TestRepo.all(from p in Post, select: 1500.0)
|
||||
assert [0.5] = TestRepo.all(from p in Post, select: p.intensity * 5)
|
||||
|
||||
# Booleans
|
||||
assert [true] = TestRepo.all(from p in Post, where: p.public == ^true, select: p.public)
|
||||
assert [true] = TestRepo.all(from p in Post, where: p.public == true, select: p.public)
|
||||
|
||||
# Binaries
|
||||
assert [^blob] = TestRepo.all(from p in Post, where: p.blob == <<0, 1>>, select: p.blob)
|
||||
assert [^blob] = TestRepo.all(from p in Post, where: p.blob == ^blob, select: p.blob)
|
||||
|
||||
# UUID
|
||||
assert [^uuid] = TestRepo.all(from p in Post, where: p.uuid == ^uuid, select: p.uuid)
|
||||
|
||||
# NaiveDatetime
|
||||
assert [^datetime] = TestRepo.all(from p in Post, where: p.inserted_at == ^datetime, select: p.inserted_at)
|
||||
|
||||
# Datetime
|
||||
datetime = DateTime.from_unix!(System.os_time(:second), :second)
|
||||
TestRepo.insert!(%User{inserted_at: datetime})
|
||||
assert [^datetime] = TestRepo.all(from u in User, where: u.inserted_at == ^datetime, select: u.inserted_at)
|
||||
|
||||
# usec
|
||||
naive_datetime = ~N[2014-01-16 20:26:51.000000]
|
||||
datetime = DateTime.from_naive!(~N[2014-01-16 20:26:51.000000], "Etc/UTC")
|
||||
TestRepo.insert!(%Usec{naive_datetime_usec: naive_datetime, utc_datetime_usec: datetime})
|
||||
assert [^naive_datetime] = TestRepo.all(from u in Usec, where: u.naive_datetime_usec == ^naive_datetime, select: u.naive_datetime_usec)
|
||||
assert [^datetime] = TestRepo.all(from u in Usec, where: u.utc_datetime_usec == ^datetime, select: u.utc_datetime_usec)
|
||||
|
||||
naive_datetime = ~N[2014-01-16 20:26:51.123000]
|
||||
datetime = DateTime.from_naive!(~N[2014-01-16 20:26:51.123000], "Etc/UTC")
|
||||
TestRepo.insert!(%Usec{naive_datetime_usec: naive_datetime, utc_datetime_usec: datetime})
|
||||
assert [^naive_datetime] = TestRepo.all(from u in Usec, where: u.naive_datetime_usec == ^naive_datetime, select: u.naive_datetime_usec)
|
||||
assert [^datetime] = TestRepo.all(from u in Usec, where: u.utc_datetime_usec == ^datetime, select: u.utc_datetime_usec)
|
||||
end
|
||||
|
||||
@tag :bitstring_type
|
||||
test "bitstring type" do
|
||||
bitstring = <<2::3>>
|
||||
|
||||
TestRepo.insert!(%Bitstring{bs: bitstring, bs_with_size: <<5::10>>})
|
||||
|
||||
# Bitstrings
|
||||
assert [^bitstring] = TestRepo.all(from p in Bitstring, where: p.bs == ^bitstring, select: p.bs)
|
||||
assert [^bitstring] = TestRepo.all(from p in Bitstring, where: p.bs == <<2::3>>, select: p.bs)
|
||||
|
||||
assert [<<42::6>>] = TestRepo.all(from p in Bitstring, limit: 1, select: p.bs_with_default)
|
||||
end
|
||||
|
||||
if Code.ensure_loaded?(Duration) do
|
||||
@tag :duration_type
|
||||
test "duration type" do
|
||||
duration = %Duration{year: 1, month: 1, second: 1, microsecond: {100, 6}}
|
||||
|
||||
struct = %Ecto.Integration.Duration{
|
||||
dur: duration,
|
||||
dur_with_fields: duration,
|
||||
dur_with_precision: duration,
|
||||
dur_with_fields_and_precision: duration
|
||||
}
|
||||
|
||||
TestRepo.insert!(struct)
|
||||
|
||||
persisted_duration =
|
||||
from(d in Ecto.Integration.Duration, where: d.dur == ^duration)
|
||||
|> TestRepo.one()
|
||||
|
||||
assert persisted_duration.dur == duration
|
||||
|
||||
# `:field` option set to MONTH so it ignores all units lower than `:month`
|
||||
assert persisted_duration.dur_with_fields == %Duration{
|
||||
year: 1,
|
||||
month: 1,
|
||||
microsecond: {0, 6}
|
||||
}
|
||||
|
||||
assert persisted_duration.dur_with_precision == %Duration{
|
||||
year: 1,
|
||||
month: 1,
|
||||
second: 1,
|
||||
microsecond: {100, 4}
|
||||
}
|
||||
|
||||
# `:field` option is set to HOUR TO SECOND so it ignores all units lower than `:second`
|
||||
assert persisted_duration.dur_with_fields_and_precision == %Duration{
|
||||
year: 1,
|
||||
month: 1,
|
||||
second: 1,
|
||||
microsecond: {0, 1}
|
||||
}
|
||||
|
||||
# `:default set in migration`
|
||||
assert persisted_duration.dur_with_default == %Duration{month: 10, microsecond: {0, 6}}
|
||||
end
|
||||
end
|
||||
|
||||
@tag :select_not
|
||||
test "primitive types boolean negate" do
|
||||
TestRepo.insert!(%Post{public: true})
|
||||
assert [false] = TestRepo.all(from p in Post, where: p.public == true, select: not p.public)
|
||||
assert [true] = TestRepo.all(from p in Post, where: p.public == true, select: not not p.public)
|
||||
end
|
||||
|
||||
test "aggregate types" do
|
||||
datetime = ~N[2014-01-16 20:26:51]
|
||||
TestRepo.insert!(%Post{inserted_at: datetime})
|
||||
query = from p in Post, select: max(p.inserted_at)
|
||||
assert [^datetime] = TestRepo.all(query)
|
||||
end
|
||||
|
||||
# We don't specifically assert on the tuple content because
|
||||
# some databases would return integer, others decimal.
|
||||
# The important is that the type has been invoked for wrapping.
|
||||
test "aggregate custom types" do
|
||||
TestRepo.insert!(%Post{wrapped_visits: {:int, 10}})
|
||||
query = from p in Post, select: sum(p.wrapped_visits)
|
||||
assert [{:int, _}] = TestRepo.all(query)
|
||||
end
|
||||
|
||||
@tag :aggregate_filters
|
||||
test "aggregate filter types" do
|
||||
datetime = ~N[2014-01-16 20:26:51]
|
||||
TestRepo.insert!(%Post{inserted_at: datetime})
|
||||
query = from p in Post, select: filter(max(p.inserted_at), p.public == ^true)
|
||||
assert [^datetime] = TestRepo.all(query)
|
||||
end
|
||||
|
||||
test "coalesce text type when default" do
|
||||
TestRepo.insert!(%Post{blob: nil})
|
||||
blob = <<0, 1>>
|
||||
query = from p in Post, select: coalesce(p.blob, ^blob)
|
||||
assert [^blob] = TestRepo.all(query)
|
||||
end
|
||||
|
||||
test "coalesce text type when value" do
|
||||
blob = <<0, 2>>
|
||||
default_blob = <<0, 1>>
|
||||
TestRepo.insert!(%Post{blob: blob})
|
||||
query = from p in Post, select: coalesce(p.blob, ^default_blob)
|
||||
assert [^blob] = TestRepo.all(query)
|
||||
end
|
||||
|
||||
test "tagged types" do
|
||||
%{id: post_id} = TestRepo.insert!(%Post{visits: 12})
|
||||
TestRepo.insert!(%Comment{text: "#{post_id}", post_id: post_id})
|
||||
|
||||
# Numbers
|
||||
assert [1] = TestRepo.all(from Post, select: type(^"1", :integer))
|
||||
assert [1.0] = TestRepo.all(from Post, select: type(^1.0, :float))
|
||||
assert [1] = TestRepo.all(from p in Post, select: type(^"1", p.visits))
|
||||
assert [1.0] = TestRepo.all(from p in Post, select: type(^"1", p.intensity))
|
||||
|
||||
# Custom wrappers
|
||||
assert [1] = TestRepo.all(from Post, select: type(^"1", CustomPermalink))
|
||||
|
||||
# Custom types
|
||||
uuid = Ecto.UUID.generate()
|
||||
assert [^uuid] = TestRepo.all(from Post, select: type(^uuid, Ecto.UUID))
|
||||
|
||||
# Parameterized types
|
||||
assert [:a] = TestRepo.all(from Post, select: type(^"a", ^@parameterized_type))
|
||||
|
||||
# Math operations
|
||||
assert [4] = TestRepo.all(from Post, select: type(2 + ^"2", :integer))
|
||||
assert [4.0] = TestRepo.all(from Post, select: type(2.0 + ^"2", :float))
|
||||
assert [4] = TestRepo.all(from p in Post, select: type(2 + ^"2", p.visits))
|
||||
assert [4.0] = TestRepo.all(from p in Post, select: type(2.0 + ^"2", p.intensity))
|
||||
|
||||
# Comparison expression
|
||||
assert [12] = TestRepo.all(from p in Post, select: type(coalesce(p.visits, 0), :integer))
|
||||
assert [1.0] = TestRepo.all(from p in Post, select: type(coalesce(p.intensity, 1.0), :float))
|
||||
|
||||
# parent_as/1
|
||||
child = from c in Comment, where: type(parent_as(:posts).id, :string) == c.text, select: c.post_id
|
||||
query = from p in Post, as: :posts, where: p.id in subquery(child), select: p.id
|
||||
assert [post_id] == TestRepo.all(query)
|
||||
end
|
||||
|
||||
test "binary id type" do
|
||||
assert %Custom{} = custom = TestRepo.insert!(%Custom{})
|
||||
bid = custom.bid
|
||||
assert [^bid] = TestRepo.all(from c in Custom, select: c.bid)
|
||||
assert [^bid] = TestRepo.all(from c in Custom, select: type(^bid, :binary_id))
|
||||
end
|
||||
|
||||
@tag :like_match_blob
|
||||
test "text type as blob" do
|
||||
assert %Post{} = post = TestRepo.insert!(%Post{blob: <<0, 1, 2>>})
|
||||
id = post.id
|
||||
assert post.blob == <<0, 1, 2>>
|
||||
assert [^id] = TestRepo.all(from p in Post, where: like(p.blob, ^<<0, 1, 2>>), select: p.id)
|
||||
end
|
||||
|
||||
@tag :like_match_blob
|
||||
@tag :text_type_as_string
|
||||
test "text type as string" do
|
||||
assert %Post{} = post = TestRepo.insert!(%Post{blob: "hello"})
|
||||
id = post.id
|
||||
assert post.blob == "hello"
|
||||
assert [^id] = TestRepo.all(from p in Post, where: like(p.blob, ^"hello"), select: p.id)
|
||||
end
|
||||
|
||||
@tag :array_type
|
||||
test "array type" do
|
||||
ints = [1, 2, 3]
|
||||
tag = TestRepo.insert!(%Tag{ints: ints})
|
||||
|
||||
assert TestRepo.all(from t in Tag, where: t.ints == ^[], select: t.ints) == []
|
||||
assert TestRepo.all(from t in Tag, where: t.ints == ^[1, 2, 3], select: t.ints) == [ints]
|
||||
|
||||
# Both sides interpolation
|
||||
assert TestRepo.all(from t in Tag, where: ^"b" in ^["a", "b", "c"], select: t.ints) == [ints]
|
||||
assert TestRepo.all(from t in Tag, where: ^"b" in [^"a", ^"b", ^"c"], select: t.ints) == [ints]
|
||||
|
||||
# Querying
|
||||
assert TestRepo.all(from t in Tag, where: t.ints == [1, 2, 3], select: t.ints) == [ints]
|
||||
assert TestRepo.all(from t in Tag, where: 0 in t.ints, select: t.ints) == []
|
||||
assert TestRepo.all(from t in Tag, where: 1 in t.ints, select: t.ints) == [ints]
|
||||
|
||||
# Update
|
||||
tag = TestRepo.update!(Ecto.Changeset.change tag, ints: nil)
|
||||
assert TestRepo.get!(Tag, tag.id).ints == nil
|
||||
|
||||
tag = TestRepo.update!(Ecto.Changeset.change tag, ints: [3, 2, 1])
|
||||
assert TestRepo.get!(Tag, tag.id).ints == [3, 2, 1]
|
||||
|
||||
# Update all
|
||||
{1, _} = TestRepo.update_all(Tag, push: [ints: 0])
|
||||
assert TestRepo.get!(Tag, tag.id).ints == [3, 2, 1, 0]
|
||||
|
||||
{1, _} = TestRepo.update_all(Tag, pull: [ints: 2])
|
||||
assert TestRepo.get!(Tag, tag.id).ints == [3, 1, 0]
|
||||
|
||||
{1, _} = TestRepo.update_all(Tag, set: [ints: nil])
|
||||
assert TestRepo.get!(Tag, tag.id).ints == nil
|
||||
end
|
||||
|
||||
@tag :array_type
|
||||
test "array type with custom types" do
|
||||
uuids = ["51fcfbdd-ad60-4ccb-8bf9-47aabd66d075"]
|
||||
TestRepo.insert!(%Tag{uuids: ["51fcfbdd-ad60-4ccb-8bf9-47aabd66d075"]})
|
||||
|
||||
assert TestRepo.all(from t in Tag, where: t.uuids == ^[], select: t.uuids) == []
|
||||
assert TestRepo.all(from t in Tag, where: t.uuids == ^["51fcfbdd-ad60-4ccb-8bf9-47aabd66d075"],
|
||||
select: t.uuids) == [uuids]
|
||||
|
||||
{1, _} = TestRepo.update_all(Tag, set: [uuids: nil])
|
||||
assert TestRepo.all(from t in Tag, select: t.uuids) == [nil]
|
||||
end
|
||||
|
||||
@tag :array_type
|
||||
test "array type with nil in array" do
|
||||
tag = TestRepo.insert!(%Tag{ints: [1, nil, 3]})
|
||||
assert tag.ints == [1, nil, 3]
|
||||
end
|
||||
|
||||
@tag :map_type
|
||||
test "untyped map" do
|
||||
post1 = TestRepo.insert!(%Post{meta: %{"foo" => "bar", "baz" => "bat"}})
|
||||
post2 = TestRepo.insert!(%Post{meta: %{foo: "bar", baz: "bat"}})
|
||||
|
||||
assert TestRepo.all(from p in Post, where: p.id == ^post1.id, select: p.meta) ==
|
||||
[%{"foo" => "bar", "baz" => "bat"}]
|
||||
assert TestRepo.all(from p in Post, where: p.id == ^post2.id, select: p.meta) ==
|
||||
[%{"foo" => "bar", "baz" => "bat"}]
|
||||
end
|
||||
|
||||
@tag :map_type
|
||||
test "typed string map" do
|
||||
post1 = TestRepo.insert!(%Post{links: %{"foo" => "http://foo.com", "bar" => "http://bar.com"}})
|
||||
post2 = TestRepo.insert!(%Post{links: %{foo: "http://foo.com", bar: "http://bar.com"}})
|
||||
|
||||
assert TestRepo.all(from p in Post, where: p.id == ^post1.id, select: p.links) ==
|
||||
[%{"foo" => "http://foo.com", "bar" => "http://bar.com"}]
|
||||
assert TestRepo.all(from p in Post, where: p.id == ^post2.id, select: p.links) ==
|
||||
[%{"foo" => "http://foo.com", "bar" => "http://bar.com"}]
|
||||
end
|
||||
|
||||
@tag :map_type
|
||||
test "typed float map" do
|
||||
post = TestRepo.insert!(%Post{intensities: %{"foo" => 1.0, "bar" => 416500.0}})
|
||||
|
||||
# Note we are using === since we want to check integer vs float
|
||||
assert TestRepo.all(from p in Post, where: p.id == ^post.id, select: p.intensities) ===
|
||||
[%{"foo" => 1.0, "bar" => 416500.0}]
|
||||
end
|
||||
|
||||
@tag :map_type
|
||||
test "map type on update" do
|
||||
post = TestRepo.insert!(%Post{meta: %{"world" => "hello"}})
|
||||
assert TestRepo.get!(Post, post.id).meta == %{"world" => "hello"}
|
||||
|
||||
post = TestRepo.update!(Ecto.Changeset.change post, meta: %{hello: "world"})
|
||||
assert TestRepo.get!(Post, post.id).meta == %{"hello" => "world"}
|
||||
|
||||
query = from(p in Post, where: p.id == ^post.id)
|
||||
TestRepo.update_all(query, set: [meta: %{world: "hello"}])
|
||||
assert TestRepo.get!(Post, post.id).meta == %{"world" => "hello"}
|
||||
end
|
||||
|
||||
@tag :map_type
|
||||
test "embeds one" do
|
||||
item = %Item{price: 123, valid_at: ~D[2014-01-16]}
|
||||
|
||||
order =
|
||||
%Order{}
|
||||
|> Ecto.Changeset.change
|
||||
|> Ecto.Changeset.put_embed(:item, item)
|
||||
|> TestRepo.insert!()
|
||||
|
||||
dbitem = TestRepo.get!(Order, order.id).item
|
||||
assert item.reference == dbitem.reference
|
||||
assert item.price == dbitem.price
|
||||
assert item.valid_at == dbitem.valid_at
|
||||
assert dbitem.id
|
||||
|
||||
[dbitem] = TestRepo.all(from o in Order, select: o.item)
|
||||
assert item.reference == dbitem.reference
|
||||
assert item.price == dbitem.price
|
||||
assert item.valid_at == dbitem.valid_at
|
||||
assert dbitem.id
|
||||
|
||||
{1, _} = TestRepo.update_all(Order, set: [item: %{dbitem | price: 456}])
|
||||
assert TestRepo.get!(Order, order.id).item.price == 456
|
||||
end
|
||||
|
||||
@tag :map_type
|
||||
@tag :json_extract_path
|
||||
test "json_extract_path with primitive values" do
|
||||
order = %Order{metadata:
|
||||
%{
|
||||
:id => 123,
|
||||
:time => ~T[09:00:00],
|
||||
"code" => "good",
|
||||
"'single quoted'" => "bar",
|
||||
"\"double quoted\"" => "baz",
|
||||
"enabled" => true,
|
||||
"extra" => [%{"enabled" => false}]
|
||||
}
|
||||
}
|
||||
|
||||
order = TestRepo.insert!(order)
|
||||
|
||||
assert TestRepo.one(from o in Order, select: json_extract_path(o.metadata, ^["id"])) == 123
|
||||
assert TestRepo.one(from o in Order, select: o.metadata["bad"]) == nil
|
||||
assert TestRepo.one(from o in Order, select: o.metadata["bad"]["bad"]) == nil
|
||||
|
||||
field = "id"
|
||||
assert TestRepo.one(from o in Order, select: o.metadata[^field]) == 123
|
||||
assert TestRepo.one(from o in Order, select: o.metadata["time"]) == "09:00:00"
|
||||
assert TestRepo.one(from o in Order, select: o.metadata["'single quoted'"]) == "bar"
|
||||
assert TestRepo.one(from o in Order, select: o.metadata["';"]) == nil
|
||||
assert TestRepo.one(from o in Order, select: o.metadata["\"double quoted\""]) == "baz"
|
||||
assert TestRepo.one(from o in Order, select: o.metadata["enabled"]) == true
|
||||
assert TestRepo.one(from o in Order, select: o.metadata["extra"][0]["enabled"]) == false
|
||||
|
||||
# where
|
||||
assert TestRepo.one(from o in Order, where: o.metadata["id"] == 123, select: o.id) == order.id
|
||||
assert TestRepo.one(from o in Order, where: o.metadata["id"] == 456, select: o.id) == nil
|
||||
assert TestRepo.one(from o in Order, where: o.metadata["code"] == "good", select: o.id) == order.id
|
||||
assert TestRepo.one(from o in Order, where: o.metadata["code"] == "bad", select: o.id) == nil
|
||||
assert TestRepo.one(from o in Order, where: o.metadata["enabled"] == true, select: o.id) == order.id
|
||||
assert TestRepo.one(from o in Order, where: o.metadata["extra"][0]["enabled"] == false, select: o.id) == order.id
|
||||
end
|
||||
|
||||
@tag :map_type
|
||||
@tag :json_extract_path
|
||||
test "json_extract_path with arrays and objects" do
|
||||
order = %Order{metadata: %{tags: [%{name: "red"}, %{name: "green"}]}}
|
||||
order = TestRepo.insert!(order)
|
||||
|
||||
assert TestRepo.one(from o in Order, select: o.metadata["tags"][0]["name"]) == "red"
|
||||
assert TestRepo.one(from o in Order, select: o.metadata["tags"][99]["name"]) == nil
|
||||
|
||||
index = 1
|
||||
assert TestRepo.one(from o in Order, select: o.metadata["tags"][^index]["name"]) == "green"
|
||||
|
||||
# where
|
||||
assert TestRepo.one(from o in Order, where: o.metadata["tags"][0]["name"] == "red", select: o.id) == order.id
|
||||
assert TestRepo.one(from o in Order, where: o.metadata["tags"][0]["name"] == "blue", select: o.id) == nil
|
||||
assert TestRepo.one(from o in Order, where: o.metadata["tags"][99]["name"] == "red", select: o.id) == nil
|
||||
end
|
||||
|
||||
@tag :map_type
|
||||
@tag :json_extract_path
|
||||
test "json_extract_path with embeds" do
|
||||
order = %Order{items: [%{valid_at: ~D[2020-01-01]}]}
|
||||
TestRepo.insert!(order)
|
||||
|
||||
assert TestRepo.one(from o in Order, select: o.items[0]["valid_at"]) == "2020-01-01"
|
||||
end
|
||||
|
||||
@tag :map_type
|
||||
@tag :json_extract_path
|
||||
test "json_extract_path with custom field source" do
|
||||
order = TestRepo.insert!(%Order{metadata: %{tags: [%{name: "red"}, %{name: "green"}]}})
|
||||
|
||||
assert TestRepo.one(from o in Order, where: o.metadata["tags"][0]["name"] == "red", select: o.id) == order.id
|
||||
end
|
||||
|
||||
@tag :map_type
|
||||
@tag :json_extract_path_with_field
|
||||
@tag :json_extract_path
|
||||
test "json_extract_path with fields in path" do
|
||||
order = %Order{id: 1, label: "tags", metadata: %{tags: [%{name: "red"}, %{name: "green"}]}}
|
||||
order = TestRepo.insert!(order)
|
||||
|
||||
assert TestRepo.one(from o in Order, select: o.metadata[o.label][1]["name"]) == "green"
|
||||
assert TestRepo.one(from o in Order, select: o.metadata["tags"][o.id]["name"]) == "green"
|
||||
|
||||
assert TestRepo.one(from o in Order, select: o.metadata["tags"][field(o, ^:id)]["name"]) ==
|
||||
"green"
|
||||
|
||||
squery = from o in Order, select: o.metadata["tags"][parent_as(:o).id]["name"]
|
||||
assert TestRepo.one(from o in Order, as: :o, where: subquery(squery) == ^"green")
|
||||
|
||||
squery = from o in Order, select: o.metadata["tags"][field(parent_as(:o), ^:id)]["name"]
|
||||
assert TestRepo.one(from o in Order, as: :o, where: subquery(squery) == ^"green")
|
||||
|
||||
assert TestRepo.one(
|
||||
from(o in Order,
|
||||
where: o.metadata["tags"][o.id]["name"] == "green",
|
||||
select: o.id)
|
||||
) == order.id
|
||||
|
||||
assert TestRepo.one(
|
||||
from(o in Order,
|
||||
where: o.metadata["tags"][field(o, ^:id)]["name"] == "green",
|
||||
select: o.id)
|
||||
) == order.id
|
||||
|
||||
squery = from o in Order, where: o.metadata["tags"][parent_as(:o).id]["name"] == "green"
|
||||
assert TestRepo.one(from o in Order, as: :o, where: exists(subquery(squery)))
|
||||
end
|
||||
|
||||
@tag :map_type
|
||||
@tag :map_type_schemaless
|
||||
test "embeds one with custom type" do
|
||||
item = %Item{price: 123, reference: "PREFIX-EXAMPLE"}
|
||||
|
||||
order =
|
||||
%Order{}
|
||||
|> Ecto.Changeset.change
|
||||
|> Ecto.Changeset.put_embed(:item, item)
|
||||
|> TestRepo.insert!()
|
||||
|
||||
dbitem = TestRepo.get!(Order, order.id).item
|
||||
assert dbitem.reference == "PREFIX-EXAMPLE"
|
||||
assert [%{"reference" => "EXAMPLE"}] = TestRepo.all(from o in "orders", select: o.item)
|
||||
end
|
||||
|
||||
@tag :map_type
|
||||
test "empty embeds one" do
|
||||
order = TestRepo.insert!(%Order{})
|
||||
assert order.item == nil
|
||||
assert TestRepo.get!(Order, order.id).item == nil
|
||||
end
|
||||
|
||||
@tag :map_type
|
||||
@tag :array_type
|
||||
test "embeds many" do
|
||||
item = %Item{price: 123, valid_at: ~D[2014-01-16]}
|
||||
tag =
|
||||
%Tag{}
|
||||
|> Ecto.Changeset.change
|
||||
|> Ecto.Changeset.put_embed(:items, [item])
|
||||
tag = TestRepo.insert!(tag)
|
||||
|
||||
[dbitem] = TestRepo.get!(Tag, tag.id).items
|
||||
assert item.price == dbitem.price
|
||||
assert item.valid_at == dbitem.valid_at
|
||||
assert dbitem.id
|
||||
|
||||
[[dbitem]] = TestRepo.all(from t in Tag, select: t.items)
|
||||
assert item.price == dbitem.price
|
||||
assert item.valid_at == dbitem.valid_at
|
||||
assert dbitem.id
|
||||
|
||||
{1, _} = TestRepo.update_all(Tag, set: [items: [%{dbitem | price: 456}]])
|
||||
assert (TestRepo.get!(Tag, tag.id).items |> hd).price == 456
|
||||
end
|
||||
|
||||
@tag :map_type
|
||||
@tag :array_type
|
||||
test "empty embeds many" do
|
||||
tag = TestRepo.insert!(%Tag{})
|
||||
assert tag.items == []
|
||||
assert TestRepo.get!(Tag, tag.id).items == []
|
||||
end
|
||||
|
||||
@tag :map_type
|
||||
@tag :array_type
|
||||
test "nested embeds" do
|
||||
red = %ItemColor{name: "red"}
|
||||
blue = %ItemColor{name: "blue"}
|
||||
item = %Item{
|
||||
primary_color: red,
|
||||
secondary_colors: [blue]
|
||||
}
|
||||
|
||||
order =
|
||||
%Order{}
|
||||
|> Ecto.Changeset.change
|
||||
|> Ecto.Changeset.put_embed(:item, item)
|
||||
order = TestRepo.insert!(order)
|
||||
|
||||
dbitem = TestRepo.get!(Order, order.id).item
|
||||
assert dbitem.primary_color.name == "red"
|
||||
assert Enum.map(dbitem.secondary_colors, & &1.name) == ["blue"]
|
||||
assert dbitem.id
|
||||
assert dbitem.primary_color.id
|
||||
|
||||
[dbitem] = TestRepo.all(from o in Order, select: o.item)
|
||||
assert dbitem.primary_color.name == "red"
|
||||
assert Enum.map(dbitem.secondary_colors, & &1.name) == ["blue"]
|
||||
assert dbitem.id
|
||||
assert dbitem.primary_color.id
|
||||
end
|
||||
|
||||
@tag :decimal_type
|
||||
test "decimal type" do
|
||||
decimal = Decimal.new("1.0")
|
||||
TestRepo.insert!(%Post{cost: decimal})
|
||||
|
||||
[cost] = TestRepo.all(from p in Post, where: p.cost == ^decimal, select: p.cost)
|
||||
assert Decimal.equal?(decimal, cost)
|
||||
[cost] = TestRepo.all(from p in Post, where: p.cost == ^1.0, select: p.cost)
|
||||
assert Decimal.equal?(decimal, cost)
|
||||
[cost] = TestRepo.all(from p in Post, where: p.cost == ^1, select: p.cost)
|
||||
assert Decimal.equal?(decimal, cost)
|
||||
[cost] = TestRepo.all(from p in Post, where: p.cost == 1.0, select: p.cost)
|
||||
assert Decimal.equal?(decimal, cost)
|
||||
[cost] = TestRepo.all(from p in Post, where: p.cost == 1, select: p.cost)
|
||||
assert Decimal.equal?(decimal, cost)
|
||||
[cost] = TestRepo.all(from p in Post, select: p.cost * 2)
|
||||
assert Decimal.equal?(Decimal.new("2.0"), cost)
|
||||
[cost] = TestRepo.all(from p in Post, select: p.cost - p.cost)
|
||||
assert Decimal.equal?(Decimal.new("0.0"), cost)
|
||||
end
|
||||
|
||||
@tag :decimal_type
|
||||
@tag :decimal_precision
|
||||
test "decimal typed aggregations" do
|
||||
decimal = Decimal.new("1.0")
|
||||
TestRepo.insert!(%Post{cost: decimal})
|
||||
|
||||
assert [1] = TestRepo.all(from p in Post, select: type(sum(p.cost), :integer))
|
||||
assert [1.0] = TestRepo.all(from p in Post, select: type(sum(p.cost), :float))
|
||||
[cost] = TestRepo.all(from p in Post, select: type(sum(p.cost), :decimal))
|
||||
assert Decimal.equal?(decimal, cost)
|
||||
end
|
||||
|
||||
@tag :decimal_type
|
||||
test "on coalesce with mixed types" do
|
||||
decimal = Decimal.new("1.0")
|
||||
TestRepo.insert!(%Post{cost: decimal})
|
||||
[cost] = TestRepo.all(from p in Post, select: coalesce(p.cost, 0))
|
||||
assert Decimal.equal?(decimal, cost)
|
||||
end
|
||||
|
||||
@tag :union_with_literals
|
||||
test "unions with literals" do
|
||||
TestRepo.insert!(%Post{})
|
||||
TestRepo.insert!(%Post{})
|
||||
|
||||
query1 = from(p in Post, select: %{n: 1})
|
||||
query2 = from(p in Post, select: %{n: 2})
|
||||
|
||||
assert TestRepo.all(union_all(query1, ^query2)) ==
|
||||
[%{n: 1}, %{n: 1}, %{n: 2}, %{n: 2}]
|
||||
|
||||
query1 = from(p in Post, select: %{n: 1.0})
|
||||
query2 = from(p in Post, select: %{n: 2.0})
|
||||
|
||||
assert TestRepo.all(union_all(query1, ^query2)) ==
|
||||
[%{n: 1.0}, %{n: 1.0}, %{n: 2.0}, %{n: 2.0}]
|
||||
|
||||
query1 = from(p in Post, select: %{n: "foo"})
|
||||
query2 = from(p in Post, select: %{n: "bar"})
|
||||
|
||||
assert TestRepo.all(union_all(query1, ^query2)) ==
|
||||
[%{n: "foo"}, %{n: "foo"}, %{n: "bar"}, %{n: "bar"}]
|
||||
end
|
||||
|
||||
test "schemaless types" do
|
||||
TestRepo.insert!(%Post{visits: 123})
|
||||
assert [123] = TestRepo.all(from p in "posts", select: type(p.visits, :integer))
|
||||
end
|
||||
|
||||
test "schemaless calendar types" do
|
||||
datetime = ~N[2014-01-16 20:26:51]
|
||||
assert {1, _} =
|
||||
TestRepo.insert_all("posts", [[inserted_at: datetime]])
|
||||
assert {1, _} =
|
||||
TestRepo.update_all("posts", set: [inserted_at: datetime])
|
||||
assert [_] =
|
||||
TestRepo.all(from p in "posts", where: p.inserted_at >= ^datetime, select: p.inserted_at)
|
||||
assert [_] =
|
||||
TestRepo.all(from p in "posts", where: p.inserted_at in [^datetime], select: p.inserted_at)
|
||||
assert [_] =
|
||||
TestRepo.all(from p in "posts", where: p.inserted_at in ^[datetime], select: p.inserted_at)
|
||||
end
|
||||
end
|
||||
53
phoenix/deps/ecto/integration_test/cases/windows.exs
Normal file
53
phoenix/deps/ecto/integration_test/cases/windows.exs
Normal file
@@ -0,0 +1,53 @@
|
||||
defmodule Ecto.Integration.WindowsTest do
|
||||
use Ecto.Integration.Case, async: Application.compile_env(:ecto, :async_integration_tests, true)
|
||||
|
||||
alias Ecto.Integration.TestRepo
|
||||
import Ecto.Query
|
||||
|
||||
alias Ecto.Integration.{Comment, User, Post}
|
||||
|
||||
test "over" do
|
||||
u1 = TestRepo.insert!(%User{name: "Tester"})
|
||||
u2 = TestRepo.insert!(%User{name: "Developer"})
|
||||
c1 = TestRepo.insert!(%Comment{text: "1", author_id: u1.id})
|
||||
c2 = TestRepo.insert!(%Comment{text: "2", author_id: u1.id})
|
||||
c3 = TestRepo.insert!(%Comment{text: "3", author_id: u1.id})
|
||||
c4 = TestRepo.insert!(%Comment{text: "4", author_id: u2.id})
|
||||
|
||||
# Over nothing
|
||||
query = from(c in Comment, select: [c, count(c.id) |> over()])
|
||||
assert [[^c1, 4], [^c2, 4], [^c3, 4], [^c4, 4]] = TestRepo.all(query)
|
||||
|
||||
# Over partition
|
||||
query = from(c in Comment, select: [c, count(c.id) |> over(partition_by: c.author_id)])
|
||||
assert [[^c1, 3], [^c2, 3], [^c3, 3], [^c4, 1]] = TestRepo.all(query)
|
||||
|
||||
# Over window
|
||||
query = from(c in Comment, windows: [w: [partition_by: c.author_id]], select: [c, count(c.id) |> over(:w)])
|
||||
assert [[^c1, 3], [^c2, 3], [^c3, 3], [^c4, 1]] = TestRepo.all(query)
|
||||
end
|
||||
|
||||
test "frame" do
|
||||
posts = Enum.map(0..6, &%{counter: &1, visits: round(:math.pow(2, &1))})
|
||||
TestRepo.insert_all(Post, posts)
|
||||
|
||||
n = 1
|
||||
query = from(p in Post,
|
||||
windows: [w: [order_by: p.counter, frame: fragment("ROWS BETWEEN ? PRECEDING AND ? FOLLOWING", ^n, ^n)]],
|
||||
select: [p.counter, sum(p.visits) |> over(:w)]
|
||||
)
|
||||
assert [[0, 3], [1, 7], [2, 14], [3, 28], [4, 56], [5, 112], [6, 96]] = TestRepo.all(query)
|
||||
|
||||
query = from(p in Post,
|
||||
windows: [w: [order_by: p.counter, frame: fragment("ROWS BETWEEN 1 FOLLOWING AND UNBOUNDED FOLLOWING")]],
|
||||
select: [p.counter, sum(p.visits) |> over(:w)]
|
||||
)
|
||||
assert [[0, 126], [1, 124], [2, 120], [3, 112], [4, 96], [5, 64], [6, nil]] = TestRepo.all(query)
|
||||
|
||||
query = from(p in Post,
|
||||
windows: [w: [order_by: p.counter, frame: fragment("ROWS CURRENT ROW")]],
|
||||
select: [p.counter, sum(p.visits) |> over(:w)]
|
||||
)
|
||||
assert [[0, 1], [1, 2], [2, 4], [3, 8], [4, 16], [5, 32], [6, 64]] = TestRepo.all(query)
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user