towerops/test/towerops_web/live/admin/monitoring_live_test.exs

199 lines
6 KiB
Elixir

defmodule ToweropsWeb.Admin.MonitoringLiveTest do
use ToweropsWeb.ConnCase
import Phoenix.LiveViewTest
import Towerops.DevicesFixtures
import Towerops.JobsFixtures
describe "MonitoringLive" do
setup [:register_and_log_in_superuser]
test "renders monitoring dashboard", %{conn: conn} do
{:ok, _view, html} = live(conn, ~p"/admin/monitoring")
assert html =~ "Job Monitoring Dashboard"
assert html =~ "Active Operations"
assert html =~ "Problems"
assert html =~ "Health Metrics"
end
test "subscribes to job lifecycle events", %{conn: conn} do
{:ok, view, _html} = live(conn, ~p"/admin/monitoring")
# Verify PubSub subscription by checking it's alive
assert Process.alive?(view.pid)
end
test "displays active operations section", %{conn: conn} do
{:ok, _view, html} = live(conn, ~p"/admin/monitoring")
assert html =~ "Active Operations"
assert html =~ "No jobs currently executing"
end
end
describe "real-time updates" do
setup [:register_and_log_in_superuser]
test "displays executing jobs from database", %{conn: conn} do
device = device_fixture()
_job =
oban_job_fixture(%{
worker: "Towerops.Workers.DevicePollerWorker",
state: "executing",
args: %{"device_id" => device.id},
attempted_at: DateTime.utc_now()
})
{:ok, _view, html} = live(conn, ~p"/admin/monitoring")
# Should show the device name in executing jobs
assert html =~ device.name
assert html =~ "Active Operations (1)"
end
end
describe "metrics display" do
setup [:register_and_log_in_superuser]
test "shows current activity metrics", %{conn: conn} do
device = device_fixture()
oban_job_fixture(%{
worker: "Towerops.Workers.DevicePollerWorker",
state: "executing",
args: %{"device_id" => device.id}
})
{:ok, _view, html} = live(conn, ~p"/admin/monitoring")
assert html =~ "Executing"
assert html =~ "1"
end
end
describe "handle_info events" do
setup [:register_and_log_in_superuser]
test "handles job started event", %{conn: conn} do
{:ok, view, _html} = live(conn, ~p"/admin/monitoring")
send(view.pid, %{event: :started, job_id: 1, worker: "TestWorker"})
assert render(view)
end
test "handles job completed event", %{conn: conn} do
{:ok, view, _html} = live(conn, ~p"/admin/monitoring")
send(view.pid, %{event: :completed, job_id: 1, worker: "TestWorker"})
assert render(view)
end
test "handles job failed event", %{conn: conn} do
{:ok, view, _html} = live(conn, ~p"/admin/monitoring")
send(view.pid, %{event: :failed, job_id: 1, worker: "TestWorker"})
assert render(view)
end
test "handles unknown events gracefully", %{conn: conn} do
{:ok, view, _html} = live(conn, ~p"/admin/monitoring")
send(view.pid, :some_unknown_event)
assert render(view)
end
end
describe "renders helpers via template" do
setup [:register_and_log_in_superuser]
test "renders worker_name + duration_in_words for a stuck poller", %{conn: conn} do
device = device_fixture()
# Poller stuck > 2 minutes triggers the stuck-jobs render path
five_minutes_ago = DateTime.add(DateTime.utc_now(), -5 * 60, :second)
oban_job_fixture(%{
worker: "Towerops.Workers.DevicePollerWorker",
state: "executing",
attempted_at: five_minutes_ago,
args: %{"device_id" => device.id}
})
{:ok, _view, html} = live(conn, ~p"/admin/monitoring")
assert html =~ "Stuck Jobs"
# worker_name/1 maps the FQ worker to "Device Poll"
assert html =~ "Device Poll"
# duration_in_words/1 emits a "5m" or "Xm" string
assert html =~ ~r/Running for \d+m/
end
test "renders event_outcome / event_border_color for completed jobs in recent activity", %{
conn: conn
} do
device = device_fixture()
oban_job_fixture(%{
worker: "Towerops.Workers.DevicePollerWorker",
state: "completed",
attempted_at: DateTime.add(DateTime.utc_now(), -120, :second),
completed_at: DateTime.utc_now(),
args: %{"device_id" => device.id}
})
oban_job_fixture(%{
worker: "Towerops.Workers.DiscoveryWorker",
state: "cancelled",
attempted_at: DateTime.add(DateTime.utc_now(), -120, :second),
completed_at: DateTime.utc_now(),
args: %{"device_id" => device.id}
})
oban_job_fixture(%{
worker: "Towerops.Workers.DevicePollerWorker",
state: "discarded",
attempted_at: DateTime.add(DateTime.utc_now(), -120, :second),
completed_at: DateTime.utc_now(),
args: %{"device_id" => device.id}
})
{:ok, _view, html} = live(conn, ~p"/admin/monitoring")
# event_outcome/1 strings
assert html =~ "Completed"
assert html =~ "Failed"
assert html =~ "Discarded"
# event_border_color/1 classes
assert html =~ "border-green-500"
assert html =~ "border-red-500"
assert html =~ "border-gray-400"
# SNMP Discovery from worker_name fallback
assert html =~ "SNMP Discovery"
end
test "duration_in_words renders 'h m' for jobs stuck for hours", %{conn: conn} do
device = device_fixture()
two_hours_ago = DateTime.add(DateTime.utc_now(), -3 * 3600, :second)
oban_job_fixture(%{
worker: "Towerops.Workers.DiscoveryWorker",
state: "executing",
attempted_at: two_hours_ago,
args: %{"device_id" => device.id}
})
{:ok, _view, html} = live(conn, ~p"/admin/monitoring")
assert html =~ ~r/Running for \dh \d+m/
end
end
defp register_and_log_in_superuser(%{conn: conn}) do
user = Towerops.AccountsFixtures.user_fixture(enable_totp: true)
user = Towerops.Repo.update!(Ecto.Changeset.change(user, is_superuser: true))
%{conn: log_in_user(conn, user), user: user}
end
end