towerops/test/towerops/alerts_test.exs
Graham McIntire 155b38ab94
feat: add APC, Arista, Calix, ADTRAN, Allied Telesis vendor modules
Add 5 new vendor modules for network infrastructure equipment:
- APC: UPS and PDU monitoring (battery, voltage, load, runtime)
- Arista EOS: Data center switches (CPU, memory)
- Calix: GPON/fiber access equipment
- ADTRAN AOS: NetVanta routers and switches
- Allied Telesis: AlliedWare/AlliedWare Plus switches

Total: 53 vendors, 88 profile names

Also cleanup commented AlertNotifier code from previous removal.
2026-01-22 11:00:21 -06:00

370 lines
12 KiB
Elixir

defmodule Towerops.AlertsTest do
use Towerops.DataCase
alias Towerops.Alerts
describe "alerts" do
import Towerops.AccountsFixtures
alias Towerops.Alerts.Alert
setup do
user = user_fixture()
{: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: "Router 1",
ip_address: "192.168.1.1",
site_id: site.id
})
%{device: device, organization: organization, user: user}
end
@valid_attrs %{
alert_type: :device_down,
triggered_at: ~U[2025-12-21 12:00:00Z],
message: "Equipment is not responding"
}
test "create_alert/1 with valid data creates an alert", %{device: device} do
attrs = Map.put(@valid_attrs, :device_id, device.id)
assert {:ok, %Alert{} = alert} = Alerts.create_alert(attrs)
assert alert.alert_type == :device_down
assert alert.message == "Equipment is not responding"
end
test "create_alert/1 with invalid data returns error changeset" do
assert {:error, %Ecto.Changeset{}} = Alerts.create_alert(%{})
end
test "list_devices_alerts/2 returns alerts for device", %{device: device} do
attrs = Map.put(@valid_attrs, :device_id, device.id)
{:ok, alert} = Alerts.create_alert(attrs)
[result] = Alerts.list_devices_alerts(device.id)
assert result.id == alert.id
end
test "list_organization_active_alerts/1 returns unresolved alerts", %{
device: device,
organization: organization
} do
attrs = Map.put(@valid_attrs, :device_id, device.id)
{:ok, alert} = Alerts.create_alert(attrs)
# Create resolved alert
{:ok, resolved_alert} =
Alerts.create_alert(Map.put(attrs, :resolved_at, ~U[2025-12-21 13:00:00Z]))
active = Alerts.list_organization_active_alerts(organization.id)
assert length(active) == 1
assert hd(active).id == alert.id
refute Enum.any?(active, fn a -> a.id == resolved_alert.id end)
end
test "list_organization_alerts/2 returns all alerts", %{
device: device,
organization: organization
} do
attrs = Map.put(@valid_attrs, :device_id, device.id)
{:ok, _alert1} = Alerts.create_alert(attrs)
{:ok, _alert2} = Alerts.create_alert(Map.put(attrs, :alert_type, :device_up))
alerts = Alerts.list_organization_alerts(organization.id, 100)
assert length(alerts) == 2
end
test "get_alert!/1 returns the alert with given id", %{device: device} do
attrs = Map.put(@valid_attrs, :device_id, device.id)
{:ok, alert} = Alerts.create_alert(attrs)
assert Alerts.get_alert!(alert.id).id == alert.id
end
test "acknowledge_alert/2 marks alert as acknowledged", %{device: device, user: user} do
attrs = Map.put(@valid_attrs, :device_id, device.id)
{:ok, alert} = Alerts.create_alert(attrs)
assert {:ok, acknowledged} = Alerts.acknowledge_alert(alert, user.id)
assert acknowledged.acknowledged_at
assert acknowledged.acknowledged_by_id == user.id
end
test "resolve_alert/1 marks alert as resolved", %{device: device} do
attrs = Map.put(@valid_attrs, :device_id, device.id)
{:ok, alert} = Alerts.create_alert(attrs)
assert {:ok, resolved} = Alerts.resolve_alert(alert)
assert resolved.resolved_at
end
test "has_active_alert?/2 returns true when active alert exists", %{device: device} do
attrs = Map.put(@valid_attrs, :device_id, device.id)
{:ok, _alert} = Alerts.create_alert(attrs)
assert Alerts.has_active_alert?(device.id, :device_down) == true
assert Alerts.has_active_alert?(device.id, :device_up) == false
end
test "has_active_alert?/2 returns false for resolved alerts", %{device: device} do
attrs = Map.put(@valid_attrs, :device_id, device.id)
{:ok, alert} = Alerts.create_alert(attrs)
{:ok, _resolved} = Alerts.resolve_alert(alert)
assert Alerts.has_active_alert?(device.id, :device_down) == false
end
test "get_active_alert/2 returns active alert of specific type", %{device: device} do
attrs = Map.put(@valid_attrs, :device_id, device.id)
{:ok, alert} = Alerts.create_alert(attrs)
found = Alerts.get_active_alert(device.id, :device_down)
assert found.id == alert.id
end
test "get_active_alert/2 returns nil when no active alert exists", %{device: device} do
assert Alerts.get_active_alert(device.id, :device_down) == nil
end
test "get_active_alert/2 returns nil for resolved alerts", %{device: device} do
attrs = Map.put(@valid_attrs, :device_id, device.id)
{:ok, alert} = Alerts.create_alert(attrs)
{:ok, _resolved} = Alerts.resolve_alert(alert)
assert Alerts.get_active_alert(device.id, :device_down) == nil
end
test "count_active_alerts/1 returns count of unresolved alerts", %{
device: device,
organization: organization
} do
attrs = Map.put(@valid_attrs, :device_id, device.id)
{:ok, _alert1} = Alerts.create_alert(attrs)
{:ok, _alert2} = Alerts.create_alert(attrs)
# Create resolved alert
{:ok, resolved} = Alerts.create_alert(attrs)
{:ok, _} = Alerts.resolve_alert(resolved)
assert Alerts.count_active_alerts(organization.id) == 2
end
test "count_active_alerts/1 returns 0 when no active alerts", %{organization: organization} do
assert Alerts.count_active_alerts(organization.id) == 0
end
test "list_devices_alerts/2 respects limit parameter", %{device: device} do
attrs = Map.put(@valid_attrs, :device_id, device.id)
# Create 5 alerts
for _i <- 1..5 do
{:ok, _} = Alerts.create_alert(attrs)
end
# Request only 3
alerts = Alerts.list_devices_alerts(device.id, 3)
assert length(alerts) == 3
end
test "list_devices_alerts/2 orders by triggered_at descending", %{device: device} do
attrs = Map.put(@valid_attrs, :device_id, device.id)
{:ok, old_alert} =
Alerts.create_alert(Map.put(attrs, :triggered_at, ~U[2025-12-21 10:00:00Z]))
{:ok, new_alert} =
Alerts.create_alert(Map.put(attrs, :triggered_at, ~U[2025-12-21 12:00:00Z]))
[first, second] = Alerts.list_devices_alerts(device.id)
assert first.id == new_alert.id
assert second.id == old_alert.id
end
test "list_organization_alerts/2 with status filter active", %{
device: device,
organization: organization,
user: user
} do
attrs = Map.put(@valid_attrs, :device_id, device.id)
# Create unacknowledged alert
{:ok, active_alert} = Alerts.create_alert(attrs)
# Create acknowledged alert
{:ok, acked_alert} = Alerts.create_alert(attrs)
{:ok, _} = Alerts.acknowledge_alert(acked_alert, user.id)
# Create resolved alert
{:ok, resolved_alert} = Alerts.create_alert(attrs)
{:ok, _} = Alerts.resolve_alert(resolved_alert)
active = Alerts.list_organization_alerts(organization.id, %{"status" => "active"})
assert length(active) == 1
assert hd(active).id == active_alert.id
end
test "list_organization_alerts/2 with status filter acknowledged", %{
device: device,
organization: organization,
user: user
} do
attrs = Map.put(@valid_attrs, :device_id, device.id)
# Create acknowledged alert
{:ok, acked_alert} = Alerts.create_alert(attrs)
{:ok, _} = Alerts.acknowledge_alert(acked_alert, user.id)
# Create unacknowledged alert
{:ok, _active} = Alerts.create_alert(attrs)
acknowledged =
Alerts.list_organization_alerts(organization.id, %{"status" => "acknowledged"})
assert length(acknowledged) == 1
assert hd(acknowledged).id == acked_alert.id
end
test "list_organization_alerts/2 with status filter resolved", %{
device: device,
organization: organization
} do
attrs = Map.put(@valid_attrs, :device_id, device.id)
# Create resolved alert
{:ok, resolved_alert} = Alerts.create_alert(attrs)
{:ok, _} = Alerts.resolve_alert(resolved_alert)
# Create active alert
{:ok, _active} = Alerts.create_alert(attrs)
resolved = Alerts.list_organization_alerts(organization.id, %{"status" => "resolved"})
assert length(resolved) == 1
assert hd(resolved).id == resolved_alert.id
end
test "list_organization_alerts/2 with limit in filters map", %{
device: device,
organization: organization
} do
attrs = Map.put(@valid_attrs, :device_id, device.id)
for _i <- 1..5 do
{:ok, _} = Alerts.create_alert(attrs)
end
alerts = Alerts.list_organization_alerts(organization.id, %{"limit" => 2})
assert length(alerts) == 2
end
test "list_organization_alerts/2 defaults to limit 100", %{
device: device,
organization: organization
} do
attrs = Map.put(@valid_attrs, :device_id, device.id)
# Just verify it doesn't error with no limit specified
{:ok, _} = Alerts.create_alert(attrs)
alerts = Alerts.list_organization_alerts(organization.id, %{})
assert length(alerts) == 1
end
test "list_organization_active_alerts/1 only returns device_down alerts", %{
device: device,
organization: organization
} do
# Create device_down alert
{:ok, down_alert} =
Alerts.create_alert(%{
device_id: device.id,
alert_type: :device_down,
triggered_at: DateTime.utc_now()
})
# Create device_up alert
{:ok, _up_alert} =
Alerts.create_alert(%{
device_id: device.id,
alert_type: :device_up,
triggered_at: DateTime.utc_now()
})
active = Alerts.list_organization_active_alerts(organization.id)
assert length(active) == 1
assert hd(active).id == down_alert.id
assert hd(active).alert_type == :device_down
end
test "list_organization_active_alerts/1 preloads associations", %{
device: device,
organization: organization
} do
attrs = Map.put(@valid_attrs, :device_id, device.id)
{:ok, _alert} = Alerts.create_alert(attrs)
[alert] = Alerts.list_organization_active_alerts(organization.id)
assert Ecto.assoc_loaded?(alert.device)
assert Ecto.assoc_loaded?(alert.device.site)
end
test "get_alert!/1 preloads associations", %{device: device} do
attrs = Map.put(@valid_attrs, :device_id, device.id)
{:ok, alert} = Alerts.create_alert(attrs)
fetched = Alerts.get_alert!(alert.id)
assert Ecto.assoc_loaded?(fetched.device)
assert Ecto.assoc_loaded?(fetched.acknowledged_by)
end
test "get_alert!/1 raises when alert doesn't exist" do
assert_raise Ecto.NoResultsError, fn ->
Alerts.get_alert!(Ecto.UUID.generate())
end
end
test "get_active_alert/2 returns most recent when multiple active alerts", %{
device: device
} do
attrs = Map.put(@valid_attrs, :device_id, device.id)
{:ok, old_alert} =
Alerts.create_alert(Map.put(attrs, :triggered_at, ~U[2025-12-21 10:00:00Z]))
{:ok, new_alert} =
Alerts.create_alert(Map.put(attrs, :triggered_at, ~U[2025-12-21 12:00:00Z]))
found = Alerts.get_active_alert(device.id, :device_down)
assert found.id == new_alert.id
refute found.id == old_alert.id
end
test "count_active_alerts/1 only counts device_down alerts", %{
device: device,
organization: organization
} do
# Create device_down alert
{:ok, _} =
Alerts.create_alert(%{
device_id: device.id,
alert_type: :device_down,
triggered_at: DateTime.utc_now()
})
# Create device_up alert (should not be counted)
{:ok, _} =
Alerts.create_alert(%{
device_id: device.id,
alert_type: :device_up,
triggered_at: DateTime.utc_now()
})
assert Alerts.count_active_alerts(organization.id) == 1
end
end
end