Promoted pure presentation and utility helpers from `defp` to `def @doc false` across ~20 LiveViews, Oban workers, and sync modules so they're reachable from unit tests. Refactored several `cond` blocks into idiomatic function heads with guards. Added ~250 new test cases in new files under test/towerops and test/towerops_web, including DB-backed tests for CnMaestro.Sync and AlertNotificationWorker, and removed dead LiveView tab components and CapacityLive (no callers anywhere in lib/test). Configured mix.exs test_coverage.ignore_modules to exclude vendored third-party code (SnmpKit, protobuf-generated Towerops.Agent.*, Absinthe GraphQL types, Phoenix HTML modules, Inspect protocol impls) from coverage calculations — these are not our project code. Coverage: 66.93% → 70.09%. Full suite: 10,127 tests, 0 failures.
225 lines
6.8 KiB
Elixir
225 lines
6.8 KiB
Elixir
defmodule ToweropsWeb.TraceLive.IndexHelpersTest do
|
|
use ExUnit.Case, async: true
|
|
|
|
alias ToweropsWeb.TraceLive.Index
|
|
|
|
describe "type_badge_class/1 + type_badge_label/1" do
|
|
test "known types" do
|
|
for {type, class, label} <- [
|
|
{:account, "badge-primary", "Account"},
|
|
{:inventory_item, "badge-secondary", "Inventory"},
|
|
{:site, "badge-warning", "Site"},
|
|
{:device, "badge-accent", "Device"},
|
|
{:access_point, "badge-info", "AP"}
|
|
] do
|
|
assert class == Index.type_badge_class(type)
|
|
assert label == Index.type_badge_label(type)
|
|
end
|
|
end
|
|
|
|
test "unknown falls through to ghost/Unknown" do
|
|
assert "badge-ghost" == Index.type_badge_class(:other)
|
|
assert "Unknown" == Index.type_badge_label(:other)
|
|
end
|
|
end
|
|
|
|
describe "status_badge_class/1" do
|
|
test "active/suspended/cancelled" do
|
|
assert "badge-success" == Index.status_badge_class("active")
|
|
assert "badge-warning" == Index.status_badge_class("suspended")
|
|
assert "badge-error" == Index.status_badge_class("cancelled")
|
|
end
|
|
|
|
test "unknown is ghost" do
|
|
assert "badge-ghost" == Index.status_badge_class(nil)
|
|
assert "badge-ghost" == Index.status_badge_class("other")
|
|
end
|
|
end
|
|
|
|
describe "device_status_color/1 + device_status_dot/1" do
|
|
test "up/down/other" do
|
|
assert String.contains?(Index.device_status_color(:up), "green")
|
|
assert String.contains?(Index.device_status_color(:down), "red")
|
|
assert String.contains?(Index.device_status_color(nil), "gray")
|
|
|
|
assert "bg-green-500" == Index.device_status_dot(:up)
|
|
assert "bg-red-500" == Index.device_status_dot(:down)
|
|
assert "bg-gray-400" == Index.device_status_dot(:other)
|
|
end
|
|
end
|
|
|
|
describe "format_speed/1" do
|
|
test "nil" do
|
|
assert "—" == Index.format_speed(nil)
|
|
end
|
|
|
|
test "< 1000 Mbps stays as Mbps" do
|
|
assert "500 Mbps" == Index.format_speed(500)
|
|
end
|
|
|
|
test ">= 1000 Mbps becomes Gbps" do
|
|
assert "1.5 Gbps" == Index.format_speed(1500)
|
|
end
|
|
|
|
test "non-numeric passes through" do
|
|
assert "" == Index.format_speed("")
|
|
end
|
|
end
|
|
|
|
describe "format_relative_time/1" do
|
|
test "nil returns em-dash" do
|
|
assert "—" == Index.format_relative_time(nil)
|
|
end
|
|
|
|
test "returns relative string for past datetime" do
|
|
dt = DateTime.add(DateTime.utc_now(), -120, :second)
|
|
result = Index.format_relative_time(dt)
|
|
assert String.contains?(result, "m") or result == "just now"
|
|
end
|
|
end
|
|
|
|
describe "format_score/1 + format_pct/1 + format_ms/1" do
|
|
test "nil returns em-dash" do
|
|
assert "—" == Index.format_score(nil)
|
|
assert "—" == Index.format_pct(nil)
|
|
assert "—" == Index.format_ms(nil)
|
|
end
|
|
|
|
test "numeric formatted with 1 decimal" do
|
|
assert "50.0" == Index.format_score(50)
|
|
assert "75.5%" == Index.format_pct(75.5)
|
|
assert "12.3 ms" == Index.format_ms(12.345)
|
|
end
|
|
end
|
|
|
|
describe "format_throughput/1" do
|
|
test "nil" do
|
|
assert "—" == Index.format_throughput(nil)
|
|
end
|
|
|
|
test "< 1000 Mbps" do
|
|
assert "100.0 Mbps" == Index.format_throughput(100)
|
|
end
|
|
|
|
test ">= 1000 becomes Gbps" do
|
|
assert "1.5 Gbps" == Index.format_throughput(1500)
|
|
end
|
|
end
|
|
|
|
describe "format_alert_type/1" do
|
|
test "nil returns em-dash" do
|
|
assert "—" == Index.format_alert_type(nil)
|
|
end
|
|
|
|
test "humanizes snake_case" do
|
|
assert "Device down" == Index.format_alert_type("device_down")
|
|
assert "Packet loss" == Index.format_alert_type("packet_loss")
|
|
end
|
|
|
|
test "non-binary stringified" do
|
|
assert "42" == Index.format_alert_type(42)
|
|
end
|
|
end
|
|
|
|
describe "score_quality/1 + score_color/1" do
|
|
test "nil is unknown/gray" do
|
|
assert :unknown == Index.score_quality(nil)
|
|
assert "text-gray-400" == Index.score_color(nil)
|
|
end
|
|
|
|
test ">= 80 is good/green" do
|
|
assert :good == Index.score_quality(90)
|
|
assert String.contains?(Index.score_color(90), "green")
|
|
end
|
|
|
|
test "50-80 is warning/yellow" do
|
|
assert :warning == Index.score_quality(60)
|
|
assert String.contains?(Index.score_color(60), "yellow")
|
|
end
|
|
|
|
test "< 50 is bad/red" do
|
|
assert :bad == Index.score_quality(30)
|
|
assert String.contains?(Index.score_color(30), "red")
|
|
end
|
|
end
|
|
|
|
describe "latency_color/1, jitter_color/1, loss_color/1" do
|
|
test "all return gray for nil" do
|
|
assert "text-gray-400" == Index.latency_color(nil)
|
|
assert "text-gray-400" == Index.jitter_color(nil)
|
|
assert "text-gray-400" == Index.loss_color(nil)
|
|
end
|
|
|
|
test "latency thresholds" do
|
|
assert String.contains?(Index.latency_color(10), "green")
|
|
assert String.contains?(Index.latency_color(50), "yellow")
|
|
assert String.contains?(Index.latency_color(200), "red")
|
|
end
|
|
|
|
test "jitter thresholds" do
|
|
assert String.contains?(Index.jitter_color(5), "green")
|
|
assert String.contains?(Index.jitter_color(20), "yellow")
|
|
assert String.contains?(Index.jitter_color(50), "red")
|
|
end
|
|
|
|
test "loss thresholds" do
|
|
assert String.contains?(Index.loss_color(0), "green")
|
|
assert String.contains?(Index.loss_color(3), "yellow")
|
|
assert String.contains?(Index.loss_color(10), "red")
|
|
end
|
|
end
|
|
|
|
describe "alert_dot/1" do
|
|
test "critical severity" do
|
|
assert "bg-red-500" == Index.alert_dot(%{severity: "critical"})
|
|
end
|
|
|
|
test "warning severity" do
|
|
assert "bg-yellow-500" == Index.alert_dot(%{severity: "warning"})
|
|
end
|
|
|
|
test "alert_type down" do
|
|
assert "bg-red-500" == Index.alert_dot(%{alert_type: "down"})
|
|
end
|
|
|
|
test "fallback" do
|
|
assert "bg-orange-400" == Index.alert_dot(%{})
|
|
end
|
|
end
|
|
|
|
describe "urgency_dot/1" do
|
|
test "maps known values" do
|
|
assert "bg-red-500" == Index.urgency_dot("critical")
|
|
assert "bg-orange-500" == Index.urgency_dot("high")
|
|
assert "bg-yellow-500" == Index.urgency_dot("medium")
|
|
assert "bg-blue-400" == Index.urgency_dot("other")
|
|
end
|
|
end
|
|
|
|
describe "airtime_quality/1" do
|
|
test "nil is unknown" do
|
|
assert :unknown == Index.airtime_quality(nil)
|
|
end
|
|
|
|
test "thresholds" do
|
|
assert :good == Index.airtime_quality(40)
|
|
assert :warning == Index.airtime_quality(70)
|
|
assert :bad == Index.airtime_quality(90)
|
|
end
|
|
end
|
|
|
|
describe "safe_type/1" do
|
|
test "maps known strings to atoms" do
|
|
assert :account == Index.safe_type("account")
|
|
assert :inventory_item == Index.safe_type("inventory_item")
|
|
assert :site == Index.safe_type("site")
|
|
assert :device == Index.safe_type("device")
|
|
assert :access_point == Index.safe_type("access_point")
|
|
end
|
|
|
|
test "unknown defaults to :account" do
|
|
assert :account == Index.safe_type("garbage")
|
|
assert :account == Index.safe_type(nil)
|
|
end
|
|
end
|
|
end
|