defmodule Mv.Membership.CustomFieldPoliciesTest do @moduledoc """ Tests for CustomField resource authorization policies. Verifies that all authenticated users with a valid role can read custom fields, and only admin can create/update/destroy custom fields. """ use Mv.DataCase, async: false alias Mv.Membership.CustomField setup do system_actor = Mv.Helpers.SystemActor.get_system_actor() %{actor: system_actor} end defp create_custom_field do admin = Mv.Fixtures.user_with_role_fixture("admin") {:ok, field} = CustomField |> Ash.Changeset.for_create(:create, %{ name: "test_field_#{System.unique_integer([:positive])}", value_type: :string }) |> Ash.create(actor: admin, domain: Mv.Membership) field end describe "read access (all roles)" do test "user with own_data can read all custom fields", %{actor: _actor} do custom_field = create_custom_field() user = Mv.Fixtures.user_with_role_fixture("own_data") {:ok, fields} = Ash.read(CustomField, actor: user, domain: Mv.Membership) ids = Enum.map(fields, & &1.id) assert custom_field.id in ids {:ok, fetched} = Ash.get(CustomField, custom_field.id, actor: user, domain: Mv.Membership) assert fetched.id == custom_field.id end test "user with read_only can read all custom fields", %{actor: _actor} do custom_field = create_custom_field() user = Mv.Fixtures.user_with_role_fixture("read_only") {:ok, fields} = Ash.read(CustomField, actor: user, domain: Mv.Membership) ids = Enum.map(fields, & &1.id) assert custom_field.id in ids {:ok, fetched} = Ash.get(CustomField, custom_field.id, actor: user, domain: Mv.Membership) assert fetched.id == custom_field.id end test "user with normal_user can read all custom fields", %{actor: _actor} do custom_field = create_custom_field() user = Mv.Fixtures.user_with_role_fixture("normal_user") {:ok, fields} = Ash.read(CustomField, actor: user, domain: Mv.Membership) ids = Enum.map(fields, & &1.id) assert custom_field.id in ids {:ok, fetched} = Ash.get(CustomField, custom_field.id, actor: user, domain: Mv.Membership) assert fetched.id == custom_field.id end test "user with admin can read all custom fields", %{actor: _actor} do custom_field = create_custom_field() user = Mv.Fixtures.user_with_role_fixture("admin") {:ok, fields} = Ash.read(CustomField, actor: user, domain: Mv.Membership) ids = Enum.map(fields, & &1.id) assert custom_field.id in ids {:ok, fetched} = Ash.get(CustomField, custom_field.id, actor: user, domain: Mv.Membership) assert fetched.id == custom_field.id end end describe "write access - non-admin cannot create/update/destroy" do setup %{actor: _actor} do user = Mv.Fixtures.user_with_role_fixture("normal_user") custom_field = create_custom_field() %{user: user, custom_field: custom_field} end test "non-admin cannot create custom field (forbidden)", %{user: user} do assert {:error, %Ash.Error.Forbidden{}} = CustomField |> Ash.Changeset.for_create(:create, %{ name: "forbidden_field_#{System.unique_integer([:positive])}", value_type: :string }) |> Ash.create(actor: user, domain: Mv.Membership) end test "non-admin cannot update custom field (forbidden)", %{ user: user, custom_field: custom_field } do assert {:error, %Ash.Error.Forbidden{}} = custom_field |> Ash.Changeset.for_update(:update, %{description: "Updated"}) |> Ash.update(actor: user, domain: Mv.Membership) end test "non-admin cannot destroy custom field (forbidden)", %{ user: user, custom_field: custom_field } do assert {:error, %Ash.Error.Forbidden{}} = Ash.destroy(custom_field, actor: user, domain: Mv.Membership) end end describe "write access - admin can create/update/destroy" do setup %{actor: _actor} do user = Mv.Fixtures.user_with_role_fixture("admin") custom_field = create_custom_field() %{user: user, custom_field: custom_field} end test "admin can create custom field", %{user: user} do name = "admin_field_#{System.unique_integer([:positive])}" assert {:ok, %CustomField{} = field} = CustomField |> Ash.Changeset.for_create(:create, %{name: name, value_type: :string}) |> Ash.create(actor: user, domain: Mv.Membership) assert field.name == name end test "admin can update custom field", %{user: user, custom_field: custom_field} do assert {:ok, updated} = custom_field |> Ash.Changeset.for_update(:update, %{description: "Admin updated"}) |> Ash.update(actor: user, domain: Mv.Membership) assert updated.description == "Admin updated" end test "admin can destroy custom field", %{user: user, custom_field: custom_field} do assert :ok = Ash.destroy(custom_field, actor: user, domain: Mv.Membership) assert {:error, _} = Ash.get(CustomField, custom_field.id, domain: Mv.Membership, actor: user) end end end