265 lines
7.3 KiB
Elixir
265 lines
7.3 KiB
Elixir
defmodule ToweropsWeb.AlertLive.IndexTest do
|
||
use ToweropsWeb.ConnCase
|
||
|
||
import Phoenix.LiveViewTest
|
||
|
||
setup :register_and_log_in_user
|
||
|
||
setup %{user: user} do
|
||
{:ok, organization} = Towerops.Organizations.create_organization(%{name: "Test Org"}, user.id)
|
||
|
||
{:ok, site} =
|
||
Towerops.Sites.create_site(%{
|
||
name: "Test Site",
|
||
organization_id: organization.id
|
||
})
|
||
|
||
{:ok, device} =
|
||
Towerops.Devices.create_device(%{
|
||
name: "Test Router",
|
||
ip_address: "192.168.1.1",
|
||
site_id: site.id,
|
||
organization_id: organization.id
|
||
})
|
||
|
||
%{organization: organization, site: site, device: device}
|
||
end
|
||
|
||
describe "Index" do
|
||
test "displays alerts page", %{conn: conn, organization: _organization} do
|
||
{:ok, _view, html} = live(conn, ~p"/alerts")
|
||
|
||
assert html =~ "Alerts"
|
||
end
|
||
|
||
test "displays empty state when no alerts", %{conn: conn, organization: _organization} do
|
||
{:ok, _view, html} = live(conn, ~p"/alerts")
|
||
|
||
assert html =~ "No active alerts"
|
||
end
|
||
|
||
@tag :skip
|
||
test "lists all alerts", %{conn: conn, organization: _organization, device: device} do
|
||
{:ok, _alert} =
|
||
Towerops.Alerts.create_alert(%{
|
||
device_id: device.id,
|
||
alert_type: "device_down",
|
||
triggered_at: DateTime.utc_now(),
|
||
message: "Device is down"
|
||
})
|
||
|
||
{:ok, _view, html} = live(conn, ~p"/alerts")
|
||
|
||
assert html =~ "Test Router"
|
||
assert html =~ "Device is down"
|
||
# Alert type badge shows "Down" for device_down alerts
|
||
assert html =~ "DOWN"
|
||
end
|
||
|
||
test "renders alerts with different severity ages (drives severity_badge_class branches)",
|
||
%{conn: conn, device: device} do
|
||
now = DateTime.utc_now()
|
||
|
||
# Yellow: <= 15 min old
|
||
{:ok, _yellow} =
|
||
Towerops.Alerts.create_alert(%{
|
||
device_id: device.id,
|
||
alert_type: "device_down",
|
||
triggered_at: DateTime.add(now, -5, :minute),
|
||
message: "Recent"
|
||
})
|
||
|
||
# Orange: 15–60 min old
|
||
{:ok, _orange} =
|
||
Towerops.Alerts.create_alert(%{
|
||
device_id: device.id,
|
||
alert_type: "device_down",
|
||
triggered_at: DateTime.add(now, -30, :minute),
|
||
message: "Half hour"
|
||
})
|
||
|
||
# Red: > 60 min old
|
||
{:ok, _red} =
|
||
Towerops.Alerts.create_alert(%{
|
||
device_id: device.id,
|
||
alert_type: "device_down",
|
||
triggered_at: DateTime.add(now, -2, :hour),
|
||
message: "Two hours"
|
||
})
|
||
|
||
{:ok, _view, html} = live(conn, ~p"/alerts")
|
||
|
||
assert html =~ "bg-yellow-500"
|
||
assert html =~ "bg-orange-500"
|
||
assert html =~ "bg-red-500"
|
||
end
|
||
|
||
test "filters critical alerts", %{conn: conn, organization: _organization, device: device} do
|
||
# Create a critical device_down alert (unresolved)
|
||
{:ok, _active_alert} =
|
||
Towerops.Alerts.create_alert(%{
|
||
device_id: device.id,
|
||
alert_type: "device_down",
|
||
triggered_at: DateTime.utc_now(),
|
||
message: "Device is down"
|
||
})
|
||
|
||
# Create a recovery alert (device_up)
|
||
{:ok, _recovery_alert} =
|
||
Towerops.Alerts.create_alert(%{
|
||
device_id: device.id,
|
||
alert_type: "device_up",
|
||
triggered_at: DateTime.utc_now(),
|
||
message: "Device recovered"
|
||
})
|
||
|
||
{:ok, _view, html} =
|
||
live(conn, ~p"/alerts?filter=critical")
|
||
|
||
# Should show device_down alert
|
||
assert html =~ "Device is down"
|
||
# Should NOT show device_up alert in critical filter
|
||
refute html =~ "Device recovered"
|
||
end
|
||
|
||
@tag :skip
|
||
test "acknowledges an alert", %{conn: conn, organization: _organization, device: device} do
|
||
{:ok, alert} =
|
||
Towerops.Alerts.create_alert(%{
|
||
device_id: device.id,
|
||
alert_type: "device_down",
|
||
triggered_at: DateTime.utc_now(),
|
||
message: "Device is down"
|
||
})
|
||
|
||
{:ok, view, _html} = live(conn, ~p"/alerts")
|
||
|
||
html =
|
||
view
|
||
|> element("button", "Ack")
|
||
|> render_click(%{"id" => alert.id})
|
||
|
||
assert html =~ "Alert acknowledged"
|
||
end
|
||
|
||
test "does not show acknowledge button for device_up alerts", %{
|
||
conn: conn,
|
||
organization: _organization,
|
||
device: device
|
||
} do
|
||
{:ok, _alert} =
|
||
Towerops.Alerts.create_alert(%{
|
||
device_id: device.id,
|
||
alert_type: "device_up",
|
||
triggered_at: DateTime.utc_now(),
|
||
message: "Device recovered"
|
||
})
|
||
|
||
{:ok, _view, html} = live(conn, ~p"/alerts")
|
||
|
||
# Should not show acknowledge button for device_up alerts
|
||
# The button text in the template is "Ack" (short for Acknowledge)
|
||
refute html =~ "phx-click=\"acknowledge\""
|
||
end
|
||
|
||
test "updates in real-time when new alert is created", %{
|
||
conn: conn,
|
||
organization: organization,
|
||
device: device
|
||
} do
|
||
{:ok, view, _html} = live(conn, ~p"/alerts")
|
||
|
||
# Broadcast new alert event on org-scoped topic
|
||
Phoenix.PubSub.broadcast(
|
||
Towerops.PubSub,
|
||
"alerts:org:#{organization.id}:new",
|
||
{:new_alert, device.id, :device_down}
|
||
)
|
||
|
||
# View should update
|
||
assert render(view)
|
||
end
|
||
|
||
test "updates in real-time when alert is resolved", %{
|
||
conn: conn,
|
||
organization: organization,
|
||
device: device
|
||
} do
|
||
{:ok, alert} =
|
||
Towerops.Alerts.create_alert(%{
|
||
device_id: device.id,
|
||
alert_type: "device_down",
|
||
triggered_at: DateTime.utc_now(),
|
||
message: "Device is down"
|
||
})
|
||
|
||
{:ok, view, _html} = live(conn, ~p"/alerts")
|
||
|
||
# Resolve the alert and broadcast
|
||
Towerops.Alerts.resolve_alert(alert)
|
||
|
||
Phoenix.PubSub.broadcast(
|
||
Towerops.PubSub,
|
||
"alerts:org:#{organization.id}:resolved",
|
||
{:alert_resolved, device.id, :device_down}
|
||
)
|
||
|
||
# View should update
|
||
assert render(view)
|
||
end
|
||
|
||
test "requires authentication", %{organization: _organization} do
|
||
conn = build_conn()
|
||
{:error, redirect} = live(conn, ~p"/alerts")
|
||
|
||
assert {:redirect, %{to: path}} = redirect
|
||
assert path == ~p"/users/log-in"
|
||
end
|
||
|
||
test "shows site subscriber context when Gaiia mapped", %{
|
||
conn: conn,
|
||
organization: organization,
|
||
site: site,
|
||
device: device
|
||
} do
|
||
{:ok, _alert} =
|
||
Towerops.Alerts.create_alert(%{
|
||
device_id: device.id,
|
||
alert_type: "device_down",
|
||
triggered_at: DateTime.utc_now(),
|
||
message: "Device is down"
|
||
})
|
||
|
||
{:ok, _} =
|
||
Towerops.Gaiia.upsert_network_site(organization.id, %{
|
||
gaiia_id: "ns-alert-test",
|
||
name: "Tower",
|
||
account_count: 75,
|
||
total_mrr: Decimal.new("3750.00"),
|
||
site_id: site.id
|
||
})
|
||
|
||
{:ok, _view, html} = live(conn, ~p"/alerts")
|
||
|
||
assert html =~ "75"
|
||
end
|
||
|
||
test "hides site subscriber context when no Gaiia mapping", %{
|
||
conn: conn,
|
||
device: device
|
||
} do
|
||
{:ok, _alert} =
|
||
Towerops.Alerts.create_alert(%{
|
||
device_id: device.id,
|
||
alert_type: "device_down",
|
||
triggered_at: DateTime.utc_now(),
|
||
message: "Device is down"
|
||
})
|
||
|
||
{:ok, _view, html} = live(conn, ~p"/alerts")
|
||
|
||
refute html =~ "subscribers"
|
||
refute html =~ "MRR"
|
||
end
|
||
end
|
||
end
|