Expand mobile channel, packet consumer, and user settings tests for coverage

This commit is contained in:
Graham McIntire 2026-05-08 12:08:21 -05:00
parent 57077e2bb1
commit d0296c9f4e
No known key found for this signature in database
GPG key ID: F4ABF488E6029E59
3 changed files with 202 additions and 0 deletions

View file

@ -202,6 +202,71 @@ defmodule Aprsme.PacketConsumerTest do
Process.sleep(50)
end
test "object event with :name atom-keyed binary in data_extended uses it" do
events = [
%{
sender: "OBJ-A",
base_callsign: "OBJ-A",
ssid: "0",
destination: "APRS",
path: "WIDE1-1",
data_type: "object",
lat: 33.0,
lon: -96.0,
data_extended: %{name: "MYATOMOBJ"},
information_field: "test",
raw_packet: "OBJ-A>APRS"
}
]
state = %{batch: [], batch_length: 0, batch_size: 1, batch_timeout: 1000, max_batch_size: 100, timer: nil}
{:noreply, [], _new_state} = PacketConsumer.handle_events(events, nil, state)
Process.sleep(50)
end
test "object event with nil data_extended" do
events = [
%{
sender: "OBJ-NIL",
base_callsign: "OBJ-NIL",
ssid: "0",
destination: "APRS",
path: "WIDE1-1",
data_type: "object",
lat: 33.0,
lon: -96.0,
data_extended: nil,
information_field: ";NULL",
raw_packet: "OBJ-NIL>APRS"
}
]
state = %{batch: [], batch_length: 0, batch_size: 1, batch_timeout: 1000, max_batch_size: 100, timer: nil}
{:noreply, [], _new_state} = PacketConsumer.handle_events(events, nil, state)
Process.sleep(50)
end
test "event with non-map data_extended (binary)" do
events = [
%{
sender: "BIN-1",
base_callsign: "BIN-1",
ssid: "0",
destination: "APRS",
path: "WIDE1-1",
data_type: "position",
# Non-map data_extended → extract_position_from_data_extended(_) at line 676.
data_extended: "raw-binary",
information_field: "test",
raw_packet: "BIN-1>APRS"
}
]
state = %{batch: [], batch_length: 0, batch_size: 1, batch_timeout: 1000, max_batch_size: 100, timer: nil}
{:noreply, [], _new_state} = PacketConsumer.handle_events(events, nil, state)
Process.sleep(50)
end
test "object event with 'name' string-keyed in data_extended uses it" do
events = [
%{

View file

@ -670,4 +670,101 @@ defmodule AprsmeWeb.MobileChannelTest do
assert pushed.lat == 33.0
end
end
describe "type-conversion helper edge cases" do
setup %{socket: socket} do
ref =
push(socket, "subscribe_bounds", %{
"north" => 90.0,
"south" => -90.0,
"east" => 180.0,
"west" => -180.0
})
assert_reply ref, :ok, _
:ok
end
test "to_float handles Decimal lat/lon values", %{socket: socket} do
packet = %{
id: "dec-1",
sender: "DEC-1",
lat: Decimal.new("33.0"),
lon: Decimal.new("-96.5"),
received_at: DateTime.utc_now(),
symbol_table_id: "/",
symbol_code: ">"
}
send(socket.channel_pid, {:streaming_packet, packet})
assert_push "packet", pushed
assert pushed.lat == 33.0
assert pushed.lng == -96.5
end
test "to_float drops un-castable atom coords", %{socket: socket} do
packet = %{
id: "atom-1",
sender: "ATM-1",
lat: :not_a_number,
lon: :still_not,
received_at: DateTime.utc_now(),
symbol_table_id: "/",
symbol_code: ">"
}
send(socket.channel_pid, {:streaming_packet, packet})
assert_push "packet", pushed
refute Map.has_key?(pushed, :lat)
refute Map.has_key?(pushed, :lng)
end
test "format_timestamp returns nil for unsupported value types", %{socket: socket} do
packet = %{
id: "ts-1",
sender: "TS-1",
lat: 33.0,
lon: -96.0,
# Non-DateTime/NaiveDateTime/nil — format_timestamp(_) → nil → field stripped.
received_at: 12_345,
symbol_table_id: "/",
symbol_code: ">"
}
send(socket.channel_pid, {:streaming_packet, packet})
assert_push "packet", pushed
refute Map.has_key?(pushed, :timestamp)
end
end
describe "subscribe_bounds limit and hours_back parsing" do
test "ensure_integer parses string limit and hours_back from payload", %{socket: socket} do
ref =
push(socket, "subscribe_bounds", %{
"north" => 34.0,
"south" => 32.0,
"east" => -95.0,
"west" => -97.0,
"limit" => "42",
"hours_back" => "2"
})
assert_reply ref, :ok, _
end
test "ensure_integer falls back to default for unparseable limit/hours_back", %{socket: socket} do
ref =
push(socket, "subscribe_bounds", %{
"north" => 34.0,
"south" => 32.0,
"east" => -95.0,
"west" => -97.0,
"limit" => "not-a-number",
"hours_back" => :atom
})
# Defaults kick in; the call should still succeed.
assert_reply ref, :ok, _
end
end
end

View file

@ -136,6 +136,46 @@ defmodule AprsmeWeb.UserSettingsLiveTest do
end
end
describe "validate_callsign event" do
setup %{conn: conn} do
user = user_fixture()
conn = log_in_user(conn, user)
{:ok, conn: conn, user: user}
end
test "exercises validate_callsign handler without crashing", %{conn: conn} do
{:ok, lv, _html} = live(conn, ~p"/users/settings", on_error: :warn)
_ =
lv
|> element("#callsign_form")
|> render_change(%{
"current_password" => valid_user_password(),
"user" => %{"callsign" => "INVALID-CALL"}
})
assert Process.alive?(lv.pid)
end
end
describe "mount with invalid email-confirmation token" do
setup %{conn: conn} do
user = user_fixture()
conn = log_in_user(conn, user)
{:ok, conn: conn}
end
test "redirects to /users/settings with an error flash", %{conn: conn} do
# An obviously bad token forces Accounts.update_user_email/2 → :error,
# which exercises the put_flash(:error, ...) branch in mount/3.
{:error, {:live_redirect, %{to: "/users/settings", flash: flash}}} =
live(conn, ~p"/users/settings/confirm_email/bogus-token", on_error: :warn)
assert is_map(flash)
# Either an error or info flash is acceptable, but the redirect must happen.
end
end
describe "update password form" do
setup %{conn: conn} do
user = user_fixture()