defmodule AprsmeWeb.PacketsLive.CallsignViewTest do use AprsmeWeb.ConnCase import Aprsme.PacketsFixtures import Phoenix.LiveViewTest describe "mount" do test "renders the page for a callsign with no existing packets", %{conn: conn} do {:ok, _view, html} = live(conn, "/packets/NOPACKETS", on_error: :warn) assert html =~ "NOPACKETS" end test "normalizes a lowercase callsign", %{conn: conn} do {:ok, _view, html} = live(conn, "/packets/abc1xyz", on_error: :warn) assert html =~ "ABC1XYZ" end test "renders with existing packet in db", %{conn: conn} do _packet = packet_fixture(%{ sender: "LIVEPKT-1", base_callsign: "LIVEPKT", ssid: "1", received_at: DateTime.utc_now(), lat: Decimal.new("33.0"), lon: Decimal.new("-96.0") }) {:ok, _view, html} = live(conn, "/packets/LIVEPKT-1", on_error: :warn) assert html =~ "LIVEPKT-1" end end describe "live packet broadcast edge cases" do test "broadcasts exceeding 100 packets carry the oldest out of live buffer", %{conn: conn} do # Pre-seed a packet so stored=[packet], live=[]. _p = packet_fixture(%{ sender: "OVERFLOW-1", base_callsign: "OVERFLOW", ssid: "1", received_at: DateTime.utc_now(), lat: Decimal.new("33.0"), lon: Decimal.new("-96.0") }) {:ok, view, _html} = live(conn, "/packets/OVERFLOW-1", on_error: :warn) # Broadcast enough live packets to overflow the 100-packet total. for i <- 1..110 do payload = %{ sender: "OVERFLOW-1", base_callsign: "OVERFLOW", ssid: "1", received_at: DateTime.add(DateTime.utc_now(), i, :second), data_type: :position, destination: "APRS", path: "", device_identifier: "APRS", data_extended: %{symbol_table_id: "/", symbol_code: ">"} } Phoenix.PubSub.broadcast( Aprsme.PubSub, "packets:OVERFLOW-1", {:postgres_packet, payload} ) end Process.sleep(100) html = render(view) assert html =~ "OVERFLOW-1" end test "handles broadcast with iso8601 string timestamp", %{conn: conn} do {:ok, view, _html} = live(conn, "/packets/STRTIME-1", on_error: :warn) payload = %{ sender: "STRTIME-1", base_callsign: "STRTIME", ssid: "1", received_at: DateTime.to_iso8601(DateTime.utc_now()), data_type: :position, destination: "APRS", path: "", data_extended: %{symbol_table_id: "/", symbol_code: ">"} } Phoenix.PubSub.broadcast(Aprsme.PubSub, "packets:STRTIME-1", {:postgres_packet, payload}) Process.sleep(30) assert render(view) =~ "STRTIME-1" end test "ignores unknown messages without crashing", %{conn: conn} do {:ok, view, _html} = live(conn, "/packets/UNKMSG-1", on_error: :warn) send(view.pid, :some_random) Process.sleep(20) assert Process.alive?(view.pid) end end describe "live packet broadcast" do test "handles raw broadcast packet without :data key", %{conn: conn} do {:ok, view, _html} = live(conn, "/packets/DB0WUN-13") # Simulate a raw broadcast payload (as it arrives from the APRS pipeline) # This map does NOT have a :data key — only %Packet{} structs do raw_payload = %{ sender: "DB0WUN-13", data_type: :weather, destination: "APN000", path: "TCPIP*,qAC,T2EISBERG", base_callsign: "DB0WUN", ssid: "13", received_at: DateTime.utc_now(), latitude: Decimal.new("50.0001"), longitude: Decimal.new("12.1393"), temperature: 31, humidity: 89, wind_speed: 3, comment: "232/003g009t031r000p000P000b10215h89L000eMB63", device_identifier: "APN000", data_extended: %{ data_type: :weather, comment: "232/003g009t031r000p000P000b10215h89L000eMB63", latitude: Decimal.new("50.0001"), longitude: Decimal.new("12.1393"), symbol_code: "_", symbol_table_id: "/" } } Phoenix.PubSub.broadcast(Aprsme.PubSub, "packets:DB0WUN-13", {:postgres_packet, raw_payload}) # Should render without crashing html = render(view) assert html =~ "DB0WUN-13" end end end