defmodule MvWeb.RoleLive.ShowTest do @moduledoc """ Tests for the role show page. Tests cover: - Displaying role information - System role badge display - User count display - Navigation - Error handling - Delete functionality """ use MvWeb.ConnCase, async: false import Phoenix.LiveViewTest require Ash.Query use Gettext, backend: MvWeb.Gettext alias Mv.Authorization alias Mv.Authorization.Role # Helper to create a role defp create_role(attrs \\ %{}) do default_attrs = %{ name: "Test Role #{System.unique_integer([:positive])}", description: "Test description", permission_set_name: "read_only" } attrs = Map.merge(default_attrs, attrs) case Authorization.create_role(attrs) do {:ok, role} -> role {:error, error} -> raise "Failed to create role: #{inspect(error)}" end end # Helper to create admin user with admin role defp create_admin_user(conn, actor) do # Create admin role admin_role = case Authorization.list_roles() do {:ok, roles} -> case Enum.find(roles, &(&1.name == "Admin")) do nil -> # Create admin role if it doesn't exist create_role(%{ name: "Admin", description: "Administrator with full access", permission_set_name: "admin" }) role -> role end _ -> # Create admin role if list_roles fails create_role(%{ name: "Admin", description: "Administrator with full access", permission_set_name: "admin" }) end # Create user {:ok, user} = Mv.Accounts.User |> Ash.Changeset.for_create(:register_with_password, %{ email: "admin#{System.unique_integer([:positive])}@mv.local", password: "testpassword123" }) |> Ash.create(actor: actor) # Assign admin role using manage_relationship {:ok, user} = user |> Ash.Changeset.for_update(:update, %{}) |> Ash.Changeset.manage_relationship(:role, admin_role, type: :append_and_remove) |> Ash.update(actor: actor) # Load role for authorization checks (must be loaded for can?/3 to work) user_with_role = Ash.load!(user, :role, domain: Mv.Accounts, actor: actor) # Store user with role in session for LiveView conn = conn_with_password_user(conn, user_with_role) {conn, user_with_role, admin_role} end describe "mount and display" do setup %{conn: conn} do system_actor = Mv.Helpers.SystemActor.get_system_actor() {conn, _user, _admin_role} = create_admin_user(conn, system_actor) %{conn: conn, actor: system_actor} end @tag :slow test "mounts successfully with valid role ID", %{conn: conn} do role = create_role() {:ok, _view, html} = live(conn, "/admin/roles/#{role.id}") assert html =~ role.name end @tag :slow test "displays role name", %{conn: conn} do role = create_role(%{name: "Test Role Name"}) {:ok, _view, html} = live(conn, "/admin/roles/#{role.id}") assert html =~ "Test Role Name" assert html =~ gettext("Name") end test "displays role description when present", %{conn: conn} do role = create_role(%{description: "This is a test description"}) {:ok, _view, html} = live(conn, "/admin/roles/#{role.id}") assert html =~ "This is a test description" assert html =~ gettext("Description") end test "displays 'No description' when description is missing", %{conn: conn} do role = create_role(%{description: nil}) {:ok, _view, html} = live(conn, "/admin/roles/#{role.id}") assert html =~ gettext("No description") end @tag :slow test "displays permission set name", %{conn: conn} do role = create_role(%{permission_set_name: "read_only"}) {:ok, _view, html} = live(conn, "/admin/roles/#{role.id}") assert html =~ "read_only" assert html =~ gettext("Permission Set") end test "displays system role badge when is_system_role is true", %{conn: conn, actor: actor} do system_role = Role |> Ash.Changeset.for_create(:create_role, %{ name: "System Role", permission_set_name: "own_data" }) |> Ash.Changeset.force_change_attribute(:is_system_role, true) |> Ash.create!(actor: actor) {:ok, _view, html} = live(conn, "/admin/roles/#{system_role.id}") assert html =~ gettext("System Role") assert html =~ gettext("Yes") end @tag :slow test "displays non-system role badge when is_system_role is false", %{conn: conn} do role = create_role() {:ok, _view, html} = live(conn, "/admin/roles/#{role.id}") assert html =~ gettext("System Role") assert html =~ gettext("No") end test "displays user count", %{conn: conn} do role = create_role() {:ok, _view, html} = live(conn, "/admin/roles/#{role.id}") # User count should be displayed (might be 0 or more) assert html =~ gettext("User") || html =~ "0" || html =~ "users" end end describe "navigation" do setup %{conn: conn} do system_actor = Mv.Helpers.SystemActor.get_system_actor() {conn, _user, _admin_role} = create_admin_user(conn, system_actor) %{conn: conn, actor: system_actor} end @tag :slow test "back button navigates to role list", %{conn: conn} do role = create_role() {:ok, view, _html} = live(conn, "/admin/roles/#{role.id}") assert {:error, {:live_redirect, %{to: to}}} = view |> element( "a[aria-label='#{gettext("Back to roles list")}'], button[aria-label='#{gettext("Back to roles list")}']" ) |> render_click() assert to == "/admin/roles" end test "edit button navigates to edit form", %{conn: conn} do role = create_role() {:ok, view, _html} = live(conn, "/admin/roles/#{role.id}") assert {:error, {:live_redirect, %{to: to}}} = view |> element( "a[href='/admin/roles/#{role.id}/edit'], button[href='/admin/roles/#{role.id}/edit']" ) |> render_click() assert to == "/admin/roles/#{role.id}/edit" end end describe "error handling" do setup %{conn: conn} do system_actor = Mv.Helpers.SystemActor.get_system_actor() {conn, _user, _admin_role} = create_admin_user(conn, system_actor) %{conn: conn, actor: system_actor} end test "redirects to role list with error for invalid role ID", %{conn: conn} do invalid_id = Ecto.UUID.generate() # Should redirect to index with error message result = live(conn, "/admin/roles/#{invalid_id}") assert match?({:error, {:redirect, %{to: "/admin/roles"}}}, result) or match?({:error, {:live_redirect, %{to: "/admin/roles"}}}, result) end end describe "delete functionality" do setup %{conn: conn} do system_actor = Mv.Helpers.SystemActor.get_system_actor() {conn, _user, _admin_role} = create_admin_user(conn, system_actor) %{conn: conn, actor: system_actor} end test "delete button is not shown for system roles", %{conn: conn, actor: actor} do system_role = Role |> Ash.Changeset.for_create(:create_role, %{ name: "System Role", permission_set_name: "own_data" }) |> Ash.Changeset.force_change_attribute(:is_system_role, true) |> Ash.create!(actor: actor) {:ok, _view, html} = live(conn, "/admin/roles/#{system_role.id}") # Delete button should not be visible for system roles refute html =~ ~r/Delete.*Role.*#{system_role.id}/i end test "delete button is shown for non-system roles", %{conn: conn} do role = create_role() {:ok, _view, html} = live(conn, "/admin/roles/#{role.id}") # Delete button should be visible for non-system roles assert html =~ gettext("Delete Role") || html =~ "delete" end end describe "page title" do setup %{conn: conn} do system_actor = Mv.Helpers.SystemActor.get_system_actor() {conn, _user, _admin_role} = create_admin_user(conn, system_actor) %{conn: conn, actor: system_actor} end test "sets correct page title", %{conn: conn} do role = create_role() {:ok, _view, html} = live(conn, "/admin/roles/#{role.id}") # Check that page title is set (might be in title tag or header) assert html =~ gettext("Show Role") || html =~ role.name end end end