test: broaden coverage across Packets/Weather/Info/Map/Status modules
This commit is contained in:
parent
5ea97a9264
commit
fac52cf288
4 changed files with 290 additions and 0 deletions
|
|
@ -472,4 +472,99 @@ defmodule Aprsme.Packets.PreparedQueriesTest do
|
|||
assert length(result) == 2
|
||||
end
|
||||
end
|
||||
|
||||
describe "get_recent_packets_in_bounds/2" do
|
||||
test "returns packets within the bounding box" do
|
||||
{:ok, _} =
|
||||
create_positioned_packet(%{
|
||||
sender: "INBOUNDS-1",
|
||||
base_callsign: "INBOUNDS",
|
||||
lat: Decimal.new("33.5"),
|
||||
lon: Decimal.new("-96.5")
|
||||
})
|
||||
|
||||
{:ok, _} =
|
||||
create_positioned_packet(%{
|
||||
sender: "OUTOFBOUNDS-1",
|
||||
base_callsign: "OUTOFBOUNDS",
|
||||
lat: Decimal.new("50.0"),
|
||||
lon: Decimal.new("-120.0")
|
||||
})
|
||||
|
||||
# bounds = [north, south, east, west]
|
||||
result = PreparedQueries.get_recent_packets_in_bounds([34.0, 33.0, -96.0, -97.0], 24)
|
||||
|
||||
senders = Enum.map(result, & &1.sender)
|
||||
assert "INBOUNDS-1" in senders
|
||||
refute "OUTOFBOUNDS-1" in senders
|
||||
end
|
||||
end
|
||||
|
||||
describe "get_packet_count_in_area/2" do
|
||||
test "counts only packets inside the bounding box" do
|
||||
{:ok, _} =
|
||||
create_positioned_packet(%{
|
||||
sender: "COUNT-IN-1",
|
||||
base_callsign: "COUNT",
|
||||
lat: Decimal.new("33.25"),
|
||||
lon: Decimal.new("-96.25")
|
||||
})
|
||||
|
||||
{:ok, _} =
|
||||
create_positioned_packet(%{
|
||||
sender: "COUNT-OUT",
|
||||
base_callsign: "COUNT",
|
||||
lat: Decimal.new("55.0"),
|
||||
lon: Decimal.new("-30.0")
|
||||
})
|
||||
|
||||
# bounds = [north, south, east, west]
|
||||
count = PreparedQueries.get_packet_count_in_area([34.0, 33.0, -96.0, -97.0], 24)
|
||||
assert is_integer(count)
|
||||
assert count >= 1
|
||||
end
|
||||
|
||||
test "returns 0 when area has no packets" do
|
||||
assert PreparedQueries.get_packet_count_in_area([0.0, -1.0, 180.0, 179.0], 1) == 0
|
||||
end
|
||||
end
|
||||
|
||||
describe "has_weather_packets?/1" do
|
||||
test "returns true when callsign has weather packets" do
|
||||
{:ok, _} =
|
||||
create_positioned_packet(%{
|
||||
sender: "HASWX-1",
|
||||
base_callsign: "HASWX",
|
||||
temperature: 72.0,
|
||||
humidity: 50.0
|
||||
})
|
||||
|
||||
assert PreparedQueries.has_weather_packets?("HASWX-1")
|
||||
end
|
||||
|
||||
test "returns false when callsign has no weather packets" do
|
||||
refute PreparedQueries.has_weather_packets?("NOWX-NEVER")
|
||||
end
|
||||
end
|
||||
|
||||
describe "weather_callsigns/1" do
|
||||
test "returns a MapSet containing callsigns with weather data" do
|
||||
{:ok, _} =
|
||||
create_positioned_packet(%{
|
||||
sender: "WXSET-1",
|
||||
base_callsign: "WXSET",
|
||||
has_weather: true,
|
||||
temperature: 65.0
|
||||
})
|
||||
|
||||
result = PreparedQueries.weather_callsigns(["WXSET-1", "NOWX-1"])
|
||||
assert %MapSet{} = result
|
||||
assert MapSet.member?(result, "WXSET-1")
|
||||
refute MapSet.member?(result, "NOWX-1")
|
||||
end
|
||||
|
||||
test "returns an empty MapSet for empty input" do
|
||||
assert PreparedQueries.weather_callsigns([]) == MapSet.new()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -165,4 +165,33 @@ defmodule AprsmeWeb.InfoLive.ShowHelpersTest do
|
|||
assert html =~ "<div"
|
||||
end
|
||||
end
|
||||
|
||||
describe "position_changed_for_test/2" do
|
||||
test "returns true when current packet is nil" do
|
||||
assert Show.position_changed_for_test(nil, %{lat: 1.0, lon: 1.0})
|
||||
end
|
||||
|
||||
test "returns false when new packet is nil" do
|
||||
refute Show.position_changed_for_test(%{lat: 1.0, lon: 1.0}, nil)
|
||||
end
|
||||
|
||||
test "returns false when positions are identical" do
|
||||
current = %{lat: 33.0, lon: -96.0}
|
||||
new = %{lat: 33.0, lon: -96.0}
|
||||
refute Show.position_changed_for_test(current, new)
|
||||
end
|
||||
|
||||
test "returns true when position changes by more than threshold" do
|
||||
current = %{lat: 33.0, lon: -96.0}
|
||||
new = %{lat: 33.01, lon: -96.0}
|
||||
assert Show.position_changed_for_test(current, new)
|
||||
end
|
||||
|
||||
test "returns true when either coordinate is nil" do
|
||||
assert Show.position_changed_for_test(%{lat: nil, lon: -96.0}, %{lat: 33.0, lon: -96.0})
|
||||
assert Show.position_changed_for_test(%{lat: 33.0, lon: nil}, %{lat: 33.0, lon: -96.0})
|
||||
assert Show.position_changed_for_test(%{lat: 33.0, lon: -96.0}, %{lat: nil, lon: -96.0})
|
||||
assert Show.position_changed_for_test(%{lat: 33.0, lon: -96.0}, %{lat: 33.0, lon: nil})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -528,4 +528,107 @@ defmodule AprsmeWeb.MapLive.IndexTest do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "MapLive.Index error handling and edge cases" do
|
||||
test "error_boundary_triggered event is handled without crashing", %{conn: conn} do
|
||||
{:ok, view, _html} = live(conn, "/", on_error: :warn)
|
||||
|
||||
assert render_hook(view, "error_boundary_triggered", %{
|
||||
"message" => "Test error",
|
||||
"stack" => "at test:1",
|
||||
"component_id" => "test-component"
|
||||
})
|
||||
end
|
||||
|
||||
test "update_map_state with bounds in params", %{conn: conn} do
|
||||
{:ok, view, _html} = live(conn, "/", on_error: :warn)
|
||||
|
||||
assert render_hook(view, "update_map_state", %{
|
||||
"center" => %{"lat" => 33.0, "lng" => -96.0},
|
||||
"zoom" => 10,
|
||||
"bounds" => %{
|
||||
"north" => 34.0,
|
||||
"south" => 32.0,
|
||||
"east" => -95.0,
|
||||
"west" => -97.0
|
||||
}
|
||||
})
|
||||
end
|
||||
|
||||
test "update_map_state with invalid/non-map center uses socket default", %{conn: conn} do
|
||||
{:ok, view, _html} = live(conn, "/?lat=33.0&lng=-96.0", on_error: :warn)
|
||||
|
||||
assert render_hook(view, "update_map_state", %{
|
||||
"center" => "not-a-map",
|
||||
"zoom" => 8
|
||||
})
|
||||
end
|
||||
|
||||
test "marker_hover_start with invalid path does not crash", %{conn: conn} do
|
||||
{:ok, view, _html} = live(conn, "/", on_error: :warn)
|
||||
|
||||
assert render_hook(view, "marker_hover_start", %{
|
||||
"id" => "m1",
|
||||
"path" => "",
|
||||
"lat" => 33.0,
|
||||
"lng" => -96.0
|
||||
})
|
||||
end
|
||||
|
||||
test "set_location event updates center", %{conn: conn} do
|
||||
{:ok, view, _html} = live(conn, "/", on_error: :warn)
|
||||
|
||||
assert render_hook(view, "set_location", %{"lat" => 40.7, "lng" => -74.0})
|
||||
end
|
||||
|
||||
test "update_trail_duration with extreme value is clamped", %{conn: conn} do
|
||||
{:ok, view, _html} = live(conn, "/", on_error: :warn)
|
||||
|
||||
# Very large value should be parsed and clamped
|
||||
assert render_hook(view, "update_trail_duration", %{"trail_duration" => "9999"})
|
||||
assert render_hook(view, "update_trail_duration", %{"trail_duration" => "0"})
|
||||
assert render_hook(view, "update_trail_duration", %{"trail_duration" => "invalid"})
|
||||
end
|
||||
|
||||
test "update_historical_hours with invalid input", %{conn: conn} do
|
||||
{:ok, view, _html} = live(conn, "/", on_error: :warn)
|
||||
|
||||
assert render_hook(view, "update_historical_hours", %{"historical_hours" => "invalid"})
|
||||
assert render_hook(view, "update_historical_hours", %{"historical_hours" => "0"})
|
||||
end
|
||||
|
||||
test "handles process_bounds_update via direct send", %{conn: conn} do
|
||||
{:ok, view, _html} = live(conn, "/", on_error: :warn)
|
||||
|
||||
bounds = %{north: 34.0, south: 32.0, east: -95.0, west: -97.0}
|
||||
send(view.pid, {:process_bounds_update, bounds})
|
||||
Process.sleep(50)
|
||||
assert render(view) =~ "aprs-map"
|
||||
end
|
||||
|
||||
test "handles postgres_packet broadcast via batcher", %{conn: conn} do
|
||||
{:ok, view, _html} = live(conn, "/?lat=33.0&lng=-96.0&z=12", on_error: :warn)
|
||||
|
||||
render_hook(view, "map_ready", %{})
|
||||
Process.sleep(50)
|
||||
|
||||
packet = %{
|
||||
id: "pg-1",
|
||||
sender: "PG-1",
|
||||
base_callsign: "PG",
|
||||
ssid: "1",
|
||||
lat: 33.0,
|
||||
lon: -96.0,
|
||||
has_position: true,
|
||||
symbol_table_id: "/",
|
||||
symbol_code: ">",
|
||||
received_at: DateTime.utc_now(),
|
||||
path: ""
|
||||
}
|
||||
|
||||
send(view.pid, {:postgres_packet, packet})
|
||||
Process.sleep(50)
|
||||
assert render(view) =~ "aprs-map"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -77,5 +77,68 @@ defmodule AprsmeWeb.WeatherLive.CallsignViewTest do
|
|||
# returned as-is.
|
||||
assert CallsignView.format_weather_value(%{humidity: "65"}, :humidity, "en") == "65"
|
||||
end
|
||||
|
||||
test "returns nil for unparseable string values on tracked keys" do
|
||||
assert CallsignView.format_weather_value(%{temperature: "not-a-number"}, :temperature, "en") ==
|
||||
nil
|
||||
end
|
||||
|
||||
test "returns stringified value for untracked numeric keys" do
|
||||
# humidity is numeric but not in the formatter map — passes through as string.
|
||||
assert CallsignView.format_weather_value(%{humidity: 65}, :humidity, "en") == "65"
|
||||
end
|
||||
|
||||
test "formats rain fields with units" do
|
||||
result = CallsignView.format_weather_value(%{rain_1h: 0.25}, :rain_1h, "en")
|
||||
assert is_binary(result)
|
||||
assert String.contains?(result, " ")
|
||||
end
|
||||
end
|
||||
|
||||
describe "live packet broadcast handling" do
|
||||
test "ignores non-matching postgres_packet broadcasts", %{conn: conn} do
|
||||
{:ok, lv, _html} = live(conn, ~p"/weather/OWNCALL", on_error: :warn)
|
||||
|
||||
# Broadcast an unrelated packet with no weather data
|
||||
unrelated = %{
|
||||
sender: "DIFFERENT",
|
||||
temperature: nil,
|
||||
humidity: nil,
|
||||
pressure: nil,
|
||||
wind_speed: nil,
|
||||
wind_direction: nil,
|
||||
rain_1h: nil
|
||||
}
|
||||
|
||||
send(lv.pid, {:postgres_packet, unrelated})
|
||||
Process.sleep(20)
|
||||
assert Process.alive?(lv.pid)
|
||||
end
|
||||
|
||||
test "ignores postgres_packet with wrong callsign", %{conn: conn} do
|
||||
{:ok, lv, _html} = live(conn, ~p"/weather/MYCALL", on_error: :warn)
|
||||
|
||||
wrong_call = %{
|
||||
sender: "OTHER",
|
||||
temperature: 72.5,
|
||||
humidity: 50,
|
||||
pressure: nil,
|
||||
wind_speed: nil,
|
||||
wind_direction: nil,
|
||||
rain_1h: nil
|
||||
}
|
||||
|
||||
send(lv.pid, {:postgres_packet, wrong_call})
|
||||
Process.sleep(20)
|
||||
assert Process.alive?(lv.pid)
|
||||
end
|
||||
|
||||
test "ignores unknown messages without crashing", %{conn: conn} do
|
||||
{:ok, lv, _html} = live(conn, ~p"/weather/UNKCALL", on_error: :warn)
|
||||
|
||||
send(lv.pid, :some_unknown_message)
|
||||
Process.sleep(20)
|
||||
assert Process.alive?(lv.pid)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue