defmodule MvWeb.GroupLive.ShowIntegrationTest do @moduledoc """ Integration tests for Add/Remove Member functionality. Tests data consistency, database operations, and multiple operations. """ use MvWeb.ConnCase, async: false import Phoenix.LiveViewTest use Gettext, backend: MvWeb.Gettext alias Mv.Membership alias Mv.Fixtures describe "data consistency" do test "member appears in group after add (verified in database)", %{conn: conn} do system_actor = Mv.Helpers.SystemActor.get_system_actor() group = Fixtures.group_fixture() {:ok, member} = Membership.create_member( %{ first_name: "Alice", last_name: "Smith", email: "alice@example.com" }, actor: system_actor ) {:ok, view, _html} = live(conn, "/groups/#{group.slug}") # Add member via UI view |> element("button", "Add Member") |> render_click() view |> element("#member-search-input") |> render_focus() view |> element("#member-search-input") |> render_change(%{"member_search" => "Alice"}) view |> element("[data-member-id='#{member.id}']") |> render_click() view |> element("button", "Add") |> render_click() # Verify in database require Ash.Query query = Mv.Membership.Group |> Ash.Query.filter(slug == ^group.slug) |> Ash.Query.load([:members]) {:ok, updated_group} = Ash.read_one(query, actor: system_actor, domain: Mv.Membership) # Member should be in group assert Enum.any?(updated_group.members, &(&1.id == member.id)) end test "member disappears from group after remove (verified in database)", %{conn: conn} do system_actor = Mv.Helpers.SystemActor.get_system_actor() group = Fixtures.group_fixture() {:ok, member} = Membership.create_member( %{ first_name: "Bob", last_name: "Jones", email: "bob@example.com" }, actor: system_actor ) # Add member to group Membership.create_member_group(%{member_id: member.id, group_id: group.id}, actor: system_actor ) {:ok, view, _html} = live(conn, "/groups/#{group.slug}") # Remove member via UI view |> element("button[phx-click='remove_member']", "") |> render_click() # Verify in database require Ash.Query query = Mv.Membership.Group |> Ash.Query.filter(slug == ^group.slug) |> Ash.Query.load([:members]) {:ok, updated_group} = Ash.read_one(query, actor: system_actor, domain: Mv.Membership) # Member should NOT be in group refute Enum.any?(updated_group.members, &(&1.id == member.id)) end test "MemberGroup association is created correctly", %{conn: conn} do system_actor = Mv.Helpers.SystemActor.get_system_actor() group = Fixtures.group_fixture() {:ok, member} = Membership.create_member( %{ first_name: "Charlie", last_name: "Brown", email: "charlie@example.com" }, actor: system_actor ) {:ok, view, _html} = live(conn, "/groups/#{group.slug}") # Add member view |> element("button", "Add Member") |> render_click() view |> element("#member-search-input") |> render_focus() view |> element("#member-search-input") |> render_change(%{"member_search" => "Charlie"}) view |> element("[data-member-id='#{member.id}']") |> render_click() view |> element("button", "Add") |> render_click() # Verify MemberGroup association exists require Ash.Query {:ok, member_groups} = Ash.read( Mv.Membership.MemberGroup |> Ash.Query.filter(member_id == ^member.id and group_id == ^group.id), actor: system_actor, domain: Mv.Membership ) assert length(member_groups) == 1 assert hd(member_groups).member_id == member.id assert hd(member_groups).group_id == group.id end test "MemberGroup association is deleted correctly", %{conn: conn} do system_actor = Mv.Helpers.SystemActor.get_system_actor() group = Fixtures.group_fixture() {:ok, member} = Membership.create_member( %{ first_name: "David", last_name: "Wilson", email: "david@example.com" }, actor: system_actor ) # Add member first Membership.create_member_group(%{member_id: member.id, group_id: group.id}, actor: system_actor ) {:ok, view, _html} = live(conn, "/groups/#{group.slug}") # Remove member view |> element("button[phx-click='remove_member']", "") |> render_click() # Verify MemberGroup association is deleted require Ash.Query {:ok, member_groups} = Ash.read( Mv.Membership.MemberGroup |> Ash.Query.filter(member_id == ^member.id and group_id == ^group.id), actor: system_actor, domain: Mv.Membership ) assert member_groups == [] end test "member itself is NOT deleted (only association)", %{conn: conn} do system_actor = Mv.Helpers.SystemActor.get_system_actor() group = Fixtures.group_fixture() {:ok, member} = Membership.create_member( %{ first_name: "Eve", last_name: "Davis", email: "eve@example.com" }, actor: system_actor ) # Add member to group Membership.create_member_group(%{member_id: member.id, group_id: group.id}, actor: system_actor ) {:ok, view, _html} = live(conn, "/groups/#{group.slug}") # Remove member from group view |> element("button[phx-click='remove_member']", "") |> render_click() # Verify member still exists {:ok, member_after_remove} = Ash.get(Mv.Membership.Member, member.id, actor: system_actor) assert member_after_remove.id == member.id assert member_after_remove.first_name == "Eve" end end describe "multiple operations" do test "multiple members can be added sequentially", %{conn: conn} do system_actor = Mv.Helpers.SystemActor.get_system_actor() group = Fixtures.group_fixture() {:ok, member1} = Membership.create_member( %{ first_name: "Frank", last_name: "Moore", email: "frank@example.com" }, actor: system_actor ) {:ok, member2} = Membership.create_member( %{ first_name: "Grace", last_name: "Taylor", email: "grace@example.com" }, actor: system_actor ) {:ok, view, _html} = live(conn, "/groups/#{group.slug}") # Add first member view |> element("button", "Add Member") |> render_click() view |> element("#member-search-input") |> render_focus() view |> element("#member-search-input") |> render_change(%{"member_search" => "Frank"}) view |> element("[data-member-id='#{member1.id}']") |> render_click() view |> element("button", "Add") |> render_click() # Add second member view |> element("button", "Add Member") |> render_click() view |> element("#member-search-input") |> render_focus() view |> element("#member-search-input") |> render_change(%{"member_search" => "Grace"}) view |> element("[data-member-id='#{member2.id}']") |> render_click() view |> element("button", "Add") |> render_click() # Both members should be in list html = render(view) assert html =~ "Frank" assert html =~ "Grace" end test "multiple members can be removed sequentially", %{conn: conn} do system_actor = Mv.Helpers.SystemActor.get_system_actor() group = Fixtures.group_fixture() {:ok, member1} = Membership.create_member( %{ first_name: "Henry", last_name: "Anderson", email: "henry@example.com" }, actor: system_actor ) {:ok, member2} = Membership.create_member( %{ first_name: "Isabel", last_name: "Martinez", email: "isabel@example.com" }, actor: system_actor ) # Add both members 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 ) {:ok, view, html} = live(conn, "/groups/#{group.slug}") # Both should be in list initially assert html =~ "Henry" assert html =~ "Isabel" # Remove first member view |> element("button[phx-click='remove_member']", "") |> render_click() # Remove second member view |> element("button[phx-click='remove_member']", "") |> render_click() # Both should be removed html = render(view) refute html =~ "Henry" refute html =~ "Isabel" end test "add and remove can be mixed", %{conn: conn} do system_actor = Mv.Helpers.SystemActor.get_system_actor() group = Fixtures.group_fixture() {:ok, member1} = Membership.create_member( %{ first_name: "Jack", last_name: "White", email: "jack@example.com" }, actor: system_actor ) {:ok, member2} = Membership.create_member( %{ first_name: "Kate", last_name: "Black", email: "kate@example.com" }, actor: system_actor ) # Add member1 first Membership.create_member_group(%{member_id: member1.id, group_id: group.id}, actor: system_actor ) {:ok, view, _html} = live(conn, "/groups/#{group.slug}") # Add member2 view |> element("button", "Add Member") |> render_click() view |> element("#member-search-input") |> render_focus() view |> element("#member-search-input") |> render_change(%{"member_search" => "Kate"}) view |> element("[data-member-id='#{member2.id}']") |> render_click() view |> element("button", "Add") |> render_click() # Remove member1 view |> element("button[phx-click='remove_member']", "") |> render_click() # Only member2 should remain html = render(view) refute html =~ "Jack" assert html =~ "Kate" end end end