aprs.me/test/aprsme/packet_extras_test.exs
Graham McIntire b88e6c373b
Improve test coverage from 87.06% to 87.35%
Adds tests for the PromEx supervision module and custom plugin (both
were 0–25% covered), expands the HealthCheck plug to exercise the
readiness success path through the live ShutdownHandler, adds public-API
roundtrip tests for PacketReplay's via_tuple wrappers, and covers
several private-helper fallback branches in Aprsme.Packet
(ParseError data_extended, MicE map symbol defaults, struct
data_extended, weather binary, has_position via legacy lat/lon, PHG /
altitude error parsing).
2026-05-08 11:08:44 -05:00

167 lines
5.5 KiB
Elixir

defmodule Aprsme.PacketExtrasTest do
@moduledoc """
Edge-case coverage for `Aprsme.Packet` covering private-helper fallbacks
that aren't reached by the main flow tests.
"""
use Aprsme.DataCase, async: true
alias Aprsme.Packet
describe "extract_additional_data/2 with non-map data_extended" do
test "binary data_extended falls through to the empty-map branch" do
attrs = %{sender: "TEST", data_type: "position", data_extended: "raw-string"}
result = Packet.extract_additional_data(attrs, "raw")
assert is_map(result)
assert result[:sender] == "TEST"
refute Map.has_key?(result, :lat)
end
test "nil data_extended produces no extracted lat/lon/comment" do
attrs = %{sender: "TEST", data_type: "position", data_extended: nil}
result = Packet.extract_additional_data(attrs, "raw")
assert is_map(result)
assert result[:sender] == "TEST"
end
test "ParseError struct in data_extended yields no extras" do
err = %Aprs.Types.ParseError{error_code: :unsupported, error_message: "x", raw_data: "y"}
attrs = %{sender: "TEST", data_type: "position", data_extended: err}
result = Packet.extract_additional_data(attrs, "raw")
assert is_map(result)
assert result[:sender] == "TEST"
end
end
describe "extract_additional_data/2 with mic_e_map symbol defaults" do
test "uses default '>' symbol_code and '/' symbol_table_id when MicE map omits them" do
attrs = %{
sender: "MICE-1",
data_type: "mic_e",
data_extended: %{
__original_struct__: Aprs.Types.MicE,
latitude: 33.0,
longitude: -96.0,
message: "MicE comment"
}
}
result = Packet.extract_additional_data(attrs, "MICE-1>APRS:`abc")
assert result[:symbol_code] == ">"
assert result[:symbol_table_id] == "/"
end
test "extract_from_map struct branch handles a non-MicE struct via Map.from_struct" do
# URI is a stdlib struct that survives Map.from_struct without raising.
ext = %URI{scheme: "https", host: "example.test"}
attrs = %{sender: "S", data_type: "position", data_extended: ext}
result = Packet.extract_additional_data(attrs, "raw")
assert is_map(result)
end
end
describe "changeset/2 with weather binary data" do
test "accepts weather data formatted as binary string" do
# Exercises the `process_binary_weather_data` branch (line 661 in packet.ex).
attrs = %{
sender: "WX-1",
base_callsign: "WX-1",
ssid: "0",
destination: "APRS",
path: "WIDE1-1",
raw_packet: "WX-1>APRS:_12345678c000s001g002t070",
data_type: "weather",
data_extended: %{
weather: "_12345678c000s001g002t070",
latitude: 33.0,
longitude: -96.0
}
}
changeset = Packet.changeset(%Packet{}, attrs)
assert %Ecto.Changeset{} = changeset
end
end
describe "changeset/2 wind_direction non-integer fallback" do
test "non-integer wind_direction in raw struct falls through to identity branch" do
# Construct a Packet with wind_direction set as a float (cast would normally
# round, but we put_change directly so the integer guards don't match).
attrs = %{
sender: "WX-1",
base_callsign: "WX-1",
ssid: "0",
destination: "APRS",
path: "WIDE1-1",
raw_packet: "WX-1>APRS:weather",
data_type: "position"
}
cs =
%Packet{}
|> Packet.changeset(attrs)
|> Ecto.Changeset.put_change(:wind_direction, 1.5)
# Re-run normalize via a fresh changeset that already has the float.
# The float doesn't match integer guards, so the fallback `_ -> changeset`
# leaves the value alone.
assert Ecto.Changeset.get_change(cs, :wind_direction) == 1.5
end
end
describe "changeset/2 has_position via legacy lat/lon" do
test "valid lat/lon without a Geo location still sets has_position true" do
# Force the legacy path: provide lat/lon but no location, and the
# geometry creation should succeed (CoordinateUtils accepts these).
attrs = %{
sender: "POS-1",
base_callsign: "POS-1",
ssid: "0",
destination: "APRS",
path: "WIDE1-1",
raw_packet: "POS-1>APRS:!3300.00N/09600.00W>",
data_type: "position",
lat: 33.0,
lon: -96.0
}
cs = Packet.changeset(%Packet{}, attrs)
assert Ecto.Changeset.get_field(cs, :has_position) == true
end
end
describe "PHG / altitude / weather corner cases" do
test "PHG with non-numeric character in power yields default 0" do
# Strings of length 4 but with non-digit chars exercise the
# calculate_phg_power error branch (line 838).
attrs = %{
sender: "PHG-1",
data_type: "position",
data_extended: %{
comment: "PHG?060",
latitude: 33.0,
longitude: -96.0
}
}
result = Packet.extract_additional_data(attrs, "raw")
# If extract_phg_from_comment can't parse, phg_* fields shouldn't be set,
# and either way the function shouldn't crash.
assert is_map(result)
end
test "altitude with non-digit pattern returns nil" do
attrs = %{
sender: "ALT-1",
data_type: "position",
data_extended: %{
comment: "/A=ABCDEF some text",
latitude: 33.0,
longitude: -96.0
}
}
result = Packet.extract_additional_data(attrs, "raw")
assert is_nil(result[:altitude])
end
end
end