diff --git a/test/mv_web/member_live/show_membership_fees_test.exs b/test/mv_web/member_live/show_membership_fees_test.exs index 5636d2b..b57417f 100644 --- a/test/mv_web/member_live/show_membership_fees_test.exs +++ b/test/mv_web/member_live/show_membership_fees_test.exs @@ -320,13 +320,12 @@ defmodule MvWeb.MemberLive.ShowMembershipFeesTest do describe "read_only cannot delete all cycles (policy enforced via Ash.destroy)" do @tag role: :read_only - test "confirm_delete_all_cycles returns error for read_only user", %{ + test "Ash.destroy returns Forbidden for read_only so handler would reject", %{ current_user: read_only_user } do - # Backend policy test: read_only cannot destroy any cycle. - # The UI hides the Delete All button for read_only; this test ensures - # that if the handler were triggered (e.g. via dev tools), the server - # would enforce policy and return Forbidden. + # The handler uses Ash.destroy per cycle, so if the handler were triggered + # (e.g. via dev tools), the server would enforce policy and show an error. + # This test verifies that Ash.destroy(cycle, actor: read_only_user) returns Forbidden. fee_type = create_fee_type(%{interval: :yearly}) member = create_member(%{membership_fee_type_id: fee_type.id}) cycle = create_cycle(member, fee_type, %{cycle_start: ~D[2023-01-01], status: :unpaid}) @@ -335,4 +334,47 @@ defmodule MvWeb.MemberLive.ShowMembershipFeesTest do Ash.destroy(cycle, domain: Mv.MembershipFees, actor: read_only_user) end end + + describe "confirm_delete_all_cycles handler (policy enforced)" do + @tag role: :admin + test "admin can delete all cycles via UI and cycles are removed", %{conn: conn} do + # Use English locale so confirmation "Yes" matches gettext("Yes") + conn = put_session(conn, :locale, "en") + + fee_type = create_fee_type(%{interval: :yearly}) + member = create_member(%{membership_fee_type_id: fee_type.id}) + _c1 = create_cycle(member, fee_type, %{cycle_start: ~D[2022-01-01], status: :paid}) + _c2 = create_cycle(member, fee_type, %{cycle_start: ~D[2023-01-01], status: :unpaid}) + + {:ok, view, _html} = live(conn, "/members/#{member.id}") + + view + |> element("button[phx-click='switch_tab'][phx-value-tab='membership_fees']") + |> render_click() + + view + |> element("button[phx-click='delete_all_cycles']") + |> render_click() + + view + |> element("input[phx-keyup='update_delete_all_confirmation']") + |> render_keyup(%{"value" => "Yes"}) + + view + |> element("button[phx-click='confirm_delete_all_cycles']") + |> render_click() + + _html = render(view) + + system_actor = Mv.Helpers.SystemActor.get_system_actor() + + remaining = + Mv.MembershipFees.MembershipFeeCycle + |> Ash.Query.filter(member_id == ^member.id) + |> Ash.read!(actor: system_actor) + + assert remaining == [], + "Expected all cycles to be deleted (handler enforces policy via Ash.destroy)" + end + end end