defmodule Mv.DataCase do @moduledoc """ This module defines the setup for tests requiring access to the application's data layer. You may define functions here to be used as helpers in your tests. Finally, if the test case interacts with the database, we enable the SQL sandbox, so changes done to the database are reverted at the end of every test. If you are using PostgreSQL, you can even run database tests asynchronously by setting `use Mv.DataCase, async: true`, although this option is not recommended for other databases. """ use ExUnit.CaseTemplate require Ash.Query using do quote do alias Mv.Repo import Ecto import Ecto.Changeset import Ecto.Query import Mv.DataCase end end setup tags do Mv.DataCase.setup_sandbox(tags) # Ensure "Mitglied" role exists for AssignDefaultRole to work in tests Mv.DataCase.ensure_default_role() :ok end @doc """ Sets up the sandbox based on the test tags. Returns the owner pid for use with Phoenix.Ecto.SQL.Sandbox. """ def setup_sandbox(tags) do pid = Ecto.Adapters.SQL.Sandbox.start_owner!(Mv.Repo, shared: not tags[:async]) on_exit(fn -> Ecto.Adapters.SQL.Sandbox.stop_owner(pid) end) pid end @doc """ Ensures the default "Mitglied" role exists in the test database. This is necessary because AssignDefaultRole expects this role to exist. Tests run in sandbox mode, so the role needs to be created for each test. """ def ensure_default_role do # Check if "Mitglied" role already exists case Mv.Authorization.Role.get_mitglied_role() do {:ok, nil} -> # Create the role if it doesn't exist Mv.Authorization.Role |> Ash.Changeset.for_create(:create_role_with_system_flag, %{ name: "Mitglied", description: "Default member role with access to own data only", permission_set_name: "own_data", is_system_role: true }) |> Ash.create!(authorize?: false, domain: Mv.Authorization) {:ok, _role} -> # Role already exists, do nothing :ok {:error, _error} -> # Ignore errors (e.g., in tests that don't need roles) :ok end end @doc """ A helper that transforms changeset errors into a map of messages. assert {:error, changeset} = Accounts.create_user(%{password: "short"}) assert "password is too short" in errors_on(changeset).password assert %{password: ["password is too short"]} = errors_on(changeset) """ def errors_on(changeset) do Ecto.Changeset.traverse_errors(changeset, fn {message, opts} -> Regex.replace(~r"%{(\w+)}", message, fn _, key -> opts |> Keyword.get(String.to_existing_atom(key), key) |> to_string() end) end) end end