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) assert new_socket.assigns.aprs_status == status assert new_socket.assigns.loading == false assert %DateTime{} = new_socket.assigns.current_time # Connected+uptime 60s → health score 2. assert new_socket.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) assert new_socket.assigns.aprs_status == status # Uptime > 86_400? 100_000 > 86_400 → yes → score 5. assert new_socket.assigns.health_score == 5 # Loading flag is untouched (this code path doesn't toggle it). assert new_socket.assigns.loading == false 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 end