mitgliederverwaltung/test/mv_web/live/group_live/index_test.exs
Simon b4adf63e83
Some checks failed
continuous-integration/drone/push Build is failing
feix: optimize queries for groups
2026-01-29 15:22:40 +01:00

198 lines
6.8 KiB
Elixir

defmodule MvWeb.GroupLive.IndexTest do
@moduledoc """
Tests for the groups overview page.
Tests cover:
- Displaying list of groups
- Creating new groups
- Deleting groups with confirmation
- Permission checks
- Edge cases
"""
use MvWeb.ConnCase, async: false
import Phoenix.LiveViewTest
use Gettext, backend: MvWeb.Gettext
alias Mv.Membership
alias Mv.Fixtures
describe "mount and display" do
test "page renders successfully for admin user", %{conn: conn} do
{:ok, _view, html} = live(conn, "/groups")
assert html =~ gettext("Groups")
end
test "lists all groups", %{conn: conn} do
group1 = Fixtures.group_fixture(%{name: "Group One"})
group2 = Fixtures.group_fixture(%{name: "Group Two"})
{:ok, _view, html} = live(conn, "/groups")
assert html =~ group1.name
assert html =~ group2.name
end
test "displays group name, description, and member count", %{conn: conn} do
_group = Fixtures.group_fixture(%{name: "Test Group", description: "Test description"})
{:ok, _view, html} = live(conn, "/groups")
assert html =~ "Test Group"
assert html =~ "Test description"
# Member count should be displayed (0 for empty group)
assert html =~ "0" or html =~ gettext("Members") or html =~ "Mitglieder"
end
test "displays 'Create Group' button for admin users", %{conn: conn} do
{:ok, _view, html} = live(conn, "/groups")
assert html =~ gettext("Create Group") or html =~ "create" or html =~ "new" or
html =~ "Gruppe erstellen"
end
test "displays empty state when no groups exist", %{conn: conn} do
{:ok, _view, html} = live(conn, "/groups")
# Should show empty state or empty list message
assert html =~ gettext("No groups") or html =~ "0" or html =~ "empty" or
html =~ "Keine Gruppen"
end
end
describe "edge cases" do
test "handles very long group names correctly", %{conn: conn} do
long_name = String.duplicate("a", 100)
_group = Fixtures.group_fixture(%{name: long_name})
{:ok, _view, html} = live(conn, "/groups")
assert html =~ long_name
end
test "handles very long descriptions correctly", %{conn: conn} do
long_description = String.duplicate("a", 500)
_group = Fixtures.group_fixture(%{description: long_description})
{:ok, _view, html} = live(conn, "/groups")
assert html =~ long_description or html =~ String.slice(long_description, 0, 100)
end
end
describe "security" do
@tag role: :member
test "non-admin users cannot access groups page", %{conn: conn} do
# Should redirect or show 403
result = live(conn, "/groups")
assert match?({:error, {:redirect, %{to: _}}}, result) ||
match?({:error, {:live_redirect, %{to: _}}}, result)
end
@tag role: :unauthenticated
test "unauthenticated users are redirected to login", %{conn: conn} do
result = live(conn, "/groups")
assert match?({:error, {:redirect, %{to: "/sign-in"}}}, result) ||
match?({:error, {:live_redirect, %{to: "/sign-in"}}}, result)
end
@tag role: :member
test "read-only users can view groups but not create", %{conn: conn} do
# Create read-only user
read_only_user = Fixtures.user_with_role_fixture("read_only")
conn = conn_with_password_user(conn, read_only_user)
{:ok, _view, html} = live(conn, "/groups")
# Should be able to see groups
assert html =~ gettext("Groups")
# Should NOT see create button
refute html =~ gettext("Create Group") or html =~ "create"
end
end
describe "performance" do
@describetag :slow
test "page loads efficiently with many groups", %{conn: conn} do
# Create multiple groups
Enum.each(1..10, fn _ -> Fixtures.group_fixture() end)
# Count queries using Telemetry
query_count_agent = Agent.start_link(fn -> 0 end) |> elem(1)
handler = fn _event, _measurements, _metadata, _config ->
Agent.update(query_count_agent, &(&1 + 1))
end
handler_id = "test-query-counter-#{System.unique_integer([:positive])}"
:telemetry.attach(handler_id, [:ash, :query, :start], handler, nil)
# Should load without N+1 queries
{:ok, _view, html} = live(conn, "/groups")
final_count = Agent.get(query_count_agent, & &1)
:telemetry.detach(handler_id)
# Verify all groups are displayed
assert html =~ gettext("Groups")
# Log actual query count for monitoring
IO.puts("\n[PERF] GroupLive.Index 'page loads efficiently': #{final_count} queries")
# Verify query count is reasonable (should avoid N+1 queries)
# Expected: 1 query for groups list + 1 batch query for member counts + LiveView setup queries
# Allow overhead for authorization, LiveView setup, and other initialization queries
assert final_count <= 12,
"Expected max 12 queries (groups list + batch member counts + LiveView setup + auth), got #{final_count}. This suggests N+1 query problem."
end
test "member count is loaded efficiently via calculation", %{conn: conn} do
group = Fixtures.group_fixture()
member1 = Fixtures.member_fixture()
member2 = Fixtures.member_fixture()
# Add members to group
system_actor = Mv.Helpers.SystemActor.get_system_actor()
Membership.create_member_group(%{member_id: member1.id, group_id: group.id},
actor: system_actor
)
Membership.create_member_group(%{member_id: member2.id, group_id: group.id},
actor: system_actor
)
# Count queries using Telemetry
query_count_agent = Agent.start_link(fn -> 0 end) |> elem(1)
handler = fn _event, _measurements, _metadata, _config ->
Agent.update(query_count_agent, &(&1 + 1))
end
handler_id = "test-query-counter-#{System.unique_integer([:positive])}"
:telemetry.attach(handler_id, [:ash, :query, :start], handler, nil)
{:ok, _view, html} = live(conn, "/groups")
final_count = Agent.get(query_count_agent, & &1)
:telemetry.detach(handler_id)
# Member count should be displayed (should be 2)
assert html =~ "2" or html =~ gettext("Members") or html =~ "Mitglieder"
# Log actual query count for monitoring
IO.puts(
"\n[PERF] GroupLive.Index 'member count is loaded efficiently': #{final_count} queries"
)
# Verify query count is reasonable (member count should be calculated efficiently)
# Expected: 1 query for groups + 1 batch query for member counts + LiveView setup queries
# Allow overhead for authorization, LiveView setup, and other initialization queries
assert final_count <= 12,
"Expected max 12 queries (groups + batch member counts + LiveView setup + auth), got #{final_count}. This suggests inefficient member count calculation."
end
end
end