Merge branch 'main' into feature/273_member_fields
Some checks failed
continuous-integration/drone/push Build is failing
Some checks failed
continuous-integration/drone/push Build is failing
This commit is contained in:
commit
0c8a255476
13 changed files with 1659 additions and 131 deletions
584
test/mv/authorization/permission_sets_test.exs
Normal file
584
test/mv/authorization/permission_sets_test.exs
Normal file
|
|
@ -0,0 +1,584 @@
|
|||
defmodule Mv.Authorization.PermissionSetsTest do
|
||||
@moduledoc """
|
||||
Tests for the PermissionSets module that defines hardcoded permission sets.
|
||||
"""
|
||||
use ExUnit.Case, async: true
|
||||
|
||||
alias Mv.Authorization.PermissionSets
|
||||
|
||||
describe "all_permission_sets/0" do
|
||||
test "returns all four permission sets" do
|
||||
sets = PermissionSets.all_permission_sets()
|
||||
|
||||
assert length(sets) == 4
|
||||
assert :own_data in sets
|
||||
assert :read_only in sets
|
||||
assert :normal_user in sets
|
||||
assert :admin in sets
|
||||
end
|
||||
end
|
||||
|
||||
describe "get_permissions/1" do
|
||||
test "all permission sets return map with :resources and :pages keys" do
|
||||
for set <- PermissionSets.all_permission_sets() do
|
||||
permissions = PermissionSets.get_permissions(set)
|
||||
|
||||
assert Map.has_key?(permissions, :resources),
|
||||
"#{set} missing :resources key"
|
||||
|
||||
assert Map.has_key?(permissions, :pages),
|
||||
"#{set} missing :pages key"
|
||||
|
||||
assert is_list(permissions.resources),
|
||||
"#{set} :resources must be a list"
|
||||
|
||||
assert is_list(permissions.pages),
|
||||
"#{set} :pages must be a list"
|
||||
end
|
||||
end
|
||||
|
||||
test "each resource permission has required keys" do
|
||||
permissions = PermissionSets.get_permissions(:own_data)
|
||||
|
||||
Enum.each(permissions.resources, fn perm ->
|
||||
assert Map.has_key?(perm, :resource)
|
||||
assert Map.has_key?(perm, :action)
|
||||
assert Map.has_key?(perm, :scope)
|
||||
assert Map.has_key?(perm, :granted)
|
||||
assert is_binary(perm.resource)
|
||||
assert perm.action in [:read, :create, :update, :destroy]
|
||||
assert perm.scope in [:own, :linked, :all]
|
||||
assert is_boolean(perm.granted)
|
||||
end)
|
||||
end
|
||||
|
||||
test "pages lists are non-empty for all permission sets" do
|
||||
for set <- [:own_data, :read_only, :normal_user, :admin] do
|
||||
permissions = PermissionSets.get_permissions(set)
|
||||
|
||||
assert not Enum.empty?(permissions.pages),
|
||||
"Permission set #{set} should have at least one page"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "get_permissions/1 - :own_data permission content" do
|
||||
test "allows User read/update with scope :own" do
|
||||
permissions = PermissionSets.get_permissions(:own_data)
|
||||
|
||||
user_read =
|
||||
Enum.find(permissions.resources, fn p -> p.resource == "User" && p.action == :read end)
|
||||
|
||||
user_update =
|
||||
Enum.find(permissions.resources, fn p -> p.resource == "User" && p.action == :update end)
|
||||
|
||||
assert user_read.scope == :own
|
||||
assert user_read.granted == true
|
||||
assert user_update.scope == :own
|
||||
assert user_update.granted == true
|
||||
end
|
||||
|
||||
test "allows Member read/update with scope :linked" do
|
||||
permissions = PermissionSets.get_permissions(:own_data)
|
||||
|
||||
member_read =
|
||||
Enum.find(permissions.resources, fn p -> p.resource == "Member" && p.action == :read end)
|
||||
|
||||
member_update =
|
||||
Enum.find(permissions.resources, fn p -> p.resource == "Member" && p.action == :update end)
|
||||
|
||||
assert member_read.scope == :linked
|
||||
assert member_read.granted == true
|
||||
assert member_update.scope == :linked
|
||||
assert member_update.granted == true
|
||||
end
|
||||
|
||||
test "allows CustomFieldValue read/update with scope :linked" do
|
||||
permissions = PermissionSets.get_permissions(:own_data)
|
||||
|
||||
cfv_read =
|
||||
Enum.find(permissions.resources, fn p ->
|
||||
p.resource == "CustomFieldValue" && p.action == :read
|
||||
end)
|
||||
|
||||
cfv_update =
|
||||
Enum.find(permissions.resources, fn p ->
|
||||
p.resource == "CustomFieldValue" && p.action == :update
|
||||
end)
|
||||
|
||||
assert cfv_read.scope == :linked
|
||||
assert cfv_read.granted == true
|
||||
assert cfv_update.scope == :linked
|
||||
assert cfv_update.granted == true
|
||||
end
|
||||
|
||||
test "allows CustomField read with scope :all" do
|
||||
permissions = PermissionSets.get_permissions(:own_data)
|
||||
|
||||
cf_read =
|
||||
Enum.find(permissions.resources, fn p ->
|
||||
p.resource == "CustomField" && p.action == :read
|
||||
end)
|
||||
|
||||
assert cf_read.scope == :all
|
||||
assert cf_read.granted == true
|
||||
end
|
||||
|
||||
test "includes correct pages" do
|
||||
permissions = PermissionSets.get_permissions(:own_data)
|
||||
|
||||
assert "/" in permissions.pages
|
||||
assert "/profile" in permissions.pages
|
||||
assert "/members/:id" in permissions.pages
|
||||
end
|
||||
end
|
||||
|
||||
describe "get_permissions/1 - :read_only permission content" do
|
||||
test "allows User read/update with scope :own" do
|
||||
permissions = PermissionSets.get_permissions(:read_only)
|
||||
|
||||
user_read =
|
||||
Enum.find(permissions.resources, fn p -> p.resource == "User" && p.action == :read end)
|
||||
|
||||
user_update =
|
||||
Enum.find(permissions.resources, fn p -> p.resource == "User" && p.action == :update end)
|
||||
|
||||
assert user_read.scope == :own
|
||||
assert user_read.granted == true
|
||||
assert user_update.scope == :own
|
||||
assert user_update.granted == true
|
||||
end
|
||||
|
||||
test "allows Member read with scope :all" do
|
||||
permissions = PermissionSets.get_permissions(:read_only)
|
||||
|
||||
member_read =
|
||||
Enum.find(permissions.resources, fn p -> p.resource == "Member" && p.action == :read end)
|
||||
|
||||
assert member_read.scope == :all
|
||||
assert member_read.granted == true
|
||||
end
|
||||
|
||||
test "does NOT allow Member create/update/destroy" do
|
||||
permissions = PermissionSets.get_permissions(:read_only)
|
||||
|
||||
member_create =
|
||||
Enum.find(permissions.resources, fn p -> p.resource == "Member" && p.action == :create end)
|
||||
|
||||
member_update =
|
||||
Enum.find(permissions.resources, fn p -> p.resource == "Member" && p.action == :update end)
|
||||
|
||||
member_destroy =
|
||||
Enum.find(permissions.resources, fn p ->
|
||||
p.resource == "Member" && p.action == :destroy
|
||||
end)
|
||||
|
||||
assert member_create == nil || member_create.granted == false
|
||||
assert member_update == nil || member_update.granted == false
|
||||
assert member_destroy == nil || member_destroy.granted == false
|
||||
end
|
||||
|
||||
test "allows CustomFieldValue read with scope :all" do
|
||||
permissions = PermissionSets.get_permissions(:read_only)
|
||||
|
||||
cfv_read =
|
||||
Enum.find(permissions.resources, fn p ->
|
||||
p.resource == "CustomFieldValue" && p.action == :read
|
||||
end)
|
||||
|
||||
assert cfv_read.scope == :all
|
||||
assert cfv_read.granted == true
|
||||
end
|
||||
|
||||
test "does NOT allow CustomFieldValue create/update/destroy" do
|
||||
permissions = PermissionSets.get_permissions(:read_only)
|
||||
|
||||
cfv_create =
|
||||
Enum.find(permissions.resources, fn p ->
|
||||
p.resource == "CustomFieldValue" && p.action == :create
|
||||
end)
|
||||
|
||||
cfv_update =
|
||||
Enum.find(permissions.resources, fn p ->
|
||||
p.resource == "CustomFieldValue" && p.action == :update
|
||||
end)
|
||||
|
||||
cfv_destroy =
|
||||
Enum.find(permissions.resources, fn p ->
|
||||
p.resource == "CustomFieldValue" && p.action == :destroy
|
||||
end)
|
||||
|
||||
assert cfv_create == nil || cfv_create.granted == false
|
||||
assert cfv_update == nil || cfv_update.granted == false
|
||||
assert cfv_destroy == nil || cfv_destroy.granted == false
|
||||
end
|
||||
|
||||
test "allows CustomField read with scope :all" do
|
||||
permissions = PermissionSets.get_permissions(:read_only)
|
||||
|
||||
cf_read =
|
||||
Enum.find(permissions.resources, fn p ->
|
||||
p.resource == "CustomField" && p.action == :read
|
||||
end)
|
||||
|
||||
assert cf_read.scope == :all
|
||||
assert cf_read.granted == true
|
||||
end
|
||||
|
||||
test "includes correct pages" do
|
||||
permissions = PermissionSets.get_permissions(:read_only)
|
||||
|
||||
assert "/" in permissions.pages
|
||||
assert "/profile" in permissions.pages
|
||||
assert "/members" in permissions.pages
|
||||
assert "/members/:id" in permissions.pages
|
||||
assert "/custom_field_values" in permissions.pages
|
||||
assert "/custom_field_values/:id" in permissions.pages
|
||||
end
|
||||
end
|
||||
|
||||
describe "get_permissions/1 - :normal_user permission content" do
|
||||
test "allows User read/update with scope :own" do
|
||||
permissions = PermissionSets.get_permissions(:normal_user)
|
||||
|
||||
user_read =
|
||||
Enum.find(permissions.resources, fn p -> p.resource == "User" && p.action == :read end)
|
||||
|
||||
user_update =
|
||||
Enum.find(permissions.resources, fn p -> p.resource == "User" && p.action == :update end)
|
||||
|
||||
assert user_read.scope == :own
|
||||
assert user_read.granted == true
|
||||
assert user_update.scope == :own
|
||||
assert user_update.granted == true
|
||||
end
|
||||
|
||||
test "allows Member read/create/update with scope :all" do
|
||||
permissions = PermissionSets.get_permissions(:normal_user)
|
||||
|
||||
member_read =
|
||||
Enum.find(permissions.resources, fn p -> p.resource == "Member" && p.action == :read end)
|
||||
|
||||
member_create =
|
||||
Enum.find(permissions.resources, fn p -> p.resource == "Member" && p.action == :create end)
|
||||
|
||||
member_update =
|
||||
Enum.find(permissions.resources, fn p -> p.resource == "Member" && p.action == :update end)
|
||||
|
||||
assert member_read.scope == :all
|
||||
assert member_read.granted == true
|
||||
assert member_create.scope == :all
|
||||
assert member_create.granted == true
|
||||
assert member_update.scope == :all
|
||||
assert member_update.granted == true
|
||||
end
|
||||
|
||||
test "does NOT allow Member destroy (safety)" do
|
||||
permissions = PermissionSets.get_permissions(:normal_user)
|
||||
|
||||
member_destroy =
|
||||
Enum.find(permissions.resources, fn p ->
|
||||
p.resource == "Member" && p.action == :destroy
|
||||
end)
|
||||
|
||||
assert member_destroy == nil || member_destroy.granted == false
|
||||
end
|
||||
|
||||
test "allows CustomFieldValue full CRUD with scope :all" do
|
||||
permissions = PermissionSets.get_permissions(:normal_user)
|
||||
|
||||
cfv_read =
|
||||
Enum.find(permissions.resources, fn p ->
|
||||
p.resource == "CustomFieldValue" && p.action == :read
|
||||
end)
|
||||
|
||||
cfv_create =
|
||||
Enum.find(permissions.resources, fn p ->
|
||||
p.resource == "CustomFieldValue" && p.action == :create
|
||||
end)
|
||||
|
||||
cfv_update =
|
||||
Enum.find(permissions.resources, fn p ->
|
||||
p.resource == "CustomFieldValue" && p.action == :update
|
||||
end)
|
||||
|
||||
cfv_destroy =
|
||||
Enum.find(permissions.resources, fn p ->
|
||||
p.resource == "CustomFieldValue" && p.action == :destroy
|
||||
end)
|
||||
|
||||
assert cfv_read.scope == :all
|
||||
assert cfv_read.granted == true
|
||||
assert cfv_create.scope == :all
|
||||
assert cfv_create.granted == true
|
||||
assert cfv_update.scope == :all
|
||||
assert cfv_update.granted == true
|
||||
assert cfv_destroy.scope == :all
|
||||
assert cfv_destroy.granted == true
|
||||
end
|
||||
|
||||
test "allows CustomField read with scope :all" do
|
||||
permissions = PermissionSets.get_permissions(:normal_user)
|
||||
|
||||
cf_read =
|
||||
Enum.find(permissions.resources, fn p ->
|
||||
p.resource == "CustomField" && p.action == :read
|
||||
end)
|
||||
|
||||
assert cf_read.scope == :all
|
||||
assert cf_read.granted == true
|
||||
end
|
||||
|
||||
test "includes correct pages" do
|
||||
permissions = PermissionSets.get_permissions(:normal_user)
|
||||
|
||||
assert "/" in permissions.pages
|
||||
assert "/profile" in permissions.pages
|
||||
assert "/members" in permissions.pages
|
||||
assert "/members/new" in permissions.pages
|
||||
assert "/members/:id" in permissions.pages
|
||||
assert "/members/:id/edit" in permissions.pages
|
||||
assert "/custom_field_values" in permissions.pages
|
||||
assert "/custom_field_values/:id" in permissions.pages
|
||||
assert "/custom_field_values/new" in permissions.pages
|
||||
assert "/custom_field_values/:id/edit" in permissions.pages
|
||||
end
|
||||
end
|
||||
|
||||
describe "get_permissions/1 - :admin permission content" do
|
||||
test "allows User full CRUD with scope :all" do
|
||||
permissions = PermissionSets.get_permissions(:admin)
|
||||
|
||||
user_read =
|
||||
Enum.find(permissions.resources, fn p -> p.resource == "User" && p.action == :read end)
|
||||
|
||||
user_create =
|
||||
Enum.find(permissions.resources, fn p -> p.resource == "User" && p.action == :create end)
|
||||
|
||||
user_update =
|
||||
Enum.find(permissions.resources, fn p -> p.resource == "User" && p.action == :update end)
|
||||
|
||||
user_destroy =
|
||||
Enum.find(permissions.resources, fn p -> p.resource == "User" && p.action == :destroy end)
|
||||
|
||||
assert user_read.scope == :all
|
||||
assert user_read.granted == true
|
||||
assert user_create.scope == :all
|
||||
assert user_create.granted == true
|
||||
assert user_update.scope == :all
|
||||
assert user_update.granted == true
|
||||
assert user_destroy.scope == :all
|
||||
assert user_destroy.granted == true
|
||||
end
|
||||
|
||||
test "allows Member full CRUD with scope :all" do
|
||||
permissions = PermissionSets.get_permissions(:admin)
|
||||
|
||||
member_read =
|
||||
Enum.find(permissions.resources, fn p -> p.resource == "Member" && p.action == :read end)
|
||||
|
||||
member_create =
|
||||
Enum.find(permissions.resources, fn p -> p.resource == "Member" && p.action == :create end)
|
||||
|
||||
member_update =
|
||||
Enum.find(permissions.resources, fn p -> p.resource == "Member" && p.action == :update end)
|
||||
|
||||
member_destroy =
|
||||
Enum.find(permissions.resources, fn p ->
|
||||
p.resource == "Member" && p.action == :destroy
|
||||
end)
|
||||
|
||||
assert member_read.scope == :all
|
||||
assert member_read.granted == true
|
||||
assert member_create.scope == :all
|
||||
assert member_create.granted == true
|
||||
assert member_update.scope == :all
|
||||
assert member_update.granted == true
|
||||
assert member_destroy.scope == :all
|
||||
assert member_destroy.granted == true
|
||||
end
|
||||
|
||||
test "allows CustomFieldValue full CRUD with scope :all" do
|
||||
permissions = PermissionSets.get_permissions(:admin)
|
||||
|
||||
cfv_read =
|
||||
Enum.find(permissions.resources, fn p ->
|
||||
p.resource == "CustomFieldValue" && p.action == :read
|
||||
end)
|
||||
|
||||
cfv_create =
|
||||
Enum.find(permissions.resources, fn p ->
|
||||
p.resource == "CustomFieldValue" && p.action == :create
|
||||
end)
|
||||
|
||||
cfv_update =
|
||||
Enum.find(permissions.resources, fn p ->
|
||||
p.resource == "CustomFieldValue" && p.action == :update
|
||||
end)
|
||||
|
||||
cfv_destroy =
|
||||
Enum.find(permissions.resources, fn p ->
|
||||
p.resource == "CustomFieldValue" && p.action == :destroy
|
||||
end)
|
||||
|
||||
assert cfv_read.scope == :all
|
||||
assert cfv_read.granted == true
|
||||
assert cfv_create.scope == :all
|
||||
assert cfv_create.granted == true
|
||||
assert cfv_update.scope == :all
|
||||
assert cfv_update.granted == true
|
||||
assert cfv_destroy.scope == :all
|
||||
assert cfv_destroy.granted == true
|
||||
end
|
||||
|
||||
test "allows CustomField full CRUD with scope :all" do
|
||||
permissions = PermissionSets.get_permissions(:admin)
|
||||
|
||||
cf_read =
|
||||
Enum.find(permissions.resources, fn p ->
|
||||
p.resource == "CustomField" && p.action == :read
|
||||
end)
|
||||
|
||||
cf_create =
|
||||
Enum.find(permissions.resources, fn p ->
|
||||
p.resource == "CustomField" && p.action == :create
|
||||
end)
|
||||
|
||||
cf_update =
|
||||
Enum.find(permissions.resources, fn p ->
|
||||
p.resource == "CustomField" && p.action == :update
|
||||
end)
|
||||
|
||||
cf_destroy =
|
||||
Enum.find(permissions.resources, fn p ->
|
||||
p.resource == "CustomField" && p.action == :destroy
|
||||
end)
|
||||
|
||||
assert cf_read.scope == :all
|
||||
assert cf_read.granted == true
|
||||
assert cf_create.scope == :all
|
||||
assert cf_create.granted == true
|
||||
assert cf_update.scope == :all
|
||||
assert cf_update.granted == true
|
||||
assert cf_destroy.scope == :all
|
||||
assert cf_destroy.granted == true
|
||||
end
|
||||
|
||||
test "allows Role full CRUD with scope :all" do
|
||||
permissions = PermissionSets.get_permissions(:admin)
|
||||
|
||||
role_read =
|
||||
Enum.find(permissions.resources, fn p -> p.resource == "Role" && p.action == :read end)
|
||||
|
||||
role_create =
|
||||
Enum.find(permissions.resources, fn p -> p.resource == "Role" && p.action == :create end)
|
||||
|
||||
role_update =
|
||||
Enum.find(permissions.resources, fn p -> p.resource == "Role" && p.action == :update end)
|
||||
|
||||
role_destroy =
|
||||
Enum.find(permissions.resources, fn p -> p.resource == "Role" && p.action == :destroy end)
|
||||
|
||||
assert role_read.scope == :all
|
||||
assert role_read.granted == true
|
||||
assert role_create.scope == :all
|
||||
assert role_create.granted == true
|
||||
assert role_update.scope == :all
|
||||
assert role_update.granted == true
|
||||
assert role_destroy.scope == :all
|
||||
assert role_destroy.granted == true
|
||||
end
|
||||
|
||||
test "has wildcard page permission" do
|
||||
permissions = PermissionSets.get_permissions(:admin)
|
||||
|
||||
assert "*" in permissions.pages
|
||||
end
|
||||
end
|
||||
|
||||
describe "valid_permission_set?/1" do
|
||||
test "returns true for valid permission set string" do
|
||||
assert PermissionSets.valid_permission_set?("own_data") == true
|
||||
assert PermissionSets.valid_permission_set?("read_only") == true
|
||||
assert PermissionSets.valid_permission_set?("normal_user") == true
|
||||
assert PermissionSets.valid_permission_set?("admin") == true
|
||||
end
|
||||
|
||||
test "returns true for valid permission set atom" do
|
||||
assert PermissionSets.valid_permission_set?(:own_data) == true
|
||||
assert PermissionSets.valid_permission_set?(:read_only) == true
|
||||
assert PermissionSets.valid_permission_set?(:normal_user) == true
|
||||
assert PermissionSets.valid_permission_set?(:admin) == true
|
||||
end
|
||||
|
||||
test "returns false for invalid permission set string" do
|
||||
assert PermissionSets.valid_permission_set?("invalid") == false
|
||||
assert PermissionSets.valid_permission_set?("") == false
|
||||
assert PermissionSets.valid_permission_set?("admin_user") == false
|
||||
end
|
||||
|
||||
test "returns false for invalid permission set atom" do
|
||||
assert PermissionSets.valid_permission_set?(:invalid) == false
|
||||
assert PermissionSets.valid_permission_set?(:unknown) == false
|
||||
end
|
||||
|
||||
test "returns false for nil input" do
|
||||
assert PermissionSets.valid_permission_set?(nil) == false
|
||||
end
|
||||
|
||||
test "returns false for invalid types" do
|
||||
assert PermissionSets.valid_permission_set?(123) == false
|
||||
assert PermissionSets.valid_permission_set?([]) == false
|
||||
assert PermissionSets.valid_permission_set?(%{}) == false
|
||||
assert PermissionSets.valid_permission_set?("") == false
|
||||
end
|
||||
end
|
||||
|
||||
describe "permission_set_name_to_atom/1" do
|
||||
test "returns {:ok, atom} for valid permission set name" do
|
||||
assert PermissionSets.permission_set_name_to_atom("own_data") == {:ok, :own_data}
|
||||
assert PermissionSets.permission_set_name_to_atom("read_only") == {:ok, :read_only}
|
||||
assert PermissionSets.permission_set_name_to_atom("normal_user") == {:ok, :normal_user}
|
||||
assert PermissionSets.permission_set_name_to_atom("admin") == {:ok, :admin}
|
||||
end
|
||||
|
||||
test "returns {:error, :invalid_permission_set} for invalid permission set name" do
|
||||
assert PermissionSets.permission_set_name_to_atom("invalid") ==
|
||||
{:error, :invalid_permission_set}
|
||||
|
||||
assert PermissionSets.permission_set_name_to_atom("") == {:error, :invalid_permission_set}
|
||||
|
||||
assert PermissionSets.permission_set_name_to_atom("admin_user") ==
|
||||
{:error, :invalid_permission_set}
|
||||
end
|
||||
|
||||
test "handles non-existent atom gracefully" do
|
||||
# String.to_existing_atom will raise ArgumentError for non-existent atoms
|
||||
assert PermissionSets.permission_set_name_to_atom("nonexistent_atom_12345") ==
|
||||
{:error, :invalid_permission_set}
|
||||
end
|
||||
end
|
||||
|
||||
describe "get_permissions/1 - error handling" do
|
||||
test "raises ArgumentError for invalid permission set with helpful message" do
|
||||
assert_raise ArgumentError,
|
||||
~r/invalid permission set: :invalid\. Must be one of:/,
|
||||
fn ->
|
||||
PermissionSets.get_permissions(:invalid)
|
||||
end
|
||||
end
|
||||
|
||||
test "error message includes all valid permission sets" do
|
||||
error =
|
||||
assert_raise ArgumentError, fn ->
|
||||
PermissionSets.get_permissions(:unknown)
|
||||
end
|
||||
|
||||
error_message = Exception.message(error)
|
||||
assert error_message =~ "own_data"
|
||||
assert error_message =~ "read_only"
|
||||
assert error_message =~ "normal_user"
|
||||
assert error_message =~ "admin"
|
||||
end
|
||||
end
|
||||
end
|
||||
97
test/mv/authorization/role_test.exs
Normal file
97
test/mv/authorization/role_test.exs
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
defmodule Mv.Authorization.RoleTest do
|
||||
@moduledoc """
|
||||
Unit tests for Role resource validations and constraints.
|
||||
"""
|
||||
use Mv.DataCase, async: true
|
||||
|
||||
alias Mv.Authorization
|
||||
|
||||
describe "permission_set_name validation" do
|
||||
test "accepts valid permission set names" do
|
||||
attrs = %{
|
||||
name: "Test Role",
|
||||
permission_set_name: "own_data"
|
||||
}
|
||||
|
||||
assert {:ok, role} = Authorization.create_role(attrs)
|
||||
assert role.permission_set_name == "own_data"
|
||||
end
|
||||
|
||||
test "rejects invalid permission set names" do
|
||||
attrs = %{
|
||||
name: "Test Role",
|
||||
permission_set_name: "invalid_set"
|
||||
}
|
||||
|
||||
assert {:error, %Ash.Error.Invalid{errors: errors}} = Authorization.create_role(attrs)
|
||||
assert error_message(errors, :permission_set_name) =~ "must be one of"
|
||||
end
|
||||
|
||||
test "accepts all four valid permission sets" do
|
||||
valid_sets = ["own_data", "read_only", "normal_user", "admin"]
|
||||
|
||||
for permission_set <- valid_sets do
|
||||
attrs = %{
|
||||
name: "Role #{permission_set}",
|
||||
permission_set_name: permission_set
|
||||
}
|
||||
|
||||
assert {:ok, _role} = Authorization.create_role(attrs)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "system role deletion protection" do
|
||||
test "prevents deletion of system roles" do
|
||||
# is_system_role is not settable via public API, so we use Ash.Changeset directly
|
||||
changeset =
|
||||
Mv.Authorization.Role
|
||||
|> Ash.Changeset.for_create(:create_role, %{
|
||||
name: "System Role",
|
||||
permission_set_name: "own_data"
|
||||
})
|
||||
|> Ash.Changeset.force_change_attribute(:is_system_role, true)
|
||||
|
||||
{:ok, system_role} = Ash.create(changeset)
|
||||
|
||||
assert {:error, %Ash.Error.Invalid{errors: errors}} =
|
||||
Authorization.destroy_role(system_role)
|
||||
|
||||
message = error_message(errors, :is_system_role)
|
||||
assert message =~ "Cannot delete system role"
|
||||
end
|
||||
|
||||
test "allows deletion of non-system roles" do
|
||||
# is_system_role defaults to false, so regular create works
|
||||
{:ok, regular_role} =
|
||||
Authorization.create_role(%{
|
||||
name: "Regular Role",
|
||||
permission_set_name: "read_only"
|
||||
})
|
||||
|
||||
assert :ok = Authorization.destroy_role(regular_role)
|
||||
end
|
||||
end
|
||||
|
||||
describe "name uniqueness" do
|
||||
test "enforces unique role names" do
|
||||
attrs = %{
|
||||
name: "Unique Role",
|
||||
permission_set_name: "own_data"
|
||||
}
|
||||
|
||||
assert {:ok, _} = Authorization.create_role(attrs)
|
||||
|
||||
assert {:error, %Ash.Error.Invalid{errors: errors}} = Authorization.create_role(attrs)
|
||||
assert error_message(errors, :name) =~ "has already been taken"
|
||||
end
|
||||
end
|
||||
|
||||
# Helper function for error evaluation
|
||||
defp error_message(errors, field) when is_atom(field) do
|
||||
errors
|
||||
|> Enum.filter(fn err -> Map.get(err, :field) == field end)
|
||||
|> Enum.map(&Map.get(&1, :message, ""))
|
||||
|> List.first() || ""
|
||||
end
|
||||
end
|
||||
Loading…
Add table
Add a link
Reference in a new issue