748 lines
21 KiB
Elixir
748 lines
21 KiB
Elixir
defmodule ToweropsWeb.AgentLive.IndexTest do
|
|
use ToweropsWeb.ConnCase, async: true
|
|
|
|
import Phoenix.LiveViewTest
|
|
import Towerops.AccountsFixtures
|
|
|
|
alias Towerops.Agents
|
|
|
|
setup do
|
|
user = user_fixture(enable_totp: true)
|
|
{:ok, organization} = Towerops.Organizations.create_organization(%{name: "Test Org"}, user.id)
|
|
|
|
%{user: user, organization: organization}
|
|
end
|
|
|
|
describe "mount" do
|
|
test "loads agent tokens for organization", %{conn: conn, user: user, organization: organization} do
|
|
# Create some agent tokens
|
|
{:ok, agent_token1, _token1} = Agents.create_agent_token(organization.id, "Agent 1")
|
|
{:ok, agent_token2, _token2} = Agents.create_agent_token(organization.id, "Agent 2")
|
|
|
|
conn =
|
|
conn
|
|
|> log_in_user(user)
|
|
|> get("/agents")
|
|
|
|
{:ok, _view, html} = live(conn)
|
|
|
|
assert html =~ "Remote Agents"
|
|
assert html =~ agent_token1.name
|
|
assert html =~ agent_token2.name
|
|
end
|
|
|
|
test "shows empty state when no agents exist", %{conn: conn, user: user, organization: _organization} do
|
|
conn =
|
|
conn
|
|
|> log_in_user(user)
|
|
|> get("/agents")
|
|
|
|
{:ok, _view, html} = live(conn)
|
|
|
|
assert html =~ "Remote Agents"
|
|
end
|
|
|
|
test "requires authentication", %{conn: conn} do
|
|
conn = get(conn, "/agents")
|
|
assert redirected_to(conn) == ~p"/users/log-in"
|
|
end
|
|
end
|
|
|
|
describe "create_agent event" do
|
|
test "creates new agent token and shows modal", %{conn: conn, user: user, organization: organization} do
|
|
conn =
|
|
conn
|
|
|> log_in_user(user)
|
|
|> get("/agents")
|
|
|
|
{:ok, view, _html} = live(conn)
|
|
|
|
html =
|
|
view
|
|
|> form("#create-agent-form", %{"name" => "New Agent"})
|
|
|> render_submit()
|
|
|
|
assert has_element?(view, "#token-modal", "New Agent")
|
|
assert html =~ "Agent created successfully"
|
|
|
|
# Verify agent was actually created
|
|
agent_tokens = Agents.list_organization_agent_tokens(organization.id)
|
|
assert length(agent_tokens) == 1
|
|
assert hd(agent_tokens).name == "New Agent"
|
|
end
|
|
|
|
test "shows error on creation failure", %{conn: conn, user: user, organization: _organization} do
|
|
conn =
|
|
conn
|
|
|> log_in_user(user)
|
|
|> get("/agents")
|
|
|
|
{:ok, view, _html} = live(conn)
|
|
|
|
html =
|
|
view
|
|
|> form("#create-agent-form", %{"name" => ""})
|
|
|> render_submit()
|
|
|
|
assert html =~ "Failed to create agent"
|
|
end
|
|
end
|
|
|
|
describe "close_token_modal event" do
|
|
test "closes the token modal", %{conn: conn, user: user, organization: _organization} do
|
|
conn =
|
|
conn
|
|
|> log_in_user(user)
|
|
|> get("/agents")
|
|
|
|
{:ok, view, _html} = live(conn)
|
|
|
|
view
|
|
|> form("#create-agent-form", %{"name" => "Test Agent"})
|
|
|> render_submit()
|
|
|
|
assert has_element?(view, "#token-modal")
|
|
|
|
view |> element("#close-token-modal") |> render_click()
|
|
|
|
refute has_element?(view, "#token-modal")
|
|
assert has_element?(view, "#agents-table", "Test Agent")
|
|
end
|
|
end
|
|
|
|
describe "delete_agent event" do
|
|
test "deletes agent token successfully", %{conn: conn, user: user, organization: organization} do
|
|
{:ok, agent_token, _token} = Agents.create_agent_token(organization.id, "Agent to Delete")
|
|
|
|
conn =
|
|
conn
|
|
|> log_in_user(user)
|
|
|> get("/agents")
|
|
|
|
{:ok, view, _html} = live(conn)
|
|
|
|
html =
|
|
view
|
|
|> element("#delete-agent-#{agent_token.id}")
|
|
|> render_click()
|
|
|
|
assert html =~ "Agent deleted successfully"
|
|
|
|
# Verify agent was deleted (no longer in list)
|
|
agent_tokens = Agents.list_organization_agent_tokens(organization.id)
|
|
assert Enum.empty?(agent_tokens)
|
|
end
|
|
end
|
|
|
|
describe "agent status display" do
|
|
test "shows Never connected for agents without last_seen_at", %{
|
|
conn: conn,
|
|
user: user,
|
|
organization: organization
|
|
} do
|
|
{:ok, agent_token, _token} = Agents.create_agent_token(organization.id, "Never Seen")
|
|
|
|
# Verify the agent has no last_seen_at
|
|
assert is_nil(agent_token.last_seen_at)
|
|
|
|
conn =
|
|
conn
|
|
|> log_in_user(user)
|
|
|> get("/agents")
|
|
|
|
{:ok, _view, html} = live(conn)
|
|
|
|
assert html =~ "Never Seen"
|
|
assert html =~ ~r/Never connected|Never/
|
|
end
|
|
|
|
test "shows agent status information in the page", %{
|
|
conn: conn,
|
|
user: user,
|
|
organization: organization
|
|
} do
|
|
{:ok, agent_token, _token} = Agents.create_agent_token(organization.id, "Status Agent")
|
|
|
|
# Update the agent to have a recent last_seen_at
|
|
agent_token
|
|
|> Ecto.Changeset.change(%{last_seen_at: Towerops.Time.now()})
|
|
|> Towerops.Repo.update!()
|
|
|
|
conn =
|
|
conn
|
|
|> log_in_user(user)
|
|
|> get("/agents")
|
|
|
|
{:ok, _view, html} = live(conn)
|
|
|
|
assert html =~ "Status Agent"
|
|
# The status should show something about timing (Online, seconds ago, etc.)
|
|
assert html =~ ~r/ago|Online/
|
|
end
|
|
end
|
|
|
|
describe "handle_params" do
|
|
test "handles :index action", %{conn: conn, user: user, organization: _organization} do
|
|
conn =
|
|
conn
|
|
|> log_in_user(user)
|
|
|> get("/agents")
|
|
|
|
{:ok, view, _html} = live(conn)
|
|
|
|
# Verify we can navigate to the index action
|
|
assert render(view) =~ "Remote Agents"
|
|
end
|
|
end
|
|
|
|
describe "cloud poller creation - superadmin only" do
|
|
setup %{organization: organization} do
|
|
# Create a superadmin user
|
|
superadmin = user_fixture(enable_totp: true)
|
|
|
|
superadmin =
|
|
superadmin
|
|
|> Ecto.Changeset.change(%{is_superuser: true})
|
|
|> Towerops.Repo.update!()
|
|
|
|
# Add superadmin to the organization
|
|
{:ok, _membership} =
|
|
Towerops.Organizations.create_membership(%{
|
|
organization_id: organization.id,
|
|
user_id: superadmin.id,
|
|
role: "admin"
|
|
})
|
|
|
|
%{superadmin: superadmin}
|
|
end
|
|
|
|
test "superadmin can create cloud poller", %{
|
|
conn: conn,
|
|
superadmin: superadmin,
|
|
organization: _organization
|
|
} do
|
|
conn =
|
|
conn
|
|
|> log_in_user(superadmin)
|
|
|> get("/agents")
|
|
|
|
{:ok, view, html} = live(conn)
|
|
|
|
# Superadmin should see the cloud poller checkbox
|
|
assert html =~ "Cloud Poller"
|
|
|
|
# Create a cloud poller
|
|
html =
|
|
view
|
|
|> form("#create-agent-form", %{"name" => "Test Cloud Poller", "is_cloud_poller" => "true"})
|
|
|> render_submit()
|
|
|
|
assert html =~ "Cloud poller created successfully"
|
|
assert html =~ "Test Cloud Poller"
|
|
|
|
# Verify cloud poller was created
|
|
cloud_pollers = Agents.list_cloud_pollers()
|
|
assert length(cloud_pollers) == 1
|
|
assert hd(cloud_pollers).name == "Test Cloud Poller"
|
|
assert hd(cloud_pollers).is_cloud_poller == true
|
|
assert hd(cloud_pollers).organization_id == nil
|
|
end
|
|
|
|
test "non-superadmin cannot create cloud poller", %{
|
|
conn: conn,
|
|
user: user,
|
|
organization: organization
|
|
} do
|
|
conn =
|
|
conn
|
|
|> log_in_user(user)
|
|
|> get("/agents")
|
|
|
|
{:ok, view, html} = live(conn)
|
|
|
|
# Regular user should NOT see the cloud poller checkbox
|
|
refute html =~ "Cloud Poller (Application-wide agent"
|
|
|
|
# The cloud_poller checkbox is intentionally not rendered for non-superadmins, so
|
|
# form/3 cannot set it (which validates the UI-level guard works). To test the
|
|
# server-side guard against a tampered POST, bypass the form helper here.
|
|
html =
|
|
render_submit(view, "create_agent", %{
|
|
"agent_form" => %{"name" => "Unauthorized Cloud Poller", "is_cloud_poller" => "true"}
|
|
})
|
|
|
|
assert html =~ "Only superadmins can create cloud pollers"
|
|
|
|
# Verify cloud poller was NOT created
|
|
cloud_pollers = Agents.list_cloud_pollers()
|
|
assert Enum.empty?(cloud_pollers)
|
|
|
|
# Verify organization agent was NOT created
|
|
org_agents = Agents.list_organization_agent_tokens(organization.id)
|
|
assert Enum.empty?(org_agents)
|
|
end
|
|
|
|
test "superadmin can create regular agent by not checking cloud poller", %{
|
|
conn: conn,
|
|
superadmin: superadmin,
|
|
organization: organization
|
|
} do
|
|
conn =
|
|
conn
|
|
|> log_in_user(superadmin)
|
|
|> get("/agents")
|
|
|
|
{:ok, view, _html} = live(conn)
|
|
|
|
# Create a regular agent (is_cloud_poller defaults to false from the hidden input)
|
|
html =
|
|
view
|
|
|> form("#create-agent-form", %{"name" => "Regular Agent"})
|
|
|> render_submit()
|
|
|
|
assert html =~ "Agent created successfully"
|
|
assert html =~ "Regular Agent"
|
|
|
|
# Verify regular agent was created
|
|
org_agents = Agents.list_organization_agent_tokens(organization.id)
|
|
assert length(org_agents) == 1
|
|
assert hd(org_agents).name == "Regular Agent"
|
|
assert hd(org_agents).is_cloud_poller == false
|
|
assert hd(org_agents).organization_id == organization.id
|
|
|
|
# Verify cloud poller was NOT created
|
|
cloud_pollers = Agents.list_cloud_pollers()
|
|
assert Enum.empty?(cloud_pollers)
|
|
end
|
|
end
|
|
|
|
describe "cloud poller visibility" do
|
|
setup %{organization: organization} do
|
|
# Create a superadmin user
|
|
superadmin = user_fixture(enable_totp: true)
|
|
|
|
superadmin =
|
|
superadmin
|
|
|> Ecto.Changeset.change(%{is_superuser: true})
|
|
|> Towerops.Repo.update!()
|
|
|
|
# Add superadmin to the organization
|
|
{:ok, _membership} =
|
|
Towerops.Organizations.create_membership(%{
|
|
organization_id: organization.id,
|
|
user_id: superadmin.id,
|
|
role: "admin"
|
|
})
|
|
|
|
# Create some cloud pollers
|
|
{:ok, cloud1, _} = Agents.create_cloud_poller("Cloud Poller 1")
|
|
{:ok, cloud2, _} = Agents.create_cloud_poller("Cloud Poller 2")
|
|
|
|
# Create organization agents
|
|
{:ok, org_agent, _} = Agents.create_agent_token(organization.id, "Org Agent")
|
|
|
|
%{superadmin: superadmin, cloud1: cloud1, cloud2: cloud2, org_agent: org_agent}
|
|
end
|
|
|
|
test "superadmin sees cloud pollers in separate section", %{
|
|
conn: conn,
|
|
superadmin: superadmin,
|
|
organization: _organization,
|
|
cloud1: cloud1,
|
|
cloud2: cloud2,
|
|
org_agent: org_agent
|
|
} do
|
|
conn =
|
|
conn
|
|
|> log_in_user(superadmin)
|
|
|> get("/agents")
|
|
|
|
{:ok, _view, html} = live(conn)
|
|
|
|
# Should see cloud pollers section header
|
|
assert html =~ "Cloud Pollers"
|
|
|
|
# Should see both cloud pollers
|
|
assert html =~ cloud1.name
|
|
assert html =~ cloud2.name
|
|
|
|
# Should see organization agent in main section
|
|
assert html =~ org_agent.name
|
|
|
|
# Should see visual distinction for cloud pollers
|
|
assert html =~ ~r/Cloud Poller|cloud-poller/
|
|
end
|
|
|
|
test "non-superadmin does not see cloud pollers section", %{
|
|
conn: conn,
|
|
user: user,
|
|
organization: _organization,
|
|
cloud1: cloud1,
|
|
cloud2: cloud2,
|
|
org_agent: org_agent
|
|
} do
|
|
conn =
|
|
conn
|
|
|> log_in_user(user)
|
|
|> get("/agents")
|
|
|
|
{:ok, _view, html} = live(conn)
|
|
|
|
# Should NOT see cloud pollers section header
|
|
refute html =~ "Cloud Pollers"
|
|
|
|
# Should NOT see cloud poller names
|
|
refute html =~ cloud1.name
|
|
refute html =~ cloud2.name
|
|
|
|
# Should see organization agent in main section
|
|
assert html =~ org_agent.name
|
|
end
|
|
end
|
|
|
|
describe "cloud poller management" do
|
|
setup %{organization: organization} do
|
|
# Create a superadmin user
|
|
superadmin = user_fixture(enable_totp: true)
|
|
|
|
superadmin =
|
|
superadmin
|
|
|> Ecto.Changeset.change(%{is_superuser: true})
|
|
|> Towerops.Repo.update!()
|
|
|
|
# Add superadmin to the organization
|
|
{:ok, _membership} =
|
|
Towerops.Organizations.create_membership(%{
|
|
organization_id: organization.id,
|
|
user_id: superadmin.id,
|
|
role: "admin"
|
|
})
|
|
|
|
{:ok, cloud_poller, _} = Agents.create_cloud_poller("Test Cloud Poller")
|
|
|
|
%{superadmin: superadmin, cloud_poller: cloud_poller}
|
|
end
|
|
|
|
test "superadmin can delete cloud poller", %{
|
|
conn: conn,
|
|
superadmin: superadmin,
|
|
organization: _organization,
|
|
cloud_poller: cloud_poller
|
|
} do
|
|
conn =
|
|
conn
|
|
|> log_in_user(superadmin)
|
|
|> get("/agents")
|
|
|
|
{:ok, view, _html} = live(conn)
|
|
|
|
html =
|
|
view
|
|
|> element("#delete-agent-#{cloud_poller.id}")
|
|
|> render_click()
|
|
|
|
assert html =~ "Agent deleted successfully"
|
|
|
|
# Verify cloud poller was deleted
|
|
cloud_pollers = Agents.list_cloud_pollers()
|
|
assert Enum.empty?(cloud_pollers)
|
|
end
|
|
|
|
test "superadmin can view cloud poller setup instructions", %{
|
|
conn: conn,
|
|
superadmin: superadmin,
|
|
organization: _organization,
|
|
cloud_poller: cloud_poller
|
|
} do
|
|
conn =
|
|
conn
|
|
|> log_in_user(superadmin)
|
|
|> get("/agents")
|
|
|
|
{:ok, view, _html} = live(conn)
|
|
|
|
html =
|
|
view
|
|
|> element("#show-setup-#{cloud_poller.id}")
|
|
|> render_click()
|
|
|
|
assert html =~ "Test Cloud Poller"
|
|
assert has_element?(view, "#token-modal")
|
|
end
|
|
end
|
|
|
|
describe "global default cloud poller management" do
|
|
setup %{organization: organization} do
|
|
# Create a superadmin user
|
|
superadmin = user_fixture(enable_totp: true)
|
|
|
|
superadmin =
|
|
superadmin
|
|
|> Ecto.Changeset.change(%{is_superuser: true})
|
|
|> Towerops.Repo.update!()
|
|
|
|
# Add superadmin to the organization
|
|
{:ok, _membership} =
|
|
Towerops.Organizations.create_membership(%{
|
|
organization_id: organization.id,
|
|
user_id: superadmin.id,
|
|
role: "admin"
|
|
})
|
|
|
|
{:ok, cloud_poller, _} = Agents.create_cloud_poller("Test Cloud Poller")
|
|
|
|
%{superadmin: superadmin, cloud_poller: cloud_poller}
|
|
end
|
|
|
|
test "superadmin can set global default cloud poller with validation", %{
|
|
conn: conn,
|
|
superadmin: superadmin,
|
|
organization: _organization,
|
|
cloud_poller: cloud_poller
|
|
} do
|
|
conn =
|
|
conn
|
|
|> log_in_user(superadmin)
|
|
|> get("/agents")
|
|
|
|
{:ok, view, html} = live(conn)
|
|
|
|
# Should see global default section
|
|
assert html =~ "Global Default Cloud Poller"
|
|
|
|
# Update dropdown selection (doesn't save yet)
|
|
view
|
|
|> form("#global-default-form", %{"agent_token_id" => cloud_poller.id})
|
|
|> render_change()
|
|
|
|
html = view |> element("#save-global-default") |> render_click()
|
|
|
|
assert html =~ "Global default cloud poller set successfully"
|
|
|
|
# Verify it was saved
|
|
assert Towerops.Settings.get_global_default_cloud_poller() == cloud_poller.id
|
|
end
|
|
|
|
test "superadmin cannot save non-existent agent as global default", %{
|
|
conn: conn,
|
|
superadmin: superadmin,
|
|
organization: _organization
|
|
} do
|
|
conn =
|
|
conn
|
|
|> log_in_user(superadmin)
|
|
|> get("/agents")
|
|
|
|
{:ok, view, _html} = live(conn)
|
|
|
|
fake_id = Ecto.UUID.generate()
|
|
|
|
# Tests a race-condition guard: the agent was deleted between page load and save.
|
|
# form/3 validates against the rendered <option> list, which by definition can't
|
|
# include the deleted agent — so we drive the events directly to reach the guard.
|
|
render_change(view, "update_selected_global_default", %{"agent_token_id" => fake_id})
|
|
html = render_click(view, "save_global_default", %{})
|
|
|
|
assert html =~ "Selected agent no longer exists"
|
|
|
|
# Verify it was NOT saved
|
|
refute Towerops.Settings.get_global_default_cloud_poller() == fake_id
|
|
end
|
|
|
|
test "superadmin can clear global default cloud poller", %{
|
|
conn: conn,
|
|
superadmin: superadmin,
|
|
organization: _organization,
|
|
cloud_poller: cloud_poller
|
|
} do
|
|
# First set a global default
|
|
Towerops.Settings.set_global_default_cloud_poller(cloud_poller.id)
|
|
|
|
conn =
|
|
conn
|
|
|> log_in_user(superadmin)
|
|
|> get("/agents")
|
|
|
|
{:ok, view, html} = live(conn)
|
|
|
|
# Should show current default
|
|
assert html =~ "Currently using"
|
|
|
|
# Select "No global default"
|
|
view
|
|
|> form("#global-default-form", %{"agent_token_id" => ""})
|
|
|> render_change()
|
|
|
|
html = view |> element("#save-global-default") |> render_click()
|
|
|
|
assert html =~ "Global default cloud poller cleared"
|
|
|
|
# Verify it was cleared
|
|
assert Towerops.Settings.get_global_default_cloud_poller() == nil
|
|
end
|
|
|
|
test "deleting cloud poller clears it from global default", %{
|
|
conn: conn,
|
|
superadmin: superadmin,
|
|
organization: _organization,
|
|
cloud_poller: cloud_poller
|
|
} do
|
|
# Set as global default
|
|
Towerops.Settings.set_global_default_cloud_poller(cloud_poller.id)
|
|
|
|
conn =
|
|
conn
|
|
|> log_in_user(superadmin)
|
|
|> get("/agents")
|
|
|
|
{:ok, view, _html} = live(conn)
|
|
|
|
# Delete the cloud poller
|
|
html =
|
|
view
|
|
|> element("#delete-agent-#{cloud_poller.id}")
|
|
|> render_click()
|
|
|
|
assert html =~ "Agent deleted successfully"
|
|
|
|
# Verify global default was cleared (CASCADE)
|
|
assert Towerops.Settings.get_global_default_cloud_poller() == nil
|
|
|
|
# Verify the UI reflects this
|
|
assert html =~ ~r/No global default|direct Phoenix/
|
|
end
|
|
|
|
test "non-superadmin cannot see global default section", %{
|
|
conn: conn,
|
|
user: user,
|
|
organization: _organization
|
|
} do
|
|
conn =
|
|
conn
|
|
|> log_in_user(user)
|
|
|> get("/agents")
|
|
|
|
{:ok, _view, html} = live(conn)
|
|
|
|
# Should NOT see global default section
|
|
refute html =~ "Global Default Cloud Poller"
|
|
end
|
|
end
|
|
|
|
describe "show_setup event" do
|
|
test "opens setup modal for an existing agent",
|
|
%{conn: conn, user: user, organization: organization} do
|
|
{:ok, agent_token, _raw} = Agents.create_agent_token(organization.id, "SU Agent")
|
|
|
|
conn = log_in_user(conn, user)
|
|
{:ok, view, _html} = live(conn, "/agents")
|
|
|
|
_ = render_hook(view, "show_setup", %{"id" => agent_token.id})
|
|
assert Process.alive?(view.pid)
|
|
end
|
|
end
|
|
|
|
describe "handle_info :tick" do
|
|
test "tick triggers re-render without crashing",
|
|
%{conn: conn, user: user, organization: _org} do
|
|
conn = log_in_user(conn, user)
|
|
{:ok, view, _html} = live(conn, "/agents")
|
|
|
|
send(view.pid, :tick)
|
|
_ = render(view)
|
|
assert Process.alive?(view.pid)
|
|
end
|
|
end
|
|
|
|
describe "handle_info agent lifecycle messages" do
|
|
test "agent_connected for matching org updates agent assigns",
|
|
%{conn: conn, user: user, organization: organization} do
|
|
{:ok, agent_token, _raw} = Agents.create_agent_token(organization.id, "Live Agent")
|
|
|
|
conn = log_in_user(conn, user)
|
|
{:ok, view, _html} = live(conn, "/agents")
|
|
|
|
send(view.pid, {:agent_connected, agent_token.id, organization.id})
|
|
_ = render(view)
|
|
assert Process.alive?(view.pid)
|
|
end
|
|
|
|
test "agent_disconnected for matching org doesn't crash",
|
|
%{conn: conn, user: user, organization: organization} do
|
|
{:ok, agent_token, _raw} = Agents.create_agent_token(organization.id, "Live Agent 2")
|
|
|
|
conn = log_in_user(conn, user)
|
|
{:ok, view, _html} = live(conn, "/agents")
|
|
|
|
send(view.pid, {:agent_disconnected, agent_token.id, organization.id})
|
|
_ = render(view)
|
|
assert Process.alive?(view.pid)
|
|
end
|
|
|
|
test "agent_heartbeat for matching org doesn't crash",
|
|
%{conn: conn, user: user, organization: organization} do
|
|
{:ok, agent_token, _raw} = Agents.create_agent_token(organization.id, "Live Agent 3")
|
|
|
|
conn = log_in_user(conn, user)
|
|
{:ok, view, _html} = live(conn, "/agents")
|
|
|
|
send(view.pid, {:agent_heartbeat, agent_token.id, organization.id})
|
|
_ = render(view)
|
|
assert Process.alive?(view.pid)
|
|
end
|
|
|
|
test "agent_connected for different org is ignored",
|
|
%{conn: conn, user: user, organization: _org} do
|
|
conn = log_in_user(conn, user)
|
|
{:ok, view, _html} = live(conn, "/agents")
|
|
|
|
send(view.pid, {:agent_connected, Ecto.UUID.generate(), Ecto.UUID.generate()})
|
|
_ = render(view)
|
|
assert Process.alive?(view.pid)
|
|
end
|
|
|
|
test "agents_stale message is handled",
|
|
%{conn: conn, user: user, organization: _org} do
|
|
conn = log_in_user(conn, user)
|
|
{:ok, view, _html} = live(conn, "/agents")
|
|
|
|
send(view.pid, {:agents_stale, []})
|
|
_ = render(view)
|
|
assert Process.alive?(view.pid)
|
|
end
|
|
end
|
|
|
|
describe "global default cloud poller events" do
|
|
test "update_selected_global_default and save_global_default for superuser" do
|
|
superuser = user_fixture(enable_totp: true)
|
|
|
|
superuser =
|
|
superuser
|
|
|> Ecto.Changeset.change(%{is_superuser: true})
|
|
|> Towerops.Repo.update!()
|
|
|
|
{:ok, super_org} =
|
|
Towerops.Organizations.create_organization(%{name: "Super Cloud Org"}, superuser.id)
|
|
|
|
{:ok, cloud_poller, _raw} = Agents.create_cloud_poller("Default Cloud")
|
|
|
|
conn =
|
|
Phoenix.ConnTest.build_conn()
|
|
|> log_in_user(superuser)
|
|
|> Plug.Conn.put_session(:current_organization_id, super_org.id)
|
|
|
|
{:ok, view, _html} = live(conn, "/agents")
|
|
|
|
_ =
|
|
render_hook(view, "update_selected_global_default", %{
|
|
"agent_token_id" => cloud_poller.id
|
|
})
|
|
|
|
_ = render_hook(view, "save_global_default", %{})
|
|
|
|
# No crash → success or warning flash
|
|
_ = render(view)
|
|
assert Process.alive?(view.pid)
|
|
end
|
|
end
|
|
end
|