defmodule ToweropsWeb.Admin.AuditLive.IndexTest do use ToweropsWeb.ConnCase, async: true import Phoenix.LiveViewTest alias Towerops.Admin setup do user = Towerops.AccountsFixtures.user_fixture(enable_totp: true) user = user |> Ecto.Changeset.change(%{is_superuser: true}) |> Towerops.Repo.update!() {:ok, organization} = Towerops.Organizations.create_organization(%{name: "Test Org"}, user.id) token = Towerops.Accounts.generate_user_session_token(user) conn = build_conn() |> Phoenix.ConnTest.init_test_session(%{}) |> Plug.Conn.put_session(:user_token, token) %{conn: conn, user: user, organization: organization} end describe "mount" do test "renders audit log page for superuser", %{conn: conn} do {:ok, _view, html} = live(conn, ~p"/admin/audit") assert html =~ "Audit Logs" end test "redirects non-superuser to /orgs" do regular_user = Towerops.AccountsFixtures.user_fixture() token = Towerops.Accounts.generate_user_session_token(regular_user) conn = build_conn() |> Phoenix.ConnTest.init_test_session(%{}) |> Plug.Conn.put_session(:user_token, token) assert {:error, {:redirect, %{to: "/orgs"}}} = live(conn, ~p"/admin/audit") end test "shows empty state when no audit logs exist", %{conn: conn} do {:ok, _view, html} = live(conn, ~p"/admin/audit") assert html =~ "No audit logs found" end test "displays audit logs when they exist", %{conn: conn, user: user} do {:ok, _log} = Admin.create_audit_log(%{ action: "impersonate_start", superuser_id: user.id }) {:ok, _view, html} = live(conn, ~p"/admin/audit") assert html =~ "Impersonate start" assert html =~ user.email end end describe "search" do setup %{user: user} do target_user = Towerops.AccountsFixtures.user_fixture() {:ok, _log1} = Admin.create_audit_log(%{ action: "impersonate_start", superuser_id: user.id, target_user_id: target_user.id }) {:ok, _log2} = Admin.create_audit_log(%{ action: "user_delete", superuser_id: user.id, metadata: %{email: "deleted@example.com"} }) %{target_user: target_user} end test "filters audit logs by action", %{conn: conn} do {:ok, view, _html} = live(conn, ~p"/admin/audit") html = view |> form("form[phx-submit=search]", %{"action" => "impersonate_start"}) |> render_submit() assert html =~ "Impersonate start" # The action dropdown always contains "User delete" as an option, # so check the table body specifically for absence of the log entry refute has_element?(view, "td span", "User delete") end test "filters audit logs by email", %{conn: conn, user: user} do {:ok, view, _html} = live(conn, ~p"/admin/audit") html = view |> form("form[phx-submit=search]", %{"email" => user.email}) |> render_submit() assert html =~ user.email end end describe "clear_filters" do test "resets all search filters", %{conn: conn, user: user} do {:ok, _log} = Admin.create_audit_log(%{ action: "impersonate_start", superuser_id: user.id }) {:ok, _log2} = Admin.create_audit_log(%{ action: "user_delete", superuser_id: user.id }) {:ok, view, _html} = live(conn, ~p"/admin/audit") # First, apply a filter view |> form("form[phx-submit=search]", %{"action" => "impersonate_start"}) |> render_submit() # Then clear filters html = render_click(view, "clear_filters") # Both logs should be visible again assert html =~ "Impersonate start" assert html =~ "User delete" end end describe "pagination" do test "navigates to next page", %{conn: conn, user: user} do # Create more than 50 audit logs (the per_page default) for _i <- 1..51 do {:ok, _log} = Admin.create_audit_log(%{ action: "device_created", superuser_id: user.id }) end {:ok, view, html} = live(conn, ~p"/admin/audit") assert html =~ "Page 1" html = render_click(view, "next_page") assert html =~ "Page 2" end test "navigates to previous page", %{conn: conn, user: user} do for _i <- 1..51 do {:ok, _log} = Admin.create_audit_log(%{ action: "device_created", superuser_id: user.id }) end {:ok, view, _html} = live(conn, ~p"/admin/audit") render_click(view, "next_page") html = render_click(view, "prev_page") assert html =~ "Page 1" end test "does not go below page 1", %{conn: conn, user: user} do # Need at least one log so the pagination UI renders {:ok, _log} = Admin.create_audit_log(%{ action: "device_created", superuser_id: user.id }) {:ok, view, _html} = live(conn, ~p"/admin/audit") html = render_click(view, "prev_page") assert html =~ "Page 1" end end end