- Replace N+1 get_latest_packet_for_callsign calls in RF path loading with single get_latest_packets_for_callsigns batch query (DISTINCT ON) - Change batch packet processing to collect marker removal IDs and push one remove_markers_batch event instead of individual remove_marker calls - Add callsignIndex reverse map (callsign_group → Set<markerID>) for O(1) lookup in new_packet/new_packets handlers instead of scanning all markers
189 lines
5.5 KiB
Elixir
189 lines
5.5 KiB
Elixir
defmodule Aprsme.Packets.PreparedQueriesTest do
|
|
use Aprsme.DataCase, async: false
|
|
|
|
alias Aprsme.Packet
|
|
alias Aprsme.Packets.PreparedQueries
|
|
alias Aprsme.Repo
|
|
|
|
defp create_positioned_packet(attrs) do
|
|
defaults = %{
|
|
sender: "TEST-1",
|
|
base_callsign: "TEST",
|
|
ssid: "1",
|
|
destination: "APRS",
|
|
data_type: "position",
|
|
lat: Decimal.new("33.0000"),
|
|
lon: Decimal.new("-96.0000"),
|
|
received_at: DateTime.truncate(DateTime.utc_now(), :second)
|
|
}
|
|
|
|
merged = Map.merge(defaults, attrs)
|
|
|
|
%Packet{}
|
|
|> Packet.changeset(Map.from_struct(Map.merge(%Packet{}, merged)))
|
|
|> Repo.insert()
|
|
end
|
|
|
|
describe "get_latest_packets_for_callsigns/1" do
|
|
test "returns full packet structs for multiple callsigns" do
|
|
{:ok, _} =
|
|
create_positioned_packet(%{
|
|
sender: "K5GVL-10",
|
|
base_callsign: "K5GVL",
|
|
ssid: "10",
|
|
lat: Decimal.new("33.1000"),
|
|
lon: Decimal.new("-96.6000"),
|
|
comment: "Digi TX"
|
|
})
|
|
|
|
{:ok, _} =
|
|
create_positioned_packet(%{
|
|
sender: "N5TXZ-10",
|
|
base_callsign: "N5TXZ",
|
|
ssid: "10",
|
|
lat: Decimal.new("33.2000"),
|
|
lon: Decimal.new("-96.5000"),
|
|
comment: "iGate"
|
|
})
|
|
|
|
result = PreparedQueries.get_latest_packets_for_callsigns(["K5GVL-10", "N5TXZ-10"])
|
|
|
|
assert length(result) == 2
|
|
assert Enum.all?(result, &is_struct(&1, Packet))
|
|
|
|
senders = Enum.map(result, & &1.sender)
|
|
assert "K5GVL-10" in senders
|
|
assert "N5TXZ-10" in senders
|
|
|
|
k5gvl = Enum.find(result, &(&1.sender == "K5GVL-10"))
|
|
assert_in_delta k5gvl.lat, 33.1, 0.01
|
|
assert_in_delta k5gvl.lon, -96.6, 0.01
|
|
end
|
|
|
|
test "returns empty list for empty input" do
|
|
assert PreparedQueries.get_latest_packets_for_callsigns([]) == []
|
|
end
|
|
|
|
test "returns empty list for nonexistent callsigns" do
|
|
result = PreparedQueries.get_latest_packets_for_callsigns(["NONEXIST-1", "FAKE-2"])
|
|
assert result == []
|
|
end
|
|
|
|
test "returns latest packet when multiple exist for a callsign" do
|
|
{:ok, _} =
|
|
create_positioned_packet(%{
|
|
sender: "K5GVL-10",
|
|
base_callsign: "K5GVL",
|
|
ssid: "10",
|
|
lat: Decimal.new("33.1000"),
|
|
lon: Decimal.new("-96.6000"),
|
|
received_at: DateTime.add(DateTime.utc_now(), -3600, :second)
|
|
})
|
|
|
|
{:ok, _} =
|
|
create_positioned_packet(%{
|
|
sender: "K5GVL-10",
|
|
base_callsign: "K5GVL",
|
|
ssid: "10",
|
|
lat: Decimal.new("34.0000"),
|
|
lon: Decimal.new("-97.0000"),
|
|
received_at: DateTime.truncate(DateTime.utc_now(), :second)
|
|
})
|
|
|
|
result = PreparedQueries.get_latest_packets_for_callsigns(["K5GVL-10"])
|
|
|
|
assert length(result) == 1
|
|
packet = hd(result)
|
|
assert is_struct(packet, Packet)
|
|
assert_in_delta packet.lat, 34.0, 0.01
|
|
assert_in_delta packet.lon, -97.0, 0.01
|
|
end
|
|
end
|
|
|
|
describe "get_latest_positions_for_callsigns/1" do
|
|
test "returns positions for multiple callsigns" do
|
|
{:ok, _} =
|
|
create_positioned_packet(%{
|
|
sender: "K5GVL-10",
|
|
base_callsign: "K5GVL",
|
|
ssid: "10",
|
|
lat: Decimal.new("33.1000"),
|
|
lon: Decimal.new("-96.6000")
|
|
})
|
|
|
|
{:ok, _} =
|
|
create_positioned_packet(%{
|
|
sender: "N5TXZ-10",
|
|
base_callsign: "N5TXZ",
|
|
ssid: "10",
|
|
lat: Decimal.new("33.2000"),
|
|
lon: Decimal.new("-96.5000")
|
|
})
|
|
|
|
result = PreparedQueries.get_latest_positions_for_callsigns(["K5GVL-10", "N5TXZ-10"])
|
|
|
|
assert length(result) == 2
|
|
callsigns = Enum.map(result, & &1.callsign)
|
|
assert "K5GVL-10" in callsigns
|
|
assert "N5TXZ-10" in callsigns
|
|
|
|
k5gvl = Enum.find(result, &(&1.callsign == "K5GVL-10"))
|
|
assert_in_delta k5gvl.lat, 33.1, 0.01
|
|
assert_in_delta k5gvl.lng, -96.6, 0.01
|
|
end
|
|
|
|
test "returns empty list for empty input" do
|
|
assert PreparedQueries.get_latest_positions_for_callsigns([]) == []
|
|
end
|
|
|
|
test "returns empty list for nonexistent callsigns" do
|
|
result = PreparedQueries.get_latest_positions_for_callsigns(["NONEXIST-1", "FAKE-2"])
|
|
assert result == []
|
|
end
|
|
|
|
test "is case-insensitive" do
|
|
{:ok, _} =
|
|
create_positioned_packet(%{
|
|
sender: "K5GVL-10",
|
|
base_callsign: "K5GVL",
|
|
ssid: "10",
|
|
lat: Decimal.new("33.1000"),
|
|
lon: Decimal.new("-96.6000")
|
|
})
|
|
|
|
result = PreparedQueries.get_latest_positions_for_callsigns(["k5gvl-10"])
|
|
|
|
assert length(result) == 1
|
|
assert hd(result).callsign == "K5GVL-10"
|
|
end
|
|
|
|
test "returns latest position when multiple packets exist for a callsign" do
|
|
{:ok, _} =
|
|
create_positioned_packet(%{
|
|
sender: "K5GVL-10",
|
|
base_callsign: "K5GVL",
|
|
ssid: "10",
|
|
lat: Decimal.new("33.1000"),
|
|
lon: Decimal.new("-96.6000"),
|
|
received_at: DateTime.add(DateTime.utc_now(), -3600, :second)
|
|
})
|
|
|
|
{:ok, _} =
|
|
create_positioned_packet(%{
|
|
sender: "K5GVL-10",
|
|
base_callsign: "K5GVL",
|
|
ssid: "10",
|
|
lat: Decimal.new("34.0000"),
|
|
lon: Decimal.new("-97.0000"),
|
|
received_at: DateTime.truncate(DateTime.utc_now(), :second)
|
|
})
|
|
|
|
result = PreparedQueries.get_latest_positions_for_callsigns(["K5GVL-10"])
|
|
|
|
assert length(result) == 1
|
|
position = hd(result)
|
|
assert_in_delta position.lat, 34.0, 0.01
|
|
assert_in_delta position.lng, -97.0, 0.01
|
|
end
|
|
end
|
|
end
|