test: add tests for approval ui

This commit is contained in:
Simon 2026-03-10 23:21:57 +01:00
parent 021b709e6a
commit 50433e607f
Signed by: simon
GPG key ID: 40E7A58C4AA1EDB2
6 changed files with 466 additions and 11 deletions

View file

@ -0,0 +1,139 @@
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
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

View file

@ -0,0 +1,115 @@
defmodule Mv.Membership.JoinRequestApprovalPolicyTest do
@moduledoc """
Policy tests for JoinRequest approval UI (Step 2).
Asserts that approve/reject and list are allowed for normal_user and admin,
and forbidden for read_only, own_data, and actor: nil.
No UI; domain and resource policies only.
"""
use Mv.DataCase, async: true
alias Mv.Fixtures
alias Mv.Membership
describe "list_join_requests/1" do
test "normal_user can list join requests" do
user = Fixtures.user_with_role_fixture("normal_user")
assert {:ok, _list} = Membership.list_join_requests(actor: user)
end
test "admin can list join requests" do
user = Fixtures.user_with_role_fixture("admin")
assert {:ok, _list} = Membership.list_join_requests(actor: user)
end
test "read_only cannot list join requests" do
user = Fixtures.user_with_role_fixture("read_only")
assert {:error, %Ash.Error.Forbidden{}} = Membership.list_join_requests(actor: user)
end
test "own_data cannot list join requests" do
user = Fixtures.user_with_role_fixture("own_data")
assert {:error, %Ash.Error.Forbidden{}} = Membership.list_join_requests(actor: user)
end
test "actor nil cannot list join requests" do
assert {:error, %Ash.Error.Forbidden{}} = Membership.list_join_requests(actor: nil)
end
end
describe "approve_join_request/2" do
setup do
request = Fixtures.submitted_join_request_fixture()
%{request: request}
end
test "normal_user can approve a submitted join request", %{request: request} do
user = Fixtures.user_with_role_fixture("normal_user")
assert {:ok, approved} = Membership.approve_join_request(request.id, actor: user)
assert approved.status == :approved
assert approved.approved_at != nil
assert approved.reviewed_by_user_id == user.id
end
test "admin can approve a submitted join request", %{request: request} do
user = Fixtures.user_with_role_fixture("admin")
assert {:ok, approved} = Membership.approve_join_request(request.id, actor: user)
assert approved.status == :approved
end
test "read_only cannot approve", %{request: request} do
user = Fixtures.user_with_role_fixture("read_only")
assert {:error, %Ash.Error.Forbidden{}} =
Membership.approve_join_request(request.id, actor: user)
end
test "own_data cannot approve", %{request: request} do
user = Fixtures.user_with_role_fixture("own_data")
assert {:error, %Ash.Error.Forbidden{}} =
Membership.approve_join_request(request.id, actor: user)
end
test "actor nil cannot approve", %{request: request} do
assert {:error, %Ash.Error.Forbidden{}} =
Membership.approve_join_request(request.id, actor: nil)
end
end
describe "reject_join_request/2" do
setup do
request = Fixtures.submitted_join_request_fixture()
%{request: request}
end
test "normal_user can reject a submitted join request", %{request: request} do
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 rejected.reviewed_by_user_id == user.id
end
test "admin can reject a submitted join request", %{request: request} do
user = Fixtures.user_with_role_fixture("admin")
assert {:ok, rejected} = Membership.reject_join_request(request.id, actor: user)
assert rejected.status == :rejected
end
test "read_only cannot reject", %{request: request} do
user = Fixtures.user_with_role_fixture("read_only")
assert {:error, %Ash.Error.Forbidden{}} =
Membership.reject_join_request(request.id, actor: user)
end
test "own_data cannot reject", %{request: request} do
user = Fixtures.user_with_role_fixture("own_data")
assert {:error, %Ash.Error.Forbidden{}} =
Membership.reject_join_request(request.id, actor: user)
end
test "actor nil cannot reject", %{request: request} do
assert {:error, %Ash.Error.Forbidden{}} =
Membership.reject_join_request(request.id, actor: nil)
end
end
end