866 lines
28 KiB
Elixir
866 lines
28 KiB
Elixir
|
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
|