test: add tests for group resource #371
Some checks failed
continuous-integration/drone/push Build is failing
Some checks failed
continuous-integration/drone/push Build is failing
This commit is contained in:
parent
2ebf289112
commit
0216dfcbbb
5 changed files with 879 additions and 0 deletions
141
test/membership/group_integration_test.exs
Normal file
141
test/membership/group_integration_test.exs
Normal file
|
|
@ -0,0 +1,141 @@
|
|||
defmodule Mv.Membership.GroupIntegrationTest do
|
||||
@moduledoc """
|
||||
Integration tests for many-to-many relationships and query performance.
|
||||
"""
|
||||
use Mv.DataCase, async: false
|
||||
|
||||
alias Mv.Membership
|
||||
|
||||
require Ash.Query
|
||||
import Ash.Expr
|
||||
|
||||
setup do
|
||||
system_actor = Mv.Helpers.SystemActor.get_system_actor()
|
||||
%{actor: system_actor}
|
||||
end
|
||||
|
||||
describe "Many-to-Many Relationship" do
|
||||
test "member can belong to multiple groups", %{actor: actor} do
|
||||
{:ok, member} = Membership.create_member(%{email: "test@test.com"}, actor: actor)
|
||||
{:ok, group1} = Membership.create_group(%{name: "Group One"}, actor: actor)
|
||||
{:ok, group2} = Membership.create_group(%{name: "Group Two"}, actor: actor)
|
||||
{:ok, group3} = Membership.create_group(%{name: "Group Three"}, actor: actor)
|
||||
|
||||
# Add member to all groups
|
||||
{:ok, _mg1} =
|
||||
Membership.create_member_group(%{member_id: member.id, group_id: group1.id},
|
||||
actor: actor
|
||||
)
|
||||
|
||||
{:ok, _mg2} =
|
||||
Membership.create_member_group(%{member_id: member.id, group_id: group2.id},
|
||||
actor: actor
|
||||
)
|
||||
|
||||
{:ok, _mg3} =
|
||||
Membership.create_member_group(%{member_id: member.id, group_id: group3.id},
|
||||
actor: actor
|
||||
)
|
||||
|
||||
# Load member with groups
|
||||
{:ok, member_with_groups} =
|
||||
Ash.load(member, :groups, actor: actor, domain: Mv.Membership)
|
||||
|
||||
assert length(member_with_groups.groups) == 3
|
||||
assert Enum.any?(member_with_groups.groups, &(&1.id == group1.id))
|
||||
assert Enum.any?(member_with_groups.groups, &(&1.id == group2.id))
|
||||
assert Enum.any?(member_with_groups.groups, &(&1.id == group3.id))
|
||||
end
|
||||
|
||||
test "group can contain multiple members", %{actor: actor} do
|
||||
{:ok, member1} = Membership.create_member(%{email: "member1@test.com"}, actor: actor)
|
||||
{:ok, member2} = Membership.create_member(%{email: "member2@test.com"}, actor: actor)
|
||||
{:ok, member3} = Membership.create_member(%{email: "member3@test.com"}, actor: actor)
|
||||
{:ok, group} = Membership.create_group(%{name: "Test Group"}, actor: actor)
|
||||
|
||||
# Add all members to group
|
||||
{:ok, _mg1} =
|
||||
Membership.create_member_group(%{member_id: member1.id, group_id: group.id},
|
||||
actor: actor
|
||||
)
|
||||
|
||||
{:ok, _mg2} =
|
||||
Membership.create_member_group(%{member_id: member2.id, group_id: group.id},
|
||||
actor: actor
|
||||
)
|
||||
|
||||
{:ok, _mg3} =
|
||||
Membership.create_member_group(%{member_id: member3.id, group_id: group.id},
|
||||
actor: actor
|
||||
)
|
||||
|
||||
# Load group with members
|
||||
{:ok, group_with_members} =
|
||||
Ash.load(group, :members, actor: actor, domain: Mv.Membership)
|
||||
|
||||
assert length(group_with_members.members) == 3
|
||||
assert Enum.any?(group_with_members.members, &(&1.id == member1.id))
|
||||
assert Enum.any?(group_with_members.members, &(&1.id == member2.id))
|
||||
assert Enum.any?(group_with_members.members, &(&1.id == member3.id))
|
||||
end
|
||||
end
|
||||
|
||||
describe "Query Performance" do
|
||||
test "preloading groups with members avoids N+1 queries", %{actor: actor} do
|
||||
# Create test data
|
||||
{:ok, member1} = Membership.create_member(%{email: "member1@test.com"}, actor: actor)
|
||||
{:ok, member2} = Membership.create_member(%{email: "member2@test.com"}, actor: actor)
|
||||
{:ok, group1} = Membership.create_group(%{name: "Group One"}, actor: actor)
|
||||
{:ok, group2} = Membership.create_group(%{name: "Group Two"}, actor: actor)
|
||||
|
||||
# Create associations
|
||||
{:ok, _mg1} =
|
||||
Membership.create_member_group(%{member_id: member1.id, group_id: group1.id},
|
||||
actor: actor
|
||||
)
|
||||
|
||||
{:ok, _mg2} =
|
||||
Membership.create_member_group(%{member_id: member1.id, group_id: group2.id},
|
||||
actor: actor
|
||||
)
|
||||
|
||||
{:ok, _mg3} =
|
||||
Membership.create_member_group(%{member_id: member2.id, group_id: group1.id},
|
||||
actor: actor
|
||||
)
|
||||
|
||||
# Count queries using Telemetry
|
||||
query_count = Agent.start_link(fn -> 0 end) |> elem(1)
|
||||
|
||||
handler = fn _event, _measurements, _metadata, _config ->
|
||||
Agent.update(query_count, &(&1 + 1))
|
||||
end
|
||||
|
||||
:telemetry.attach("test-query-counter", [:ash, :query, :start], handler, nil)
|
||||
|
||||
# Load all members with groups preloaded (should be efficient with JOIN)
|
||||
{:ok, members} =
|
||||
Ash.read(Mv.Membership.Member, actor: actor, domain: Mv.Membership, load: [:groups])
|
||||
|
||||
final_count = Agent.get(query_count, & &1)
|
||||
:telemetry.detach("test-query-counter")
|
||||
|
||||
member1_loaded = Enum.find(members, &(&1.id == member1.id))
|
||||
member2_loaded = Enum.find(members, &(&1.id == member2.id))
|
||||
|
||||
# Verify preloading worked
|
||||
assert length(member1_loaded.groups) == 2
|
||||
assert length(member2_loaded.groups) == 1
|
||||
|
||||
# Verify groups are correctly associated
|
||||
assert Enum.any?(member1_loaded.groups, &(&1.id == group1.id))
|
||||
assert Enum.any?(member1_loaded.groups, &(&1.id == group2.id))
|
||||
assert Enum.any?(member2_loaded.groups, &(&1.id == group1.id))
|
||||
|
||||
# Verify query count is reasonable (should be 2 queries: one for members, one for groups)
|
||||
# Note: Exact count may vary based on Ash implementation, but should be much less than N+1
|
||||
assert final_count <= 3,
|
||||
"Expected max 3 queries (members + groups + possible count), got #{final_count}. This suggests N+1 query problem."
|
||||
end
|
||||
end
|
||||
end
|
||||
Loading…
Add table
Add a link
Reference in a new issue