- Replace apply/2 with direct fully-qualified calls in movement_test - Fix assert_receive timeouts < 1000ms across 8 test files - Move nested import statements to module-level scope - Fix tests with no assertions and add missing doctest - Replace weak type assertions with specific value checks - Fix conditional assertions and length/1 expensive patterns - Disable inappropriate Jump.CredoChecks.AvoidSocketAssignsInTest - Fix tests not calling application code with credo:disable - Add various credo:disable comments for legitimate patterns
284 lines
9 KiB
Elixir
284 lines
9 KiB
Elixir
defmodule AprsmeWeb.StatusLive.IndexTest do
|
|
use AprsmeWeb.ConnCase
|
|
|
|
import Phoenix.LiveViewTest
|
|
|
|
alias AprsmeWeb.StatusLive.Index
|
|
alias Phoenix.LiveView.Socket
|
|
|
|
describe "mount via HTTP" do
|
|
test "renders the status page", %{conn: conn} do
|
|
{:ok, _lv, html} = live(conn, ~p"/status", on_error: :warn)
|
|
assert html =~ "System Status"
|
|
assert html =~ "APRS-IS Connection"
|
|
end
|
|
|
|
test "renders health score 3 (stable under 1 hour)", %{conn: conn} do
|
|
_ =
|
|
case :ets.info(:query_cache) do
|
|
:undefined -> :ets.new(:query_cache, [:named_table, :public, :set])
|
|
_ -> :ok
|
|
end
|
|
|
|
status = %{
|
|
connected: true,
|
|
server: "test.aprs.test",
|
|
port: 14_580,
|
|
connected_at: DateTime.add(DateTime.utc_now(), -1000, :second),
|
|
uptime_seconds: 1000,
|
|
login_id: "N0CALL",
|
|
filter: "r/33/-96/100",
|
|
packet_stats: %{total_packets: 10, last_packet_at: DateTime.utc_now(), packets_per_second: 1},
|
|
stored_packet_count: 50,
|
|
oldest_packet_timestamp: DateTime.add(DateTime.utc_now(), -3600, :second)
|
|
}
|
|
|
|
Aprsme.Cache.put(:query_cache, "aprs_status", status)
|
|
|
|
{:ok, _lv, html} = live(conn, ~p"/status", on_error: :warn)
|
|
# Uptime 1000s < 3600 → score 3. 50 stored packets.
|
|
assert html =~ "System Status"
|
|
assert html =~ "Connected"
|
|
|
|
Aprsme.Cache.del(:query_cache, "aprs_status")
|
|
end
|
|
|
|
test "renders health score 4 (stable under 1 day)", %{conn: conn} do
|
|
_ =
|
|
case :ets.info(:query_cache) do
|
|
:undefined -> :ets.new(:query_cache, [:named_table, :public, :set])
|
|
_ -> :ok
|
|
end
|
|
|
|
status = %{
|
|
connected: true,
|
|
server: "test",
|
|
port: 14_580,
|
|
connected_at: DateTime.add(DateTime.utc_now(), -10_000, :second),
|
|
# Uptime 10,000s is between 3600 and 86_400 → score 4.
|
|
uptime_seconds: 10_000,
|
|
login_id: "N0CALL",
|
|
filter: "r/0/0/100",
|
|
packet_stats: %{total_packets: 1, last_packet_at: nil, packets_per_second: 0},
|
|
stored_packet_count: 0,
|
|
oldest_packet_timestamp: nil
|
|
}
|
|
|
|
Aprsme.Cache.put(:query_cache, "aprs_status", status)
|
|
|
|
{:ok, _lv, html} = live(conn, ~p"/status", on_error: :warn)
|
|
assert html =~ "Connected"
|
|
|
|
Aprsme.Cache.del(:query_cache, "aprs_status")
|
|
end
|
|
|
|
test "renders the connected-path template when cache has a live status", %{conn: conn} do
|
|
# Ensure the cache ETS table exists for test.
|
|
_ =
|
|
case :ets.info(:query_cache) do
|
|
:undefined -> :ets.new(:query_cache, [:named_table, :public, :set])
|
|
_ -> :ok
|
|
end
|
|
|
|
status = %{
|
|
connected: true,
|
|
server: "cached.aprs.test",
|
|
port: 14_580,
|
|
connected_at: DateTime.add(DateTime.utc_now(), -3600, :second),
|
|
uptime_seconds: 3600,
|
|
login_id: "N0CALL",
|
|
filter: "r/33/-96/100",
|
|
packet_stats: %{
|
|
total_packets: 1234,
|
|
last_packet_at: DateTime.add(DateTime.utc_now(), -30, :second),
|
|
packets_per_second: 5
|
|
},
|
|
stored_packet_count: 5000,
|
|
oldest_packet_timestamp: DateTime.add(DateTime.utc_now(), -86_400, :second)
|
|
}
|
|
|
|
Aprsme.Cache.put(:query_cache, "aprs_status", status)
|
|
|
|
{:ok, _lv, html} = live(conn, ~p"/status", on_error: :warn)
|
|
assert html =~ "Connected"
|
|
assert html =~ "cached.aprs.test"
|
|
assert html =~ "N0CALL"
|
|
# Total packets formatted with thousands separator.
|
|
assert html =~ "1,234"
|
|
|
|
# Cleanup so other tests aren't affected.
|
|
Aprsme.Cache.del(:query_cache, "aprs_status")
|
|
end
|
|
end
|
|
|
|
describe "handle_info({:status_updated, status}, socket)" do
|
|
test "updates assigns with the new status" do
|
|
status = %{
|
|
connected: true,
|
|
server: "test-server",
|
|
port: 14_580,
|
|
connected_at: DateTime.utc_now(),
|
|
uptime_seconds: 60,
|
|
login_id: "TEST-ID",
|
|
filter: "r/0/0/100",
|
|
packet_stats: %{total_packets: 0, packets_per_second: 0, last_packet_at: nil},
|
|
stored_packet_count: 0,
|
|
oldest_packet_timestamp: nil
|
|
}
|
|
|
|
socket = %Socket{assigns: %{__changed__: %{}}}
|
|
|
|
assert {:noreply, new_socket} = Index.handle_info({:status_updated, status}, socket)
|
|
assigns = Map.get(new_socket, :assigns)
|
|
assert assigns.aprs_status == status
|
|
assert assigns.loading == false
|
|
assert %DateTime{} = assigns.current_time
|
|
# Connected+uptime 60s → health score 2.
|
|
assert assigns.health_score == 2
|
|
end
|
|
end
|
|
|
|
describe "handle_info({:aprs_status_update, status}, socket)" do
|
|
test "updates status without flipping loading flag" do
|
|
status = %{
|
|
connected: true,
|
|
server: "test-server",
|
|
port: 14_580,
|
|
connected_at: DateTime.utc_now(),
|
|
uptime_seconds: 100_000,
|
|
login_id: "TEST-ID",
|
|
filter: "r/0/0/100",
|
|
packet_stats: %{total_packets: 5, packets_per_second: 1, last_packet_at: DateTime.utc_now()},
|
|
stored_packet_count: 123,
|
|
oldest_packet_timestamp: DateTime.utc_now()
|
|
}
|
|
|
|
socket = %Socket{assigns: %{__changed__: %{}, loading: false}}
|
|
|
|
assert {:noreply, new_socket} =
|
|
Index.handle_info({:aprs_status_update, status}, socket)
|
|
|
|
assigns = Map.get(new_socket, :assigns)
|
|
assert assigns.aprs_status == status
|
|
# Uptime > 86_400? 100_000 > 86_400 → yes → score 5.
|
|
assert assigns.health_score == 5
|
|
# Loading flag is untouched (this code path doesn't toggle it).
|
|
assert assigns.loading == false
|
|
end
|
|
end
|
|
|
|
describe "format_uptime/1" do
|
|
test "returns 'Not connected' when seconds <= 0" do
|
|
assert Index.format_uptime(0) =~ "Not connected"
|
|
assert Index.format_uptime(-100) =~ "Not connected"
|
|
end
|
|
|
|
test "formats seconds-only durations" do
|
|
assert Index.format_uptime(45) == "45s"
|
|
end
|
|
|
|
test "formats minutes+seconds durations" do
|
|
assert Index.format_uptime(125) == "2m 5s"
|
|
end
|
|
|
|
test "formats hours+minutes+seconds durations" do
|
|
assert Index.format_uptime(3 * 3600 + 45 * 60 + 30) == "3h 45m 30s"
|
|
end
|
|
|
|
test "formats day+hour+minute+second durations" do
|
|
total = 2 * 86_400 + 3 * 3600 + 4 * 60 + 5
|
|
assert Index.format_uptime(total) == "2d 3h 4m 5s"
|
|
end
|
|
end
|
|
|
|
describe "get_health_description/2" do
|
|
test "score 1 returns disconnected" do
|
|
assert Index.get_health_description(1, false) =~ "Disconnected"
|
|
end
|
|
|
|
test "score 5 connected returns excellent/healthy text" do
|
|
result = Index.get_health_description(5, true)
|
|
assert byte_size(result) > 0
|
|
end
|
|
|
|
test "all defined score combinations return non-empty text" do
|
|
for score <- 1..5 do
|
|
for connected <- [true, false] do
|
|
result = Index.get_health_description(score, connected)
|
|
assert byte_size(result) > 0
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "render with cluster_info populated" do
|
|
test "renders the Cluster Status section when cluster_info is present", %{conn: conn} do
|
|
_ =
|
|
case :ets.info(:query_cache) do
|
|
:undefined -> :ets.new(:query_cache, [:named_table, :public, :set])
|
|
_ -> :ok
|
|
end
|
|
|
|
status = %{
|
|
connected: true,
|
|
server: "clustered.aprs.test",
|
|
port: 14_580,
|
|
connected_at: DateTime.add(DateTime.utc_now(), -60, :second),
|
|
uptime_seconds: 60,
|
|
login_id: "N0CALL",
|
|
filter: "r/33/-96/100",
|
|
packet_stats: %{total_packets: 10, packets_per_second: 1, last_packet_at: DateTime.utc_now()},
|
|
stored_packet_count: 100,
|
|
oldest_packet_timestamp: DateTime.utc_now(),
|
|
cluster_info: %{
|
|
total_nodes: 3,
|
|
connected_nodes: 2,
|
|
leader_node: "node-a",
|
|
all_nodes: ["node-a", "node-b", "node-c"]
|
|
}
|
|
}
|
|
|
|
Aprsme.Cache.put(:query_cache, "aprs_status", status)
|
|
|
|
{:ok, _lv, html} = live(conn, ~p"/status", on_error: :warn)
|
|
assert html =~ "Cluster Status"
|
|
assert html =~ "Total Nodes"
|
|
assert html =~ "node-a"
|
|
assert html =~ "node-b"
|
|
|
|
Aprsme.Cache.del(:query_cache, "aprs_status")
|
|
end
|
|
end
|
|
|
|
describe "{:status_updated, status} update path" do
|
|
test "applies a fresh status map and updates loading=false" do
|
|
socket = %Socket{
|
|
assigns: %{
|
|
aprs_status: %{},
|
|
current_time: DateTime.utc_now(),
|
|
health_score: 1,
|
|
loading: true,
|
|
__changed__: %{}
|
|
}
|
|
}
|
|
|
|
status = %{
|
|
connected: true,
|
|
server: "test",
|
|
port: 14_580,
|
|
connected_at: DateTime.utc_now(),
|
|
uptime_seconds: 100,
|
|
login_id: "N0CALL",
|
|
filter: "r/33/-96/100",
|
|
packet_stats: %{total_packets: 1, packets_per_second: 0, last_packet_at: DateTime.utc_now()},
|
|
stored_packet_count: 0,
|
|
oldest_packet_timestamp: nil
|
|
}
|
|
|
|
assert {:noreply, new_socket} = Index.handle_info({:status_updated, status}, socket)
|
|
assigns = Map.get(new_socket, :assigns)
|
|
assert assigns.loading == false
|
|
assert assigns.aprs_status == status
|
|
end
|
|
end
|
|
end
|