mitgliederverwaltung/test/membership/join_request_approval_domain_test.exs
Simon 86d9242d83
All checks were successful
continuous-integration/drone/push Build is passing
feat: add approval ui for join requests
2026-03-11 02:04:03 +01:00

174 lines
5.7 KiB
Elixir
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

defmodule Mv.Membership.JoinRequestApprovalDomainTest do
@moduledoc """
Domain tests for JoinRequest approval: approve/reject and promotion to Member (Step 2).
Asserts that approve creates one Member with mapped data, reject does not create Member,
status rules, and idempotency. No User creation in MVP.
"""
use Mv.DataCase, async: true
import Ash.Expr
require Ash.Query
alias Mv.Fixtures
alias Mv.Helpers.SystemActor
alias Mv.Membership
alias Mv.Membership.Member
defp member_count do
actor = SystemActor.get_system_actor()
{:ok, members} = Membership.list_members(actor: actor)
length(members)
end
describe "approve_join_request/2 promotion to Member" do
test "approve creates exactly one member with email, first_name, last_name from JoinRequest" do
request =
Fixtures.submitted_join_request_fixture(%{
first_name: "Approved",
last_name: "User"
})
count_before = member_count()
user = Fixtures.user_with_role_fixture("normal_user")
assert {:ok, approved} = Membership.approve_join_request(request.id, actor: user)
assert approved.status == :approved
assert member_count() == count_before + 1
request_email = request.email
[member] =
Member
|> Ash.Query.filter(expr(^ref(:email) == ^request_email))
|> Ash.read!(actor: SystemActor.get_system_actor(), domain: Membership)
assert member.email == request.email
assert member.first_name == request.first_name
assert member.last_name == request.last_name
end
test "approve does not create a User (MVP)" do
request = Fixtures.submitted_join_request_fixture()
user = Fixtures.user_with_role_fixture("normal_user")
assert {:ok, _} = Membership.approve_join_request(request.id, actor: user)
# No User should exist with this email from the approval flow
request_email = request.email
users_with_email =
Mv.Accounts.User
|> Ash.Query.filter(expr(^ref(:email) == ^request_email))
|> Ash.read!(authorize?: false)
assert users_with_email == []
end
end
describe "reject_join_request/2" do
test "reject does not create a member" do
request = Fixtures.submitted_join_request_fixture()
count_before = member_count()
user = Fixtures.user_with_role_fixture("normal_user")
assert {:ok, rejected} = Membership.reject_join_request(request.id, actor: user)
assert rejected.status == :rejected
assert rejected.rejected_at != nil
assert member_count() == count_before
end
end
describe "approve_join_request/2 status and idempotency" do
test "approve when status is already approved is idempotent or returns error" do
request = Fixtures.submitted_join_request_fixture()
user = Fixtures.user_with_role_fixture("normal_user")
assert {:ok, _} = Membership.approve_join_request(request.id, actor: user)
count_after_first = member_count()
# Second approve: either {:ok, request} with no duplicate member, or {:error, _}
result = Membership.approve_join_request(request.id, actor: user)
if match?({:ok, _}, result) do
assert member_count() == count_after_first
else
assert {:error, _} = result
end
end
test "approve when status is pending_confirmation returns error" do
token = "pending-token-#{System.unique_integer([:positive])}"
attrs = %{
email: "pending#{System.unique_integer([:positive])}@example.com",
confirmation_token: token
}
{:ok, request} = Membership.submit_join_request(attrs, actor: nil)
assert request.status == :pending_confirmation
user = Fixtures.user_with_role_fixture("normal_user")
assert {:error, _} = Membership.approve_join_request(request.id, actor: user)
end
test "approve when status is rejected returns error" do
request = Fixtures.submitted_join_request_fixture()
user = Fixtures.user_with_role_fixture("normal_user")
assert {:ok, _} = Membership.reject_join_request(request.id, actor: user)
assert {:error, _} = Membership.approve_join_request(request.id, actor: user)
end
end
describe "approve_join_request/2 defaults" do
setup do
# Create a fee type and set it as the default in settings so SetDefaultMembershipFeeType
# can assign it when a member is created from a join request (no fee type in form_data).
actor = SystemActor.get_system_actor()
{:ok, fee_type} =
Ash.create(
Mv.MembershipFees.MembershipFeeType,
%{
name: "Default Test Fee Type #{System.unique_integer([:positive])}",
amount: Decimal.new("50.00"),
interval: :yearly
},
actor: actor,
domain: Mv.MembershipFees
)
{:ok, settings} = Membership.get_settings()
settings
|> Ash.Changeset.for_update(
:update_membership_fee_settings,
%{default_membership_fee_type_id: fee_type.id},
actor: actor
)
|> Ash.update!(actor: actor)
:ok
end
test "created member has join_date and membership_fee_type when not in form_data" do
request = Fixtures.submitted_join_request_fixture()
user = Fixtures.user_with_role_fixture("normal_user")
assert {:ok, _} = Membership.approve_join_request(request.id, actor: user)
request_email = request.email
[member] =
Member
|> Ash.Query.filter(expr(^ref(:email) == ^request_email))
|> Ash.read!(actor: SystemActor.get_system_actor(), domain: Membership)
assert member.join_date != nil
assert member.membership_fee_type_id != nil
end
end
end