defmodule MvWeb.AuthorizationTest do @moduledoc """ Tests for UI-level authorization helpers. """ use ExUnit.Case, async: true alias MvWeb.Authorization alias Mv.Membership.Member alias Mv.Accounts.User describe "can?/3 with resource atom" do test "returns true when user has permission for resource+action" do admin = %{ id: "admin-123", role: %{permission_set_name: "admin"} } assert Authorization.can?(admin, :create, Mv.Membership.Member) == true assert Authorization.can?(admin, :read, Mv.Membership.Member) == true assert Authorization.can?(admin, :update, Mv.Membership.Member) == true assert Authorization.can?(admin, :destroy, Mv.Membership.Member) == true end test "returns false when user lacks permission" do read_only_user = %{ id: "read-only-123", role: %{permission_set_name: "read_only"} } assert Authorization.can?(read_only_user, :create, Mv.Membership.Member) == false assert Authorization.can?(read_only_user, :read, Mv.Membership.Member) == true assert Authorization.can?(read_only_user, :update, Mv.Membership.Member) == false assert Authorization.can?(read_only_user, :destroy, Mv.Membership.Member) == false end test "returns false for nil user" do assert Authorization.can?(nil, :create, Mv.Membership.Member) == false assert Authorization.can?(nil, :read, Mv.Membership.Member) == false end test "admin can manage roles" do admin = %{ id: "admin-123", role: %{permission_set_name: "admin"} } assert Authorization.can?(admin, :create, Mv.Authorization.Role) == true assert Authorization.can?(admin, :read, Mv.Authorization.Role) == true assert Authorization.can?(admin, :update, Mv.Authorization.Role) == true assert Authorization.can?(admin, :destroy, Mv.Authorization.Role) == true end test "non-admin cannot manage roles" do normal_user = %{ id: "normal-123", role: %{permission_set_name: "normal_user"} } assert Authorization.can?(normal_user, :create, Mv.Authorization.Role) == false assert Authorization.can?(normal_user, :read, Mv.Authorization.Role) == false assert Authorization.can?(normal_user, :update, Mv.Authorization.Role) == false assert Authorization.can?(normal_user, :destroy, Mv.Authorization.Role) == false end end describe "can?/3 with record struct - scope :all" do test "admin can update any member" do admin = %{ id: "admin-123", role: %{permission_set_name: "admin"} } member1 = %Member{id: "member-1", user: %User{id: "other-user"}} member2 = %Member{id: "member-2", user: %User{id: "another-user"}} assert Authorization.can?(admin, :update, member1) == true assert Authorization.can?(admin, :update, member2) == true end test "normal_user can update any member" do normal_user = %{ id: "normal-123", role: %{permission_set_name: "normal_user"} } member = %Member{id: "member-1", user: %User{id: "other-user"}} assert Authorization.can?(normal_user, :update, member) == true end end describe "can?/3 with record struct - scope :own" do test "user can update own User record" do user = %{ id: "user-123", role: %{permission_set_name: "own_data"} } own_user_record = %User{id: "user-123"} other_user_record = %User{id: "other-user"} assert Authorization.can?(user, :update, own_user_record) == true assert Authorization.can?(user, :update, other_user_record) == false end end describe "can?/3 with record struct - scope :linked" do test "user can update linked member" do user = %{ id: "user-123", role: %{permission_set_name: "own_data"} } # Member has_one :user (inverse relationship) linked_member = %Member{id: "member-1", user: %User{id: "user-123"}} unlinked_member = %Member{id: "member-2", user: nil} unlinked_member_other = %Member{id: "member-3", user: %User{id: "other-user"}} assert Authorization.can?(user, :update, linked_member) == true assert Authorization.can?(user, :update, unlinked_member) == false assert Authorization.can?(user, :update, unlinked_member_other) == false end test "user can update CustomFieldValue of linked member" do user = %{ id: "user-123", role: %{permission_set_name: "own_data"} } linked_cfv = %Mv.Membership.CustomFieldValue{ id: "cfv-1", member: %Member{id: "member-1", user: %User{id: "user-123"}} } unlinked_cfv = %Mv.Membership.CustomFieldValue{ id: "cfv-2", member: %Member{id: "member-2", user: nil} } unlinked_cfv_other = %Mv.Membership.CustomFieldValue{ id: "cfv-3", member: %Member{id: "member-3", user: %User{id: "other-user"}} } assert Authorization.can?(user, :update, linked_cfv) == true assert Authorization.can?(user, :update, unlinked_cfv) == false assert Authorization.can?(user, :update, unlinked_cfv_other) == false end end describe "can_access_page?/2" do test "admin can access all pages via wildcard" do admin = %{ id: "admin-123", role: %{permission_set_name: "admin"} } assert Authorization.can_access_page?(admin, "/admin/roles") == true assert Authorization.can_access_page?(admin, "/members") == true assert Authorization.can_access_page?(admin, "/any/page") == true end test "read_only user can access allowed pages" do read_only_user = %{ id: "read-only-123", role: %{permission_set_name: "read_only"} } assert Authorization.can_access_page?(read_only_user, "/") == true assert Authorization.can_access_page?(read_only_user, "/members") == true assert Authorization.can_access_page?(read_only_user, "/members/123") == true assert Authorization.can_access_page?(read_only_user, "/admin/roles") == false end test "matches dynamic routes correctly" do read_only_user = %{ id: "read-only-123", role: %{permission_set_name: "read_only"} } assert Authorization.can_access_page?(read_only_user, "/members/123") == true assert Authorization.can_access_page?(read_only_user, "/members/abc") == true assert Authorization.can_access_page?(read_only_user, "/members/123/edit") == false end test "returns false for nil user" do assert Authorization.can_access_page?(nil, "/members") == false assert Authorization.can_access_page?(nil, "/admin/roles") == false end end describe "error handling" do test "user without role returns false" do user_without_role = %{id: "user-123", role: nil} assert Authorization.can?(user_without_role, :create, Mv.Membership.Member) == false assert Authorization.can_access_page?(user_without_role, "/members") == false end test "user with invalid permission_set_name returns false" do user_with_invalid_permission = %{ id: "user-123", role: %{permission_set_name: "invalid_set"} } assert Authorization.can?(user_with_invalid_permission, :create, Mv.Membership.Member) == false assert Authorization.can_access_page?(user_with_invalid_permission, "/members") == false end test "handles missing fields gracefully" do user_missing_role = %{id: "user-123"} assert Authorization.can?(user_missing_role, :create, Mv.Membership.Member) == false assert Authorization.can_access_page?(user_missing_role, "/members") == false end end end