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 + 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" # 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