247 lines
8.4 KiB
Elixir
247 lines
8.4 KiB
Elixir
defmodule ToweropsWeb.AgentLiveTest do
|
|
use ToweropsWeb.ConnCase
|
|
|
|
import Ecto.Query
|
|
import ExUnit.CaptureLog
|
|
import Phoenix.LiveViewTest
|
|
|
|
alias Towerops.Agents
|
|
|
|
setup :register_and_log_in_user
|
|
|
|
setup %{user: user} do
|
|
{:ok, organization} = Towerops.Organizations.create_organization(%{name: "Test Org"}, user.id)
|
|
|
|
%{organization: organization}
|
|
end
|
|
|
|
describe "Index" do
|
|
test "lists all agent tokens", %{conn: conn, organization: organization} do
|
|
{:ok, _agent_token, _token} = Agents.create_agent_token(organization.id, "Agent 1")
|
|
|
|
{:ok, _view, html} = live(conn, ~p"/orgs/#{organization.slug}/agents")
|
|
|
|
assert html =~ "Remote Agents"
|
|
assert html =~ "Agent 1"
|
|
end
|
|
|
|
test "displays empty state when no agents", %{conn: conn, organization: organization} do
|
|
{:ok, _view, html} = live(conn, ~p"/orgs/#{organization.slug}/agents")
|
|
|
|
assert html =~ "No agents"
|
|
assert html =~ "Get started by creating your first remote agent"
|
|
end
|
|
|
|
test "creates new agent and shows token modal", %{conn: conn, organization: organization} do
|
|
{:ok, view, _html} = live(conn, ~p"/orgs/#{organization.slug}/agents")
|
|
|
|
html =
|
|
view
|
|
|> form("#new-agent-form form", %{name: "Test Agent"})
|
|
|> render_submit()
|
|
|
|
assert html =~ "Agent Setup Instructions"
|
|
assert html =~ "Test Agent"
|
|
assert html =~ "Authentication Token"
|
|
end
|
|
|
|
test "deletes agent", %{conn: conn, organization: organization} do
|
|
{:ok, agent_token, _token} = Agents.create_agent_token(organization.id, "Agent 1")
|
|
|
|
{:ok, view, _html} = live(conn, ~p"/orgs/#{organization.slug}/agents")
|
|
|
|
html =
|
|
view
|
|
|> element("button", "Delete")
|
|
|> render_click()
|
|
|
|
assert html =~ "Agent deleted successfully"
|
|
|
|
# Verify agent was actually deleted
|
|
assert is_nil(Towerops.Repo.get(Agents.AgentToken, agent_token.id))
|
|
end
|
|
|
|
test "closes token modal", %{conn: conn, organization: organization} do
|
|
{:ok, view, _html} = live(conn, ~p"/orgs/#{organization.slug}/agents")
|
|
|
|
# Create agent to show modal
|
|
view
|
|
|> form("#new-agent-form form", %{name: "Test Agent"})
|
|
|> render_submit()
|
|
|
|
# Close modal
|
|
html =
|
|
view
|
|
|> element("button", "I've Saved the Token")
|
|
|> render_click()
|
|
|
|
refute html =~ "Agent Created Successfully"
|
|
end
|
|
|
|
test "requires authentication", %{organization: organization} do
|
|
conn = build_conn()
|
|
{:error, redirect} = live(conn, ~p"/orgs/#{organization.slug}/agents")
|
|
|
|
assert {:redirect, %{to: path}} = redirect
|
|
assert path == ~p"/users/log-in"
|
|
end
|
|
|
|
test "displays agent with warning status", %{conn: conn, organization: organization} do
|
|
{:ok, agent_token, _token} = Agents.create_agent_token(organization.id, "Agent 1")
|
|
|
|
# Update last_seen_at to 3 minutes ago (warning status)
|
|
three_minutes_ago = DateTime.add(DateTime.utc_now(), -180, :second)
|
|
|
|
Agents.update_agent_token_heartbeat(agent_token.id, "127.0.0.1")
|
|
|
|
Towerops.Repo.update_all(
|
|
from(t in Agents.AgentToken, where: t.id == ^agent_token.id),
|
|
set: [last_seen_at: three_minutes_ago]
|
|
)
|
|
|
|
{:ok, _view, html} = live(conn, ~p"/orgs/#{organization.slug}/agents")
|
|
|
|
assert html =~ "Warning"
|
|
assert html =~ "3m ago"
|
|
end
|
|
|
|
test "displays agent with offline status", %{conn: conn, organization: organization} do
|
|
{:ok, agent_token, _token} = Agents.create_agent_token(organization.id, "Agent 1")
|
|
|
|
# Update last_seen_at to 10 minutes ago (offline status)
|
|
ten_minutes_ago = DateTime.add(DateTime.utc_now(), -600, :second)
|
|
|
|
Agents.update_agent_token_heartbeat(agent_token.id, "127.0.0.1")
|
|
|
|
Towerops.Repo.update_all(
|
|
from(t in Agents.AgentToken, where: t.id == ^agent_token.id),
|
|
set: [last_seen_at: ten_minutes_ago]
|
|
)
|
|
|
|
{:ok, _view, html} = live(conn, ~p"/orgs/#{organization.slug}/agents")
|
|
|
|
assert html =~ "Offline"
|
|
assert html =~ "10m ago"
|
|
end
|
|
|
|
test "displays agent last seen time in hours", %{conn: conn, organization: organization} do
|
|
{:ok, agent_token, _token} = Agents.create_agent_token(organization.id, "Agent 1")
|
|
|
|
# Update last_seen_at to 2 hours ago
|
|
two_hours_ago = DateTime.add(DateTime.utc_now(), -7200, :second)
|
|
|
|
Agents.update_agent_token_heartbeat(agent_token.id, "127.0.0.1")
|
|
|
|
Towerops.Repo.update_all(
|
|
from(t in Agents.AgentToken, where: t.id == ^agent_token.id),
|
|
set: [last_seen_at: two_hours_ago]
|
|
)
|
|
|
|
{:ok, _view, html} = live(conn, ~p"/orgs/#{organization.slug}/agents")
|
|
|
|
assert html =~ "2h ago"
|
|
end
|
|
|
|
test "displays agent last seen time in days", %{conn: conn, organization: organization} do
|
|
{:ok, agent_token, _token} = Agents.create_agent_token(organization.id, "Agent 1")
|
|
|
|
# Update last_seen_at to 2 days ago
|
|
two_days_ago = DateTime.add(DateTime.utc_now(), -172_800, :second)
|
|
|
|
Agents.update_agent_token_heartbeat(agent_token.id, "127.0.0.1")
|
|
|
|
Towerops.Repo.update_all(
|
|
from(t in Agents.AgentToken, where: t.id == ^agent_token.id),
|
|
set: [last_seen_at: two_days_ago]
|
|
)
|
|
|
|
{:ok, _view, html} = live(conn, ~p"/orgs/#{organization.slug}/agents")
|
|
|
|
assert html =~ "2d ago"
|
|
end
|
|
end
|
|
|
|
describe "Edit" do
|
|
test "loads edit page with existing agent name", %{conn: conn, organization: organization} do
|
|
{:ok, agent_token, _token} = Agents.create_agent_token(organization.id, "Old Agent Name")
|
|
|
|
{:ok, _view, html} = live(conn, ~p"/orgs/#{organization.slug}/agents/#{agent_token.id}/edit")
|
|
|
|
assert html =~ "Edit Agent"
|
|
assert html =~ "Old Agent Name"
|
|
assert html =~ "Update the agent name"
|
|
end
|
|
|
|
test "validates empty name shows error on submit", %{conn: conn, organization: organization} do
|
|
{:ok, agent_token, _token} = Agents.create_agent_token(organization.id, "Test Agent")
|
|
|
|
{:ok, view, _html} = live(conn, ~p"/orgs/#{organization.slug}/agents/#{agent_token.id}/edit")
|
|
|
|
html =
|
|
view
|
|
|> form("#edit-agent-form", agent_token: %{name: ""})
|
|
|> render_submit()
|
|
|
|
assert html =~ "can't be blank"
|
|
|
|
# Verify agent was not updated
|
|
agent = Agents.get_agent_token!(agent_token.id)
|
|
assert agent.name == "Test Agent"
|
|
end
|
|
|
|
test "successful save updates agent and redirects to show page", %{
|
|
conn: conn,
|
|
organization: organization
|
|
} do
|
|
{:ok, agent_token, _token} = Agents.create_agent_token(organization.id, "Old Name")
|
|
|
|
{:ok, view, _html} = live(conn, ~p"/orgs/#{organization.slug}/agents/#{agent_token.id}/edit")
|
|
|
|
view
|
|
|> form("#edit-agent-form", agent_token: %{name: "New Name"})
|
|
|> render_submit()
|
|
|
|
# Verify redirect to show page with flash message
|
|
assert_redirect(view, ~p"/orgs/#{organization.slug}/agents/#{agent_token.id}")
|
|
|
|
# Verify agent was updated
|
|
updated_agent = Agents.get_agent_token!(agent_token.id)
|
|
assert updated_agent.name == "New Name"
|
|
end
|
|
|
|
test "cancel button navigates back to show page", %{conn: conn, organization: organization} do
|
|
{:ok, agent_token, _token} = Agents.create_agent_token(organization.id, "Test Agent")
|
|
|
|
{:ok, _view, html} = live(conn, ~p"/orgs/#{organization.slug}/agents/#{agent_token.id}/edit")
|
|
|
|
assert html =~ "Cancel"
|
|
|
|
# Find cancel link and verify it points to show page
|
|
assert html =~
|
|
~p"/orgs/#{organization.slug}/agents/#{agent_token.id}"
|
|
end
|
|
|
|
test "requires authentication", %{organization: organization} do
|
|
{:ok, agent_token, _token} = Agents.create_agent_token(organization.id, "Test Agent")
|
|
|
|
conn = build_conn()
|
|
{:error, redirect} = live(conn, ~p"/orgs/#{organization.slug}/agents/#{agent_token.id}/edit")
|
|
|
|
assert {:redirect, %{to: path}} = redirect
|
|
assert path == ~p"/users/log-in"
|
|
end
|
|
|
|
test "verifies agent belongs to organization", %{conn: conn, organization: organization, user: user} do
|
|
# Create another organization
|
|
{:ok, other_org} = Towerops.Organizations.create_organization(%{name: "Other Org"}, user.id)
|
|
{:ok, agent_token, _token} = Agents.create_agent_token(other_org.id, "Other Agent")
|
|
|
|
# Try to edit agent from other organization - capture_log suppresses expected Router exception log
|
|
capture_log(fn ->
|
|
assert_raise Ecto.NoResultsError, fn ->
|
|
live(conn, ~p"/orgs/#{organization.slug}/agents/#{agent_token.id}/edit")
|
|
end
|
|
end)
|
|
end
|
|
end
|
|
end
|