towerops/test/towerops_web/live/alert_live/index_helpers_test.exs

258 lines
8.2 KiB
Elixir

defmodule ToweropsWeb.AlertLive.IndexHelpersTest do
use ExUnit.Case, async: true
alias ToweropsWeb.AlertLive.Index
defp now, do: DateTime.utc_now()
defp ago(minutes), do: DateTime.add(now(), -minutes * 60, :second)
describe "filter_alerts/2" do
setup do
alerts = [
%{alert_type: "device_down", resolved_at: nil, triggered_at: ago(5)},
%{alert_type: "device_down", resolved_at: ago(1), triggered_at: ago(60)},
%{alert_type: "cpu", resolved_at: nil, triggered_at: ago(2)}
]
%{alerts: alerts}
end
test "all returns everything", %{alerts: alerts} do
assert alerts == Index.filter_alerts(alerts, "all")
assert alerts == Index.filter_alerts(alerts, "unknown_filter")
end
test "critical keeps only unresolved device_down", %{alerts: alerts} do
[alert] = Index.filter_alerts(alerts, "critical")
assert alert.alert_type == "device_down"
assert is_nil(alert.resolved_at)
end
test "unresolved keeps alerts without resolved_at", %{alerts: alerts} do
results = Index.filter_alerts(alerts, "unresolved")
assert length(results) == 2
assert Enum.all?(results, &is_nil(&1.resolved_at))
end
test "resolved keeps alerts with resolved_at", %{alerts: alerts} do
[alert] = Index.filter_alerts(alerts, "resolved")
assert alert.resolved_at
end
end
describe "severity_weight/1" do
test "unresolved device_down gets weight 0" do
assert 0 == Index.severity_weight(%{alert_type: "device_down", resolved_at: nil})
end
test "resolved device_down gets weight 1" do
assert 1 ==
Index.severity_weight(%{alert_type: "device_down", resolved_at: DateTime.utc_now()})
end
test "other alert types get weight 2" do
assert 2 == Index.severity_weight(%{alert_type: "cpu", resolved_at: nil})
assert 2 == Index.severity_weight(%{alert_type: "link_down", resolved_at: nil})
end
end
describe "sort_alerts/3" do
test "sort by age puts oldest first" do
alerts = [
%{triggered_at: ago(5)},
%{triggered_at: ago(60)},
%{triggered_at: ago(30)}
]
sorted = Index.sort_alerts(alerts, "age", %{})
ts = Enum.map(sorted, & &1.triggered_at)
assert ts == Enum.sort(ts, {:asc, DateTime})
end
test "default sort puts unresolved device_down first" do
alerts = [
%{alert_type: "cpu", resolved_at: nil, triggered_at: ago(1)},
%{alert_type: "device_down", resolved_at: nil, triggered_at: ago(60)},
%{alert_type: "device_down", resolved_at: DateTime.utc_now(), triggered_at: ago(30)}
]
[first | _] = Index.sort_alerts(alerts, "severity", %{})
assert first.alert_type == "device_down"
assert is_nil(first.resolved_at)
end
test "sort by impact puts highest subscriber count first" do
alert_a = %{
gaiia_impact: %{"total_subscribers" => 5},
device: %{site_id: "s1"},
alert_type: "x",
resolved_at: nil,
triggered_at: ago(1)
}
alert_b = %{
gaiia_impact: %{"total_subscribers" => 100},
device: %{site_id: "s2"},
alert_type: "x",
resolved_at: nil,
triggered_at: ago(1)
}
alert_c = %{gaiia_impact: nil, device: %{site_id: "s1"}, alert_type: "x", resolved_at: nil, triggered_at: ago(1)}
[first, second, third] = Index.sort_alerts([alert_a, alert_b, alert_c], "impact", %{})
assert first.gaiia_impact["total_subscribers"] == 100
assert second.gaiia_impact["total_subscribers"] == 5
assert is_nil(third.gaiia_impact)
end
end
describe "get_site_subscriber_count/1" do
test "integer account_count returns count" do
assert 42 == Index.get_site_subscriber_count(%{account_count: 42})
end
test "non-integer or missing returns 0" do
assert 0 == Index.get_site_subscriber_count(%{account_count: nil})
assert 0 == Index.get_site_subscriber_count(%{})
assert 0 == Index.get_site_subscriber_count(nil)
end
end
describe "get_subscriber_count/2" do
test "uses gaiia_impact first if available" do
alert = %{
gaiia_impact: %{"total_subscribers" => 100},
device: %{site_id: "s1"}
}
assert 100 == Index.get_subscriber_count(alert, %{"s1" => %{account_count: 50}})
end
test "falls back to site-level subscribers" do
alert = %{gaiia_impact: nil, device: %{site_id: "s1"}}
assert 50 == Index.get_subscriber_count(alert, %{"s1" => %{account_count: 50}})
end
test "returns 0 when no data available" do
alert = %{gaiia_impact: nil, device: %{site_id: nil}}
assert 0 == Index.get_subscriber_count(alert, %{})
end
end
describe "severity_color/1" do
test "resolved alert is gray" do
assert "gray" == Index.severity_color(%{resolved_at: DateTime.utc_now()})
end
test "recent device_down is yellow" do
assert "yellow" == Index.severity_color(%{alert_type: "device_down", resolved_at: nil, triggered_at: ago(5)})
end
test "device_down >15m is orange" do
assert "orange" ==
Index.severity_color(%{alert_type: "device_down", resolved_at: nil, triggered_at: ago(20)})
end
test "device_down >60m is red" do
assert "red" ==
Index.severity_color(%{alert_type: "device_down", resolved_at: nil, triggered_at: ago(90)})
end
test "other type is green" do
assert "green" == Index.severity_color(%{alert_type: "cpu", resolved_at: nil, triggered_at: ago(1)})
end
end
describe "age_severity_color/1" do
test "> 60 → red" do
assert "red" == Index.age_severity_color(61)
assert "red" == Index.age_severity_color(500)
end
test "> 15 && <= 60 → orange" do
assert "orange" == Index.age_severity_color(16)
assert "orange" == Index.age_severity_color(60)
end
test "<= 15 → yellow" do
assert "yellow" == Index.age_severity_color(0)
assert "yellow" == Index.age_severity_color(15)
assert "yellow" == Index.age_severity_color(-5)
end
end
describe "format_age_minutes/1" do
test "< 1 returns 'just now'" do
assert "just now" == Index.format_age_minutes(0)
assert "just now" == Index.format_age_minutes(-3)
end
test "< 60 returns minutes" do
assert "5m ago" == Index.format_age_minutes(5)
assert "59m ago" == Index.format_age_minutes(59)
end
test "< 1440 returns hours" do
assert "1h ago" == Index.format_age_minutes(60)
assert "23h ago" == Index.format_age_minutes(1439)
end
test ">= 1440 returns days" do
assert "1d ago" == Index.format_age_minutes(1440)
assert "7d ago" == Index.format_age_minutes(7 * 1440 + 100)
end
end
describe "format_duration_minutes/1" do
test "< 1 returns <1m" do
assert "<1m" == Index.format_duration_minutes(0)
end
test "< 60 returns m" do
assert "15m" == Index.format_duration_minutes(15)
end
test "< 1440 returns Xh Ym" do
assert "1h 5m" == Index.format_duration_minutes(65)
assert "2h 0m" == Index.format_duration_minutes(120)
end
test ">= 1440 returns Xd Yh" do
assert "1d 2h" == Index.format_duration_minutes(1440 + 120)
assert "3d 0h" == Index.format_duration_minutes(3 * 1440)
end
end
describe "age_text/1" do
test "combines triggered_at with format_age_minutes" do
alert = %{triggered_at: ago(30)}
assert Index.age_text(alert) =~ "m ago"
end
end
describe "duration_text/1" do
test "for unresolved alerts uses current time" do
alert = %{triggered_at: ago(5), resolved_at: nil}
assert Index.duration_text(alert) =~ "m"
end
test "for resolved alerts uses resolved_at" do
alert = %{triggered_at: ago(60), resolved_at: ago(30)}
assert Index.duration_text(alert) =~ "m"
end
end
describe "format_number/1" do
test "adds commas every 3 digits" do
assert "1,000" == Index.format_number(1_000)
assert "1,000,000" == Index.format_number(1_000_000)
assert "42" == Index.format_number(42)
end
test "non-integer passes through to_string" do
assert "3.14" == Index.format_number(3.14)
assert "foo" == Index.format_number("foo")
end
end
end