towerops/test/towerops_web/live/admin/agent_live/index_test.exs
Graham McIntire c1fca6276a
Add real-time updates to admin agents page via PubSub
Broadcast agent lifecycle events (created/deleted) on a separate
"admin:agents" topic so the admin page updates dynamically when
agents are added or removed from any organization.
2026-02-10 11:20:51 -06:00

121 lines
4.1 KiB
Elixir

defmodule ToweropsWeb.Admin.AgentLive.IndexTest do
use ToweropsWeb.ConnCase
import Phoenix.LiveViewTest
alias Towerops.Agents
describe "admin agents page" do
setup [:register_and_log_in_superuser]
test "renders page with all agents across organizations", %{conn: conn, user: user} do
{:ok, org1} = Towerops.Organizations.create_organization(%{name: "Org Alpha"}, user.id)
{:ok, org2} =
Towerops.Organizations.create_organization(%{name: "Org Beta"}, user.id, bypass_limits: true)
{:ok, _agent1, _} = Agents.create_agent_token(org1.id, "Alpha Agent")
{:ok, _agent2, _} = Agents.create_agent_token(org2.id, "Beta Agent")
{:ok, _cloud, _} = Agents.create_cloud_poller("Cloud Poller 1")
{:ok, _view, html} = live(conn, ~p"/admin/agents")
assert html =~ "All Agents"
assert html =~ "Alpha Agent"
assert html =~ "Beta Agent"
assert html =~ "Cloud Poller 1"
assert html =~ "Org Alpha"
assert html =~ "Org Beta"
assert html =~ "Cloud Poller"
end
test "shows cloud badge for cloud pollers", %{conn: conn} do
{:ok, _cloud, _} = Agents.create_cloud_poller("My Cloud Poller")
{:ok, _view, html} = live(conn, ~p"/admin/agents")
assert html =~ "Cloud"
assert html =~ "My Cloud Poller"
end
test "shows debug badge for debug-enabled agents", %{conn: conn, user: user} do
{:ok, org} = Towerops.Organizations.create_organization(%{name: "Debug Org"}, user.id)
{:ok, agent, _} = Agents.create_agent_token(org.id, "Debug Agent")
{:ok, _} = Agents.update_agent_token(agent, %{allow_remote_debug: true})
{:ok, _view, html} = live(conn, ~p"/admin/agents")
assert html =~ "Debug"
end
test "shows status badges", %{conn: conn, user: user} do
{:ok, org} = Towerops.Organizations.create_organization(%{name: "Status Org"}, user.id)
{:ok, _agent, _} = Agents.create_agent_token(org.id, "Status Agent")
{:ok, _view, html} = live(conn, ~p"/admin/agents")
assert html =~ "Never connected"
end
test "shows version when available", %{conn: conn, user: user} do
{:ok, org} = Towerops.Organizations.create_organization(%{name: "Version Org"}, user.id)
{:ok, agent, _} = Agents.create_agent_token(org.id, "Versioned Agent")
Agents.update_agent_token_heartbeat(agent.id, "10.0.0.1", %{"version" => "1.2.3"})
{:ok, _view, html} = live(conn, ~p"/admin/agents")
assert html =~ "v1.2.3"
end
end
describe "real-time updates" do
setup [:register_and_log_in_superuser]
test "updates when a new agent is created", %{conn: conn, user: user} do
{:ok, view, _html} = live(conn, ~p"/admin/agents")
{:ok, org} = Towerops.Organizations.create_organization(%{name: "New Org"}, user.id)
{:ok, _agent, _} = Agents.create_agent_token(org.id, "Dynamic Agent")
html = render(view)
assert html =~ "Dynamic Agent"
assert html =~ "New Org"
end
test "updates when a new cloud poller is created", %{conn: conn} do
{:ok, view, _html} = live(conn, ~p"/admin/agents")
{:ok, _cloud, _} = Agents.create_cloud_poller("Dynamic Cloud Poller")
html = render(view)
assert html =~ "Dynamic Cloud Poller"
end
test "updates when an agent is deleted", %{conn: conn, user: user} do
{:ok, org} = Towerops.Organizations.create_organization(%{name: "Delete Org"}, user.id)
{:ok, agent, _} = Agents.create_agent_token(org.id, "Doomed Agent")
{:ok, view, html} = live(conn, ~p"/admin/agents")
assert html =~ "Doomed Agent"
{:ok, _} = Agents.delete_agent_token(agent.id)
html = render(view)
refute html =~ "Doomed Agent"
end
end
describe "non-superuser access" do
setup [:register_and_log_in_user]
test "redirects non-superusers", %{conn: conn} do
assert {:error, {:redirect, %{to: "/orgs"}}} = live(conn, ~p"/admin/agents")
end
end
defp register_and_log_in_superuser(%{conn: conn}) do
user = Towerops.AccountsFixtures.user_fixture()
user = Towerops.Repo.update!(Ecto.Changeset.change(user, is_superuser: true))
%{conn: log_in_user(conn, user), user: user}
end
end