From e3dfdf03bae28d0158b4de98dfd0d13a02d67ed2 Mon Sep 17 00:00:00 2001 From: Graham McIntire Date: Thu, 23 Apr 2026 16:55:59 -0500 Subject: [PATCH] test: cover PacketConsumer init and process_batch callbacks --- test/aprsme/packet_consumer_test.exs | 70 +++++++++++ test/aprsme_web/live/map_live/index_test.exs | 118 +++++++++++++++++++ 2 files changed, 188 insertions(+) diff --git a/test/aprsme/packet_consumer_test.exs b/test/aprsme/packet_consumer_test.exs index 6fcbd9f..7ba94fb 100644 --- a/test/aprsme/packet_consumer_test.exs +++ b/test/aprsme/packet_consumer_test.exs @@ -7,6 +7,76 @@ defmodule Aprsme.PacketConsumerTest do alias Aprsme.PacketConsumer alias Aprsme.StreamingPacketsPubSub + describe "init/1" do + test "returns a :consumer tuple with default configuration" do + # Start an isolated consumer with no subscription to avoid contaminating + # the global packet pipeline. We skip the auto-subscribe by using :manual. + assert {:consumer, state, opts} = + PacketConsumer.init( + batch_size: 50, + batch_timeout: 500, + max_batch_size: 123, + subscribe_to: [] + ) + + assert state.batch == [] + assert state.batch_length == 0 + assert state.batch_size == 50 + assert state.batch_timeout == 500 + assert state.max_batch_size == 123 + assert is_reference(state.timer) + assert opts[:subscribe_to] == [] + + # Cancel the timer to avoid stray :process_batch messages later. + Process.cancel_timer(state.timer) + end + + test "applies default batch sizing when not provided" do + assert {:consumer, state, _opts} = PacketConsumer.init(subscribe_to: []) + assert state.batch_size == 100 + assert state.batch_timeout == 1000 + assert state.max_batch_size == 1000 + Process.cancel_timer(state.timer) + end + end + + describe "handle_info(:process_batch, state)" do + test "empty batch just restarts the timer" do + {:consumer, state, _} = PacketConsumer.init(subscribe_to: []) + Process.cancel_timer(state.timer) + state = %{state | timer: nil} + + assert {:noreply, [], new_state} = PacketConsumer.handle_info(:process_batch, state) + assert new_state.batch == [] + assert new_state.batch_length == 0 + assert is_reference(new_state.timer) + Process.cancel_timer(new_state.timer) + end + + test "non-empty batch processes and then resets timer" do + {:consumer, state, _} = PacketConsumer.init(subscribe_to: []) + Process.cancel_timer(state.timer) + + # A packet with minimal required fields. process_batch will try to + # insert — that may fail, but handle_info should still return :noreply. + packet = %{ + sender: "UNIT-1", + base_callsign: "UNIT", + ssid: "1", + raw_packet: "UNIT-1>APRS:>test", + received_at: DateTime.utc_now() + } + + state = %{state | batch: [packet], batch_length: 1, timer: nil} + + assert {:noreply, [], new_state} = PacketConsumer.handle_info(:process_batch, state) + assert new_state.batch == [] + assert new_state.batch_length == 0 + assert is_reference(new_state.timer) + Process.cancel_timer(new_state.timer) + end + end + describe "object broadcast with correct identifier" do test "broadcasts object packets using object_name instead of sender" do # Subscribe to packets in test bounds diff --git a/test/aprsme_web/live/map_live/index_test.exs b/test/aprsme_web/live/map_live/index_test.exs index 3688091..bd83a7e 100644 --- a/test/aprsme_web/live/map_live/index_test.exs +++ b/test/aprsme_web/live/map_live/index_test.exs @@ -291,5 +291,123 @@ defmodule AprsmeWeb.MapLive.IndexTest do send(view.pid, :cleanup_old_packets) assert render(view) =~ "aprs-map" end + + test "responds to :reload_historical_packets", %{conn: conn} do + {:ok, view, _html} = live(conn, "/", on_error: :warn) + send(view.pid, :reload_historical_packets) + assert render(view) =~ "aprs-map" + end + + test "responds to a postgres_packet message", %{conn: conn} do + {:ok, view, _html} = live(conn, "/", on_error: :warn) + + packet = %{ + id: "123", + sender: "TEST-1", + base_callsign: "TEST", + ssid: "1", + lat: 35.0, + lon: -90.0, + symbol_table_id: "/", + symbol_code: ">", + comment: "test packet", + received_at: DateTime.utc_now(), + path: "WIDE1-1" + } + + send(view.pid, {:postgres_packet, packet}) + assert render(view) =~ "aprs-map" + end + + test "responds to a spatial_packet message", %{conn: conn} do + {:ok, view, _html} = live(conn, "/", on_error: :warn) + + packet = %{ + id: "789", + sender: "SPATIAL", + base_callsign: "SPATIAL", + ssid: "0", + lat: 41.0, + lon: -97.0, + symbol_table_id: "/", + symbol_code: "-", + comment: nil, + received_at: DateTime.utc_now(), + path: "" + } + + send(view.pid, {:spatial_packet, packet}) + assert render(view) =~ "aprs-map" + end + + test "responds to a packet_batch message", %{conn: conn} do + {:ok, view, _html} = live(conn, "/", on_error: :warn) + + packets = + for i <- 1..3 do + %{ + id: "batch-#{i}", + sender: "BATCH-#{i}", + base_callsign: "BATCH", + ssid: "#{i}", + lat: 40.0 + i * 0.1, + lon: -98.0 + i * 0.1, + symbol_table_id: "/", + symbol_code: ">", + comment: "batch #{i}", + received_at: DateTime.utc_now(), + path: "" + } + end + + send(view.pid, {:packet_batch, packets}) + assert render(view) =~ "aprs-map" + end + + test "responds to a streaming_packet message", %{conn: conn} do + {:ok, view, _html} = live(conn, "/", on_error: :warn) + + packet = %{ + id: "stream-1", + sender: "STREAM", + base_callsign: "STREAM", + ssid: "0", + lat: 39.0, + lon: -98.0, + symbol_table_id: "/", + symbol_code: ">", + comment: nil, + received_at: DateTime.utc_now(), + path: "" + } + + send(view.pid, {:streaming_packet, packet}) + assert render(view) =~ "aprs-map" + end + + test "responds to a drain_connections message", %{conn: conn} do + {:ok, view, _html} = live(conn, "/", on_error: :warn) + send(view.pid, {:drain_connections, 5}) + assert render(view) =~ "aprs-map" + end + + test "responds to load_rf_path_station_packets message", %{conn: conn} do + {:ok, view, _html} = live(conn, "/", on_error: :warn) + send(view.pid, {:load_rf_path_station_packets, ["K1ABC", "W5XYZ"]}) + assert render(view) =~ "aprs-map" + end + + test "responds to show_error message", %{conn: conn} do + {:ok, view, _html} = live(conn, "/", on_error: :warn) + send(view.pid, {:show_error, "something went wrong"}) + assert render(view) + end + + test "responds to historical_loading_timeout message", %{conn: conn} do + {:ok, view, _html} = live(conn, "/", on_error: :warn) + # Use a stale generation — should skip without side effects. + send(view.pid, {:historical_loading_timeout, -999}) + assert render(view) =~ "aprs-map" + end end end