258 lines
8.2 KiB
Elixir
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
|