From 039ee83ecbe5c28d690a8d5769cec478a254ddaa Mon Sep 17 00:00:00 2001 From: Graham McIntire Date: Fri, 8 May 2026 12:20:53 -0500 Subject: [PATCH] Add HistoricalLoader real-Packets module path tests and LeaderElection delay branch test --- test/aprsme/cluster/leader_election_test.exs | 23 ++++++++ .../live/map_live/historical_loader_test.exs | 55 +++++++++++++++++++ 2 files changed, 78 insertions(+) diff --git a/test/aprsme/cluster/leader_election_test.exs b/test/aprsme/cluster/leader_election_test.exs index 902d395..1ffe2fa 100644 --- a/test/aprsme/cluster/leader_election_test.exs +++ b/test/aprsme/cluster/leader_election_test.exs @@ -492,6 +492,29 @@ defmodule Aprsme.Cluster.LeaderElectionTest do end end + describe "schedule_initial_election delay branch" do + test "non-zero election_initial_delay_ms uses Process.send_after path" do + original = Application.get_env(:aprsme, :election_initial_delay_ms, 100) + Application.put_env(:aprsme, :election_initial_delay_ms, 50) + + try do + # init/1 in non-clustered mode calls schedule_initial_election(false) + # which goes to the `else: Process.send_after(...)` branch when delay > 0. + original_cluster = Application.get_env(:aprsme, :cluster_enabled, false) + Application.put_env(:aprsme, :cluster_enabled, false) + + try do + assert {:ok, _state} = LeaderElection.init([]) + # The :attempt_election message is scheduled 50ms out — won't arrive within this test. + after + Application.put_env(:aprsme, :cluster_enabled, original_cluster) + end + after + Application.put_env(:aprsme, :election_initial_delay_ms, original) + end + end + end + describe "handle_info(:check_leadership) direct callback" do test "no-op for a non-leader — reschedules and keeps state" do state = %LeaderElection{is_leader: false, leader_node: nil} diff --git a/test/aprsme_web/live/map_live/historical_loader_test.exs b/test/aprsme_web/live/map_live/historical_loader_test.exs index 8f8a192..93b8baa 100644 --- a/test/aprsme_web/live/map_live/historical_loader_test.exs +++ b/test/aprsme_web/live/map_live/historical_loader_test.exs @@ -33,6 +33,61 @@ defmodule AprsmeWeb.MapLive.HistoricalLoaderTest do %Socket{assigns: Map.merge(defaults, assigns)} end + describe "real Packets module path (no mock)" do + setup do + original = Application.get_env(:aprsme, :packets_module) + Application.put_env(:aprsme, :packets_module, Aprsme.Packets) + on_exit(fn -> Application.put_env(:aprsme, :packets_module, original) end) + :ok + end + + test "load_historical_batch with real Packets module returns gracefully" do + # Real Packets.get_recent_packets_for_map runs against the DB; in a clean + # sandbox it returns []. We exercise the un-mocked branch (lines 256-309 + # in historical_loader.ex) without crashing. + socket = + build_socket(%{ + map_bounds: %{north: 34.0, south: 32.0, east: -95.0, west: -97.0}, + map_zoom: 10, + historical_loading: true, + loading_generation: 1, + tracked_callsign: "" + }) + + result = HistoricalLoader.load_historical_batch(socket, 0, 1) + assert is_map(result.assigns) + end + + test "load_historical_batch with a tracked callsign exercises maybe_include_latest_packet" do + socket = + build_socket(%{ + map_bounds: %{north: 34.0, south: 32.0, east: -95.0, west: -97.0}, + map_zoom: 10, + historical_loading: true, + loading_generation: 1, + tracked_callsign: "TRACK-1" + }) + + result = HistoricalLoader.load_historical_batch(socket, 0, 1) + assert is_map(result.assigns) + end + + test "load_historical_batch with stale generation is a no-op" do + socket = + build_socket(%{ + map_bounds: %{north: 34.0, south: 32.0, east: -95.0, west: -97.0}, + map_zoom: 10, + historical_loading: true, + loading_generation: 5, + tracked_callsign: "" + }) + + # Pass an older generation — should bail out without doing work. + result = HistoricalLoader.load_historical_batch(socket, 0, 1) + assert is_map(result.assigns) + end + end + describe "cancel_pending_loads/1" do test "cancels pending timers and clears the list" do timer1 = Process.send_after(self(), :noop, 60_000)