190 lines
6.4 KiB
Elixir
190 lines
6.4 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")
|
|
|
|
# Verify query count is reasonable (should avoid N+1 queries)
|
|
# Expected: 1 query for groups list + possible count queries
|
|
# Allow some overhead for LiveView setup queries
|
|
assert final_count <= 5,
|
|
"Expected max 5 queries (groups list + possible counts + LiveView setup), 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"
|
|
|
|
# Verify query count is reasonable (member count should be calculated efficiently)
|
|
# Expected: 1 query for groups + 1 query for member counts (aggregated)
|
|
# Allow some overhead for LiveView setup queries
|
|
assert final_count <= 5,
|
|
"Expected max 5 queries (groups + member counts + LiveView setup), got #{final_count}. This suggests inefficient member count calculation."
|
|
end
|
|
end
|
|
end
|