towerops/test/towerops/dashboard_test.exs
Graham McIntire 20f5f48372
feat: command center dashboard with unified insights and contextual enrichment
Transform the dashboard into a single-pane-of-glass command center that
blends operational metrics with business context from Gaiia subscriber data.

- Extend insight schema with multi-source support (snmp, gaiia, system)
- Add Oban workers for automated insight generation (device health, system, gaiia)
- New Dashboard context with health score computation and site summaries
- Rewrite dashboard LiveView with health overview, alert/insight feeds, site grid
- Add source filter pills for insights with URL state management
- Add metrics bar to site detail pages (device count, alerts, subscribers, MRR)
- Add subscriber/MRR context to alert list site links
- Add subscriber badges to device list site headers
- Real-time PubSub updates for alerts on dashboard
2026-02-13 14:52:49 -06:00

174 lines
5.2 KiB
Elixir

defmodule Towerops.DashboardTest do
use Towerops.DataCase, async: true
import Towerops.AccountsFixtures
import Towerops.DevicesFixtures
import Towerops.OrganizationsFixtures
alias Towerops.Dashboard
alias Towerops.Gaiia
setup do
user = user_fixture()
org = organization_fixture(user.id)
%{org: org, user: user}
end
describe "get_dashboard_summary/1" do
test "returns correct structure with empty org", %{org: org} do
summary = Dashboard.get_dashboard_summary(org.id)
assert is_map(summary)
assert Map.has_key?(summary, :health_score)
assert Map.has_key?(summary, :devices)
assert Map.has_key?(summary, :alerts)
assert Map.has_key?(summary, :subscribers)
assert Map.has_key?(summary, :insights)
end
test "returns device counts", %{org: org} do
device = device_fixture(%{organization_id: org.id})
Towerops.Devices.update_device_status(device, :up)
summary = Dashboard.get_dashboard_summary(org.id)
assert summary.devices.total == 1
assert summary.devices.up == 1
assert summary.devices.down == 0
end
test "returns alert count", %{org: org} do
device = device_fixture(%{organization_id: org.id})
{:ok, _alert} =
Towerops.Alerts.create_alert(%{
device_id: device.id,
alert_type: :device_down,
triggered_at: DateTime.utc_now(),
message: "Device is down"
})
summary = Dashboard.get_dashboard_summary(org.id)
assert summary.alerts.active_count == 1
end
test "returns subscriber totals from Gaiia", %{org: org} do
{:ok, _} =
Gaiia.upsert_network_site(org.id, %{
gaiia_id: "ns-dash",
name: "Tower",
account_count: 100,
total_mrr: Decimal.new("5000.00")
})
summary = Dashboard.get_dashboard_summary(org.id)
assert summary.subscribers.total == 100
assert Decimal.equal?(summary.subscribers.total_mrr, Decimal.new("5000.00"))
end
test "returns zero subscribers when no Gaiia data", %{org: org} do
summary = Dashboard.get_dashboard_summary(org.id)
assert summary.subscribers.total == 0
end
test "returns insight counts by urgency", %{org: org} do
summary = Dashboard.get_dashboard_summary(org.id)
assert summary.insights.critical == 0
assert summary.insights.warning == 0
assert summary.insights.info == 0
end
end
describe "compute_health_score/1" do
test "returns 100 when all devices up, no alerts", %{org: org} do
device = device_fixture(%{organization_id: org.id})
Towerops.Devices.update_device_status(device, :up)
score = Dashboard.compute_health_score(org.id)
assert score == 100
end
test "returns lower score when devices down", %{org: org} do
d1 = device_fixture(%{organization_id: org.id})
d2 = device_fixture(%{organization_id: org.id})
Towerops.Devices.update_device_status(d1, :up)
Towerops.Devices.update_device_status(d2, :down)
score = Dashboard.compute_health_score(org.id)
assert score < 100
assert score > 0
end
test "returns 100 when no devices exist", %{org: org} do
score = Dashboard.compute_health_score(org.id)
assert score == 100
end
test "penalizes for critical alerts", %{org: org} do
device = device_fixture(%{organization_id: org.id})
Towerops.Devices.update_device_status(device, :up)
{:ok, _} =
Towerops.Alerts.create_alert(%{
device_id: device.id,
alert_type: :device_down,
triggered_at: DateTime.utc_now(),
message: "Device down"
})
score = Dashboard.compute_health_score(org.id)
assert score < 100
end
end
describe "get_org_subscriber_summary/1" do
test "returns totals from Gaiia network sites", %{org: org} do
{:ok, _} =
Gaiia.upsert_network_site(org.id, %{
gaiia_id: "ns-sub1",
name: "Tower A",
account_count: 50,
total_mrr: Decimal.new("2500.00")
})
{:ok, _} =
Gaiia.upsert_network_site(org.id, %{
gaiia_id: "ns-sub2",
name: "Tower B",
account_count: 30,
total_mrr: Decimal.new("1500.00")
})
result = Dashboard.get_org_subscriber_summary(org.id)
assert result.total == 80
assert Decimal.equal?(result.total_mrr, Decimal.new("4000.00"))
end
test "returns zeros when no Gaiia data", %{org: org} do
result = Dashboard.get_org_subscriber_summary(org.id)
assert result.total == 0
assert Decimal.equal?(result.total_mrr, Decimal.new(0))
end
end
describe "list_site_summaries/1" do
test "returns enriched site data", %{org: org} do
{:ok, site} =
Towerops.Sites.create_site(%{
name: "Test Tower",
organization_id: org.id
})
device = device_fixture(%{organization_id: org.id, site_id: site.id})
Towerops.Devices.update_device_status(device, :up)
summaries = Dashboard.list_site_summaries(org.id)
assert summaries != []
summary = Enum.find(summaries, &(&1.site_id == site.id))
assert summary.name == "Test Tower"
assert summary.device_count >= 1
end
end
end