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 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 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 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 result[:sender] == "S" 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 result[:sender] == "PHG-1" 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