defmodule TarakanWeb.AdminLiveTest do use TarakanWeb.ConnCase, async: true import Phoenix.LiveViewTest alias Tarakan.Accounts alias Tarakan.Accounts.Account alias Tarakan.Repo setup %{conn: conn} do admin = account_fixture() |> Account.authorization_changeset(%{ state: "active", platform_role: "admin", trust_tier: "reviewer" }) |> Repo.update!() %{admin: admin, conn: log_in_account(conn, admin)} end test "admin sees account summary, search, and management links", %{conn: conn} do target = account_fixture(%{handle: "managed-account"}) {:ok, view, _html} = live(conn, ~p"/admin") assert has_element?(view, "#admin-dashboard") assert has_element?(view, "#admin-summary-admins") assert has_element?(view, "#admin-account-filter") assert has_element?(view, "#admin-account-#{target.id}-manage") view |> form("#admin-account-filter", filters: %{query: "managed-account"}) |> render_change() assert has_element?(view, "#admin-account-#{target.id}-manage") end test "admin updates account standing, role, and trust tier", %{conn: conn} do target = account_fixture() {:ok, view, _html} = live(conn, ~p"/admin/accounts/#{target.id}") assert has_element?(view, "#admin-account") assert has_element?(view, "#admin-authorization-form") view |> form("#admin-authorization-form", authorization: %{ state: "active", platform_role: "moderator", trust_tier: "reviewer" } ) |> render_submit() updated = Accounts.get_account!(target.id) assert updated.state == "active" assert updated.platform_role == "moderator" assert updated.trust_tier == "reviewer" assert has_element?(view, "#admin-authorization-form") end test "ordinary members cannot open platform administration", %{conn: _admin_conn} do member = account_fixture() |> Account.authorization_changeset(%{state: "active"}) |> Repo.update!() conn = log_in_account(build_conn(), member) assert {:error, {:redirect, %{to: path, flash: flash}}} = live(conn, ~p"/admin") assert path == ~p"/" assert flash["error"] == "Administrator access is required." end test "last administrator cannot remove their own authority", %{conn: conn, admin: admin} do {:ok, view, _html} = live(conn, ~p"/admin/accounts/#{admin.id}") view |> form("#admin-authorization-form", authorization: %{ state: "active", platform_role: "member", trust_tier: "reviewer" } ) |> render_submit() assert Accounts.get_account!(admin.id).platform_role == "admin" assert has_element?(view, "#admin-authorization-form") end test "filtering requires a still-valid sudo session", %{conn: conn} do {:ok, view, _html} = live(conn, ~p"/admin") expire_sudo_mode(view) assert {:error, {:live_redirect, %{to: "/accounts/log-in?return_to=%2Fadmin"}}} = view |> form("#admin-account-filter", filters: %{query: "anything"}) |> render_change() end test "saving requires a still-valid sudo session", %{conn: conn} do target = account_fixture() {:ok, view, _html} = live(conn, ~p"/admin/accounts/#{target.id}") expire_sudo_mode(view) assert {:error, {:live_redirect, %{to: "/accounts/log-in?return_to=%2Fadmin%2Faccounts%2F" <> _}}} = view |> form("#admin-authorization-form", authorization: %{ state: "active", platform_role: "moderator", trust_tier: "reviewer" } ) |> render_submit() assert Accounts.get_account!(target.id).platform_role == target.platform_role end # Simulates the two-hour sudo window expiring while the LiveView stays open. defp expire_sudo_mode(view) do stale = DateTime.add(DateTime.utc_now(), -3, :hour) :sys.replace_state(view.pid, fn state -> account = %{state.socket.assigns.current_scope.account | authenticated_at: stale} put_in(state.socket.assigns.current_scope.account, account) end) end end