Add other SSIDs list to map sidebar, fix context/web boundary
- Add Packets.get_other_ssids/1 to query other SSIDs for a base callsign - Show other SSIDs in MapLive sidebar with track and info link buttons - Clicking an SSID tracks it on the map (zooms, shows marker, updates URL) - Refactor InfoLive.Show to use shared Packets.get_other_ssids/1 - Remove web-layer dependency from Packets context (no more AprsmeWeb.TimeHelpers calls from data layer) - Return raw received_at DateTime instead of pre-formatted timestamp maps - Format timestamps in the view layer where they belong
This commit is contained in:
parent
3b64c95fe9
commit
d803d070ac
6 changed files with 277 additions and 74 deletions
|
|
@ -26,3 +26,7 @@ XSS.Raw: XSS,lib/aprsme_web/components/core_components.ex:48,1E00DD8
|
||||||
SQL.Query: SQL injection,lib/aprsme_web/live/info_live/show.ex:603,52B452D
|
SQL.Query: SQL injection,lib/aprsme_web/live/info_live/show.ex:603,52B452D
|
||||||
XSS.Raw: XSS,lib/aprsme_web/live/info_live/show.ex:661,5C70BB5
|
XSS.Raw: XSS,lib/aprsme_web/live/info_live/show.ex:661,5C70BB5
|
||||||
XSS.Raw: XSS,lib/aprsme_web/live/info_live/show.ex:643,692A7EC
|
XSS.Raw: XSS,lib/aprsme_web/live/info_live/show.ex:643,692A7EC
|
||||||
|
XSS.Raw: XSS,lib/aprsme_web/live/info_live/show.ex:604,12E0C65
|
||||||
|
SQL.Query: SQL injection,lib/aprsme_web/live/info_live/show.ex:448,1662A49
|
||||||
|
SQL.Query: SQL injection,lib/aprsme_web/live/info_live/show.ex:546,37D03BD
|
||||||
|
XSS.Raw: XSS,lib/aprsme_web/live/info_live/show.ex:586,509A015
|
||||||
|
|
@ -859,6 +859,54 @@ defmodule Aprsme.Packets do
|
||||||
PreparedQueries.has_weather_packets?(callsign)
|
PreparedQueries.has_weather_packets?(callsign)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Gets other SSIDs for a given callsign's base callsign.
|
||||||
|
Returns a list of maps with callsign, ssid, received_at, and packet info.
|
||||||
|
Only returns SSIDs active within the last hour.
|
||||||
|
"""
|
||||||
|
@spec get_other_ssids(String.t()) :: list(map())
|
||||||
|
def get_other_ssids(callsign) when is_binary(callsign) do
|
||||||
|
base_callsign = Aprsme.Callsign.extract_base(callsign)
|
||||||
|
one_hour_ago = DateTime.add(DateTime.utc_now(), -3600, :second)
|
||||||
|
|
||||||
|
query =
|
||||||
|
from p in Packet,
|
||||||
|
where: p.base_callsign == ^base_callsign,
|
||||||
|
where: p.received_at >= ^one_hour_ago,
|
||||||
|
where: p.sender != ^callsign,
|
||||||
|
distinct: p.sender,
|
||||||
|
order_by: [desc: p.received_at],
|
||||||
|
limit: 10,
|
||||||
|
select: %{
|
||||||
|
sender: p.sender,
|
||||||
|
ssid: p.ssid,
|
||||||
|
received_at: p.received_at,
|
||||||
|
id: p.id,
|
||||||
|
symbol_table_id: p.symbol_table_id,
|
||||||
|
symbol_code: p.symbol_code
|
||||||
|
}
|
||||||
|
|
||||||
|
query
|
||||||
|
|> Repo.all()
|
||||||
|
|> Enum.map(fn row ->
|
||||||
|
packet = %Packet{
|
||||||
|
id: row.id,
|
||||||
|
sender: row.sender,
|
||||||
|
ssid: row.ssid,
|
||||||
|
received_at: row.received_at,
|
||||||
|
symbol_table_id: row.symbol_table_id,
|
||||||
|
symbol_code: row.symbol_code
|
||||||
|
}
|
||||||
|
|
||||||
|
%{
|
||||||
|
callsign: row.sender,
|
||||||
|
ssid: row.ssid,
|
||||||
|
received_at: row.received_at,
|
||||||
|
packet: packet
|
||||||
|
}
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
defp get_latest_weather_in_window(callsign, hours) do
|
defp get_latest_weather_in_window(callsign, hours) do
|
||||||
import Ecto.Query
|
import Ecto.Query
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,6 @@ defmodule AprsmeWeb.InfoLive.Show do
|
||||||
alias AprsmeWeb.AprsSymbol
|
alias AprsmeWeb.AprsSymbol
|
||||||
alias AprsmeWeb.Live.SharedPacketHandler
|
alias AprsmeWeb.Live.SharedPacketHandler
|
||||||
alias AprsmeWeb.MapLive.PacketUtils
|
alias AprsmeWeb.MapLive.PacketUtils
|
||||||
alias AprsmeWeb.TimeUtils
|
|
||||||
|
|
||||||
@neighbor_limit 10
|
@neighbor_limit 10
|
||||||
|
|
||||||
|
|
@ -74,7 +73,7 @@ defmodule AprsmeWeb.InfoLive.Show do
|
||||||
locale = Map.get(socket.assigns, :locale, "en")
|
locale = Map.get(socket.assigns, :locale, "en")
|
||||||
neighbors = get_neighbors(packet, normalized_callsign, locale)
|
neighbors = get_neighbors(packet, normalized_callsign, locale)
|
||||||
has_weather_packets = PacketUtils.has_weather_packets?(normalized_callsign)
|
has_weather_packets = PacketUtils.has_weather_packets?(normalized_callsign)
|
||||||
other_ssids = get_other_ssids(normalized_callsign)
|
other_ssids = Packets.get_other_ssids(normalized_callsign)
|
||||||
|
|
||||||
heard_by_stations = get_heard_by_stations(normalized_callsign, locale)
|
heard_by_stations = get_heard_by_stations(normalized_callsign, locale)
|
||||||
stations_heard_by = get_stations_heard_by(normalized_callsign, locale)
|
stations_heard_by = get_stations_heard_by(normalized_callsign, locale)
|
||||||
|
|
@ -361,62 +360,6 @@ defmodule AprsmeWeb.InfoLive.Show do
|
||||||
if bearing < 0, do: bearing + 360, else: bearing
|
if bearing < 0, do: bearing + 360, else: bearing
|
||||||
end
|
end
|
||||||
|
|
||||||
defp get_other_ssids(callsign) do
|
|
||||||
alias Aprsme.Packet
|
|
||||||
alias Aprsme.Repo
|
|
||||||
# Extract base callsign from the full callsign (remove SSID if present)
|
|
||||||
base_callsign = extract_base_callsign(callsign)
|
|
||||||
|
|
||||||
# Get recent packets for the base callsign to find other SSIDs
|
|
||||||
one_hour_ago = TimeUtils.one_hour_ago()
|
|
||||||
|
|
||||||
# Use a window function to get the most recent packet per sender
|
|
||||||
# This is much more efficient than fetching 100 packets and filtering in Elixir
|
|
||||||
query = """
|
|
||||||
WITH recent_ssids AS (
|
|
||||||
SELECT DISTINCT ON (sender)
|
|
||||||
sender, ssid, received_at, id, symbol_table_id, symbol_code
|
|
||||||
FROM packets
|
|
||||||
WHERE base_callsign = $1
|
|
||||||
AND received_at >= $2
|
|
||||||
AND sender != $3
|
|
||||||
ORDER BY sender, received_at DESC
|
|
||||||
)
|
|
||||||
SELECT * FROM recent_ssids
|
|
||||||
ORDER BY received_at DESC
|
|
||||||
LIMIT 10
|
|
||||||
"""
|
|
||||||
|
|
||||||
case Repo.query(query, [base_callsign, one_hour_ago, callsign]) do
|
|
||||||
{:ok, result} ->
|
|
||||||
Enum.map(result.rows, fn [sender, ssid, received_at, id, symbol_table_id, symbol_code] ->
|
|
||||||
# Create a minimal packet struct for display
|
|
||||||
packet = %Packet{
|
|
||||||
id: id,
|
|
||||||
sender: sender,
|
|
||||||
ssid: ssid,
|
|
||||||
received_at: received_at,
|
|
||||||
symbol_table_id: symbol_table_id,
|
|
||||||
symbol_code: symbol_code
|
|
||||||
}
|
|
||||||
|
|
||||||
%{
|
|
||||||
callsign: sender,
|
|
||||||
ssid: ssid,
|
|
||||||
last_heard: format_timestamp_for_display(packet),
|
|
||||||
packet: packet
|
|
||||||
}
|
|
||||||
end)
|
|
||||||
|
|
||||||
{:error, _} ->
|
|
||||||
[]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
defp extract_base_callsign(callsign) do
|
|
||||||
Callsign.extract_base(callsign)
|
|
||||||
end
|
|
||||||
|
|
||||||
defp get_heard_by_stations(callsign, locale) do
|
defp get_heard_by_stations(callsign, locale) do
|
||||||
alias Aprsme.Repo
|
alias Aprsme.Repo
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -368,14 +368,14 @@
|
||||||
<% end %>
|
<% end %>
|
||||||
</td>
|
</td>
|
||||||
<td class="opacity-70">
|
<td class="opacity-70">
|
||||||
<%= if ssid_info.last_heard && ssid_info.last_heard.timestamp do %>
|
<%= if ssid_info.received_at do %>
|
||||||
<span
|
<span
|
||||||
class="text-sm"
|
class="text-sm"
|
||||||
phx-hook="TimeAgoHook"
|
phx-hook="TimeAgoHook"
|
||||||
id={"ssid-time-#{ssid_info.ssid}"}
|
id={"ssid-time-#{ssid_info.ssid}"}
|
||||||
data-timestamp={ssid_info.last_heard.timestamp}
|
data-timestamp={DateTime.to_iso8601(ssid_info.received_at)}
|
||||||
>
|
>
|
||||||
{ssid_info.last_heard.time_ago}
|
{AprsmeWeb.TimeHelpers.time_ago_in_words(ssid_info.received_at)}
|
||||||
</span>
|
</span>
|
||||||
<% else %>
|
<% else %>
|
||||||
<span class="text-sm opacity-70">{gettext("Unknown")}</span>
|
<span class="text-sm opacity-70">{gettext("Unknown")}</span>
|
||||||
|
|
|
||||||
|
|
@ -176,20 +176,22 @@ defmodule AprsmeWeb.MapLive.Index do
|
||||||
historical_hours = Map.get(socket.assigns, :historical_hours, "1")
|
historical_hours = Map.get(socket.assigns, :historical_hours, "1")
|
||||||
packet_age_threshold = Map.get(socket.assigns, :packet_age_threshold, one_hour_ago)
|
packet_age_threshold = Map.get(socket.assigns, :packet_age_threshold, one_hour_ago)
|
||||||
|
|
||||||
# If tracking a specific callsign, fetch their latest packet
|
# If tracking a specific callsign, fetch their latest packet and other SSIDs
|
||||||
tracked_callsign_latest_packet =
|
{tracked_callsign_latest_packet, other_ssids} =
|
||||||
if tracked_callsign == "" do
|
if tracked_callsign == "" do
|
||||||
nil
|
{nil, []}
|
||||||
else
|
else
|
||||||
try do
|
try do
|
||||||
Packets.get_latest_packet_for_callsign(tracked_callsign)
|
packet = Packets.get_latest_packet_for_callsign(tracked_callsign)
|
||||||
|
ssids = Packets.get_other_ssids(tracked_callsign)
|
||||||
|
{packet, ssids}
|
||||||
rescue
|
rescue
|
||||||
# Handle database connection errors gracefully (especially in tests)
|
# Handle database connection errors gracefully (especially in tests)
|
||||||
DBConnection.OwnershipError ->
|
DBConnection.OwnershipError ->
|
||||||
nil
|
{nil, []}
|
||||||
|
|
||||||
_ ->
|
_ ->
|
||||||
nil
|
{nil, []}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -206,6 +208,7 @@ defmodule AprsmeWeb.MapLive.Index do
|
||||||
overlay_callsign: "",
|
overlay_callsign: "",
|
||||||
tracked_callsign: tracked_callsign,
|
tracked_callsign: tracked_callsign,
|
||||||
tracked_callsign_latest_packet: tracked_callsign_latest_packet,
|
tracked_callsign_latest_packet: tracked_callsign_latest_packet,
|
||||||
|
other_ssids: other_ssids,
|
||||||
trail_duration: trail_duration,
|
trail_duration: trail_duration,
|
||||||
historical_hours: historical_hours,
|
historical_hours: historical_hours,
|
||||||
packet_age_threshold: packet_age_threshold,
|
packet_age_threshold: packet_age_threshold,
|
||||||
|
|
@ -461,13 +464,40 @@ defmodule AprsmeWeb.MapLive.Index do
|
||||||
if normalized_callsign == "" do
|
if normalized_callsign == "" do
|
||||||
# Clear tracking
|
# Clear tracking
|
||||||
socket
|
socket
|
||||||
|> assign(tracked_callsign: "")
|
|> assign(tracked_callsign: "", other_ssids: [])
|
||||||
|> update_url_with_current_state()
|
|> update_url_with_current_state()
|
||||||
else
|
else
|
||||||
# Set tracking and navigate to callsign URL
|
# Set tracking, fetch latest packet, zoom to location, and show marker
|
||||||
socket
|
other_ssids = Packets.get_other_ssids(normalized_callsign)
|
||||||
|> assign(tracked_callsign: normalized_callsign)
|
latest_packet = Packets.get_latest_packet_for_callsign(normalized_callsign)
|
||||||
|> push_patch(to: "/#{normalized_callsign}")
|
|
||||||
|
socket =
|
||||||
|
assign(socket,
|
||||||
|
tracked_callsign: normalized_callsign,
|
||||||
|
other_ssids: other_ssids,
|
||||||
|
tracked_callsign_latest_packet: latest_packet
|
||||||
|
)
|
||||||
|
|
||||||
|
# Zoom to the callsign's location and display its marker
|
||||||
|
socket =
|
||||||
|
if latest_packet && latest_packet.lat && latest_packet.lon do
|
||||||
|
lat = Aprsme.EncodingUtils.to_float(latest_packet.lat) || 0.0
|
||||||
|
lon = Aprsme.EncodingUtils.to_float(latest_packet.lon) || 0.0
|
||||||
|
|
||||||
|
packet_data = DataBuilder.build_packet_data(latest_packet)
|
||||||
|
|
||||||
|
socket
|
||||||
|
|> push_event("zoom_to_location", %{lat: lat, lng: lon, zoom: 12})
|
||||||
|
|> push_event("add_historical_packets_batch", %{
|
||||||
|
packets: [packet_data],
|
||||||
|
batch: 0,
|
||||||
|
is_final: false
|
||||||
|
})
|
||||||
|
else
|
||||||
|
socket
|
||||||
|
end
|
||||||
|
|
||||||
|
push_patch(socket, to: "/#{normalized_callsign}")
|
||||||
end
|
end
|
||||||
|
|
||||||
{:noreply, socket}
|
{:noreply, socket}
|
||||||
|
|
@ -477,7 +507,7 @@ defmodule AprsmeWeb.MapLive.Index do
|
||||||
def handle_event("clear_tracking", _params, socket) do
|
def handle_event("clear_tracking", _params, socket) do
|
||||||
socket =
|
socket =
|
||||||
socket
|
socket
|
||||||
|> assign(tracked_callsign: "", overlay_callsign: "")
|
|> assign(tracked_callsign: "", overlay_callsign: "", other_ssids: [])
|
||||||
|> update_url_with_current_state()
|
|> update_url_with_current_state()
|
||||||
|
|
||||||
{:noreply, socket}
|
{:noreply, socket}
|
||||||
|
|
@ -1489,6 +1519,57 @@ defmodule AprsmeWeb.MapLive.Index do
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Other SSIDs -->
|
||||||
|
<%= if @tracked_callsign != "" and @other_ssids != [] do %>
|
||||||
|
<div class="space-y-3">
|
||||||
|
<label class="block text-sm font-semibold text-slate-700 dark:text-slate-300 flex items-center space-x-2">
|
||||||
|
<svg class="w-4 h-4 text-purple-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
stroke-width="2"
|
||||||
|
d="M15.75 6a3.75 3.75 0 11-7.5 0 3.75 3.75 0 017.5 0zM4.501 20.118a7.5 7.5 0 0114.998 0A17.933 17.933 0 0112 21.75c-2.676 0-5.216-.584-7.499-1.632z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
<span>{gettext("Other SSIDs")}</span>
|
||||||
|
</label>
|
||||||
|
<div class="space-y-1">
|
||||||
|
<%= for ssid_info <- @other_ssids do %>
|
||||||
|
<div class="flex items-center justify-between px-3 py-2 rounded-lg bg-white dark:bg-slate-800 border border-slate-200 dark:border-slate-700 hover:border-indigo-300 dark:hover:border-indigo-600 transition-all duration-200 text-sm group">
|
||||||
|
<button
|
||||||
|
phx-click="track_callsign"
|
||||||
|
phx-value-callsign={ssid_info.callsign}
|
||||||
|
class="font-semibold text-indigo-600 dark:text-indigo-400 group-hover:text-indigo-800 dark:group-hover:text-indigo-300 cursor-pointer"
|
||||||
|
>
|
||||||
|
{ssid_info.callsign}
|
||||||
|
</button>
|
||||||
|
<div class="flex items-center space-x-2">
|
||||||
|
<%= if ssid_info.received_at do %>
|
||||||
|
<span class="text-xs text-slate-500 dark:text-slate-400">
|
||||||
|
{time_ago_in_words(ssid_info.received_at)}
|
||||||
|
</span>
|
||||||
|
<% end %>
|
||||||
|
<.link
|
||||||
|
navigate={~p"/info/#{ssid_info.callsign}"}
|
||||||
|
class="text-xs text-slate-400 hover:text-indigo-600 dark:hover:text-indigo-400 transition-colors"
|
||||||
|
title={gettext("Station info")}
|
||||||
|
>
|
||||||
|
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
stroke-width="2"
|
||||||
|
d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</.link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
<!-- Trail Duration -->
|
<!-- Trail Duration -->
|
||||||
<div class="space-y-4">
|
<div class="space-y-4">
|
||||||
<label class="block text-sm font-semibold text-slate-700 dark:text-slate-300 flex items-center space-x-2">
|
<label class="block text-sm font-semibold text-slate-700 dark:text-slate-300 flex items-center space-x-2">
|
||||||
|
|
|
||||||
|
|
@ -744,6 +744,133 @@ defmodule Aprsme.PacketsTest do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "get_other_ssids/2" do
|
||||||
|
test "returns other SSIDs for a base callsign" do
|
||||||
|
now = DateTime.utc_now()
|
||||||
|
|
||||||
|
# Create packets for multiple SSIDs of the same base callsign
|
||||||
|
PacketsFixtures.packet_fixture(%{
|
||||||
|
sender: "AE5PL",
|
||||||
|
base_callsign: "AE5PL",
|
||||||
|
ssid: "0",
|
||||||
|
received_at: DateTime.add(now, -1800, :second),
|
||||||
|
symbol_table_id: "/",
|
||||||
|
symbol_code: "-"
|
||||||
|
})
|
||||||
|
|
||||||
|
PacketsFixtures.packet_fixture(%{
|
||||||
|
sender: "AE5PL-1",
|
||||||
|
base_callsign: "AE5PL",
|
||||||
|
ssid: "1",
|
||||||
|
received_at: DateTime.add(now, -900, :second),
|
||||||
|
symbol_table_id: "/",
|
||||||
|
symbol_code: ">"
|
||||||
|
})
|
||||||
|
|
||||||
|
PacketsFixtures.packet_fixture(%{
|
||||||
|
sender: "AE5PL-15",
|
||||||
|
base_callsign: "AE5PL",
|
||||||
|
ssid: "15",
|
||||||
|
received_at: DateTime.add(now, -300, :second),
|
||||||
|
symbol_table_id: "/",
|
||||||
|
symbol_code: "#"
|
||||||
|
})
|
||||||
|
|
||||||
|
# Query for SSIDs other than AE5PL (the base callsign with no SSID)
|
||||||
|
results = Packets.get_other_ssids("AE5PL")
|
||||||
|
|
||||||
|
callsigns = Enum.map(results, & &1.callsign)
|
||||||
|
assert "AE5PL-1" in callsigns
|
||||||
|
assert "AE5PL-15" in callsigns
|
||||||
|
refute "AE5PL" in callsigns
|
||||||
|
end
|
||||||
|
|
||||||
|
test "returns other SSIDs when querying from an SSID variant" do
|
||||||
|
now = DateTime.utc_now()
|
||||||
|
|
||||||
|
PacketsFixtures.packet_fixture(%{
|
||||||
|
sender: "W5ABC",
|
||||||
|
base_callsign: "W5ABC",
|
||||||
|
ssid: "0",
|
||||||
|
received_at: DateTime.add(now, -1800, :second)
|
||||||
|
})
|
||||||
|
|
||||||
|
PacketsFixtures.packet_fixture(%{
|
||||||
|
sender: "W5ABC-9",
|
||||||
|
base_callsign: "W5ABC",
|
||||||
|
ssid: "9",
|
||||||
|
received_at: DateTime.add(now, -900, :second)
|
||||||
|
})
|
||||||
|
|
||||||
|
# Query from the -9 SSID should show the base callsign
|
||||||
|
results = Packets.get_other_ssids("W5ABC-9")
|
||||||
|
|
||||||
|
callsigns = Enum.map(results, & &1.callsign)
|
||||||
|
assert "W5ABC" in callsigns
|
||||||
|
refute "W5ABC-9" in callsigns
|
||||||
|
end
|
||||||
|
|
||||||
|
test "returns empty list when no other SSIDs exist" do
|
||||||
|
PacketsFixtures.packet_fixture(%{
|
||||||
|
sender: "LONELY",
|
||||||
|
base_callsign: "LONELY",
|
||||||
|
ssid: "0"
|
||||||
|
})
|
||||||
|
|
||||||
|
results = Packets.get_other_ssids("LONELY")
|
||||||
|
assert results == []
|
||||||
|
end
|
||||||
|
|
||||||
|
test "only returns SSIDs with recent packets" do
|
||||||
|
now = DateTime.utc_now()
|
||||||
|
|
||||||
|
# Create a recent packet
|
||||||
|
PacketsFixtures.packet_fixture(%{
|
||||||
|
sender: "RECENT-1",
|
||||||
|
base_callsign: "RECENT",
|
||||||
|
ssid: "1",
|
||||||
|
received_at: DateTime.add(now, -300, :second)
|
||||||
|
})
|
||||||
|
|
||||||
|
# Create an old packet (2 hours ago, outside 1-hour window)
|
||||||
|
PacketsFixtures.packet_fixture(%{
|
||||||
|
sender: "RECENT-2",
|
||||||
|
base_callsign: "RECENT",
|
||||||
|
ssid: "2",
|
||||||
|
received_at: DateTime.add(now, -7200, :second)
|
||||||
|
})
|
||||||
|
|
||||||
|
results = Packets.get_other_ssids("RECENT")
|
||||||
|
|
||||||
|
callsigns = Enum.map(results, & &1.callsign)
|
||||||
|
assert "RECENT-1" in callsigns
|
||||||
|
refute "RECENT-2" in callsigns
|
||||||
|
end
|
||||||
|
|
||||||
|
test "includes symbol information and received_at in results" do
|
||||||
|
now = DateTime.utc_now()
|
||||||
|
|
||||||
|
PacketsFixtures.packet_fixture(%{
|
||||||
|
sender: "SYM-1",
|
||||||
|
base_callsign: "SYM",
|
||||||
|
ssid: "1",
|
||||||
|
received_at: now,
|
||||||
|
symbol_table_id: "/",
|
||||||
|
symbol_code: ">"
|
||||||
|
})
|
||||||
|
|
||||||
|
[result] = Packets.get_other_ssids("SYM")
|
||||||
|
|
||||||
|
assert result.callsign == "SYM-1"
|
||||||
|
assert result.ssid == "1"
|
||||||
|
assert result.packet.symbol_table_id == "/"
|
||||||
|
assert result.packet.symbol_code == ">"
|
||||||
|
# Returns raw received_at DateTime, not a formatted map
|
||||||
|
assert %DateTime{} = result.received_at
|
||||||
|
refute Map.has_key?(result, :last_heard)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
describe "get_last_hour_packets/0" do
|
describe "get_last_hour_packets/0" do
|
||||||
test "returns packets from last hour" do
|
test "returns packets from last hour" do
|
||||||
now = DateTime.utc_now()
|
now = DateTime.utc_now()
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue