mitgliederverwaltung/test/mv/authorization/actor_test.exs
Moritz ef4df57a6f
Restrict Actor.ensure_loaded to Mv.Accounts.User only
Pattern match on %Mv.Accounts.User{} instead of generic actor.
Clearer intention, prevents accidental authorization bypasses.
Non-User actors are returned as-is (no-op).
2026-01-27 10:23:13 +01:00

84 lines
2.7 KiB
Elixir

defmodule Mv.Authorization.ActorTest do
@moduledoc """
Tests for the Actor helper module.
"""
use Mv.DataCase, async: false
alias Mv.Accounts
alias Mv.Authorization.Actor
describe "ensure_loaded/1" do
test "returns nil when actor is nil" do
assert Actor.ensure_loaded(nil) == nil
end
test "returns actor as-is when role is already loaded" do
# Create user with role
{:ok, user} =
Accounts.User
|> Ash.Changeset.for_create(:register_with_password, %{
email: "test#{System.unique_integer([:positive])}@example.com",
password: "testpassword123"
})
|> Ash.create()
# Load role
{:ok, user_with_role} = Ash.load(user, :role, domain: Mv.Accounts)
# Should return as-is (no additional load)
result = Actor.ensure_loaded(user_with_role)
assert result.id == user.id
assert result.role != %Ash.NotLoaded{}
end
test "loads role when it's NotLoaded" do
# Create a role first
{:ok, role} =
Mv.Authorization.Role
|> Ash.Changeset.for_create(:create_role, %{
name: "Test Role #{System.unique_integer([:positive])}",
description: "Test role",
permission_set_name: "own_data"
})
|> Ash.create()
# Create user with role
{:ok, user} =
Accounts.User
|> Ash.Changeset.for_create(:register_with_password, %{
email: "test#{System.unique_integer([:positive])}@example.com",
password: "testpassword123"
})
|> Ash.create()
# Assign role to user
{:ok, user_with_role} =
user
|> Ash.Changeset.for_update(:update, %{})
|> Ash.Changeset.manage_relationship(:role, role, type: :append_and_remove)
|> Ash.update()
# Fetch user again WITHOUT loading role (simulates "role not preloaded" scenario)
{:ok, user_without_role_loaded} =
Ash.get(Accounts.User, user_with_role.id, domain: Mv.Accounts)
# User has role as NotLoaded (relationship not preloaded)
assert match?(%Ash.NotLoaded{}, user_without_role_loaded.role)
# ensure_loaded should load it
result = Actor.ensure_loaded(user_without_role_loaded)
assert result.id == user.id
refute match?(%Ash.NotLoaded{}, result.role)
assert result.role.id == role.id
end
test "returns non-User actors as-is (no-op)" do
# Create a plain map (not Mv.Accounts.User)
other_actor = %{id: "fake", role: %Ash.NotLoaded{field: :role}}
# Should return as-is (pattern match doesn't apply to non-User)
result = Actor.ensure_loaded(other_actor)
assert result == other_actor
end
end
end