449 lines
15 KiB
Elixir
449 lines
15 KiB
Elixir
defmodule AprsmeWeb.MapLive.IntegrationTest do
|
|
use AprsmeWeb.ConnCase
|
|
|
|
import Aprsme.PacketsFixtures
|
|
import Phoenix.LiveViewTest
|
|
|
|
describe "MapLive.Index with real packet data" do
|
|
test "renders a map with pre-existing packets and simulates bounds_changed", %{conn: conn} do
|
|
# Seed some packets in the bounds we'll scroll to.
|
|
now = DateTime.utc_now()
|
|
|
|
for i <- 1..3 do
|
|
packet_fixture(%{
|
|
sender: "INT#{i}",
|
|
base_callsign: "INT#{i}",
|
|
ssid: "0",
|
|
received_at: DateTime.add(now, -i * 60, :second),
|
|
lat: Decimal.new("#{40 + i * 0.01}"),
|
|
lon: Decimal.new("#{-74 - i * 0.01}"),
|
|
has_position: true,
|
|
path: "WIDE1-1"
|
|
})
|
|
end
|
|
|
|
{:ok, view, _html} = live(conn, "/?lat=40.0&lng=-74.0&z=11", on_error: :warn)
|
|
|
|
# Fire a bounds_changed event covering the seeded region.
|
|
bounds = %{
|
|
"bounds" => %{
|
|
"north" => 41.0,
|
|
"south" => 39.5,
|
|
"east" => -73.0,
|
|
"west" => -74.5
|
|
}
|
|
}
|
|
|
|
assert render_hook(view, "bounds_changed", bounds)
|
|
assert render_hook(view, "map_ready", %{})
|
|
|
|
# Allow async historical loading to run.
|
|
Process.sleep(100)
|
|
html = render(view)
|
|
assert html =~ "aprs-map"
|
|
end
|
|
|
|
test "tracks a callsign that has a real packet", %{conn: conn} do
|
|
_packet =
|
|
packet_fixture(%{
|
|
sender: "TRACKME",
|
|
base_callsign: "TRACKME",
|
|
ssid: "0",
|
|
received_at: DateTime.utc_now(),
|
|
lat: Decimal.new("33.0"),
|
|
lon: Decimal.new("-96.5"),
|
|
has_position: true,
|
|
path: "WIDE1-1"
|
|
})
|
|
|
|
{:ok, view, _html} = live(conn, "/?call=TRACKME", on_error: :warn)
|
|
assert render_hook(view, "map_ready", %{})
|
|
Process.sleep(100)
|
|
html = render(view)
|
|
assert html =~ "TRACKME" or html =~ "aprs-map"
|
|
end
|
|
|
|
test "callsign path route loads the tracked callsign", %{conn: conn} do
|
|
_packet =
|
|
packet_fixture(%{
|
|
sender: "PATH1",
|
|
base_callsign: "PATH1",
|
|
ssid: "0",
|
|
received_at: DateTime.utc_now(),
|
|
lat: Decimal.new("35.0"),
|
|
lon: Decimal.new("-90.0"),
|
|
has_position: true,
|
|
path: "WIDE1-1"
|
|
})
|
|
|
|
{:ok, _view, html} = live(conn, "/PATH1", on_error: :warn)
|
|
assert html =~ "aprs-map"
|
|
end
|
|
|
|
test "spatial_packet arriving for an in-bounds callsign triggers display", %{conn: conn} do
|
|
{:ok, view, _html} = live(conn, "/?lat=33.0&lng=-96.0&z=12", on_error: :warn)
|
|
|
|
render_hook(view, "bounds_changed", %{
|
|
"bounds" => %{
|
|
"north" => 34.0,
|
|
"south" => 32.0,
|
|
"east" => -95.0,
|
|
"west" => -97.0
|
|
}
|
|
})
|
|
|
|
packet = %{
|
|
id: "sp-1",
|
|
sender: "SP-1",
|
|
base_callsign: "SP",
|
|
ssid: "1",
|
|
lat: 33.0,
|
|
lon: -96.0,
|
|
has_position: true,
|
|
symbol_table_id: "/",
|
|
symbol_code: ">",
|
|
received_at: DateTime.utc_now(),
|
|
path: "WIDE1-1",
|
|
comment: "in bounds"
|
|
}
|
|
|
|
send(view.pid, {:spatial_packet, packet})
|
|
Process.sleep(50)
|
|
assert render(view) =~ "aprs-map"
|
|
end
|
|
|
|
test "handles a batch of packets including out-of-bounds ones", %{conn: conn} do
|
|
{:ok, view, _html} = live(conn, "/?lat=33.0&lng=-96.0&z=12", on_error: :warn)
|
|
|
|
render_hook(view, "bounds_changed", %{
|
|
"bounds" => %{
|
|
"north" => 34.0,
|
|
"south" => 32.0,
|
|
"east" => -95.0,
|
|
"west" => -97.0
|
|
}
|
|
})
|
|
|
|
packets = [
|
|
%{
|
|
id: "b-1",
|
|
sender: "IN-1",
|
|
base_callsign: "IN",
|
|
ssid: "1",
|
|
lat: 33.5,
|
|
lon: -96.0,
|
|
has_position: true,
|
|
symbol_table_id: "/",
|
|
symbol_code: ">",
|
|
received_at: DateTime.utc_now(),
|
|
path: ""
|
|
},
|
|
%{
|
|
id: "b-2",
|
|
sender: "OUT-1",
|
|
base_callsign: "OUT",
|
|
ssid: "1",
|
|
lat: 50.0,
|
|
lon: -80.0,
|
|
has_position: true,
|
|
symbol_table_id: "/",
|
|
symbol_code: ">",
|
|
received_at: DateTime.utc_now(),
|
|
path: ""
|
|
}
|
|
]
|
|
|
|
send(view.pid, {:packet_batch, packets})
|
|
Process.sleep(50)
|
|
assert render(view) =~ "aprs-map"
|
|
end
|
|
end
|
|
|
|
describe "MapLive handle_event coverage for less-exercised events" do
|
|
test "popup_closed, get_assigns, set_slideover_state, request_geolocation, geolocation_error", %{conn: conn} do
|
|
{:ok, view, _html} = live(conn, "/?lat=33.0&lng=-96.0&z=10", on_error: :warn)
|
|
render_hook(view, "map_ready", %{})
|
|
|
|
assert render_hook(view, "popup_closed", %{})
|
|
assert render_hook(view, "set_slideover_state", %{"open" => false})
|
|
assert render_hook(view, "set_slideover_state", %{"open" => true})
|
|
assert render_hook(view, "request_geolocation", %{})
|
|
assert render_hook(view, "geolocation_error", %{"error" => "denied"})
|
|
assert render_hook(view, "toggle_slideover", %{})
|
|
end
|
|
|
|
test "marker_hover_start and marker_hover_end exercise hover handlers", %{conn: conn} do
|
|
{:ok, view, _html} = live(conn, "/?lat=33.0&lng=-96.0&z=12", on_error: :warn)
|
|
render_hook(view, "map_ready", %{})
|
|
|
|
assert render_hook(view, "marker_hover_start", %{
|
|
"id" => "m-1",
|
|
"path" => "WIDE1-1",
|
|
"lat" => 33.0,
|
|
"lng" => -96.0
|
|
})
|
|
|
|
assert render_hook(view, "marker_hover_end", %{})
|
|
end
|
|
|
|
test "search_callsign with empty input no-ops", %{conn: conn} do
|
|
{:ok, view, _html} = live(conn, "/", on_error: :warn)
|
|
assert render_hook(view, "search_callsign", %{"callsign" => " "})
|
|
end
|
|
|
|
test "track_callsign with empty input clears tracking", %{conn: conn} do
|
|
{:ok, view, _html} = live(conn, "/?call=ZZZ", on_error: :warn)
|
|
render_hook(view, "map_ready", %{})
|
|
|
|
# Empty callsign goes through the clear-tracking branch.
|
|
assert render_hook(view, "track_callsign", %{"callsign" => ""})
|
|
end
|
|
|
|
test "track_callsign with non-empty callsign that has a packet zooms and adds marker", %{conn: conn} do
|
|
# Seed a real packet so track_callsign exercises the latest_packet branch.
|
|
packet_fixture(%{
|
|
sender: "TRACKEV",
|
|
base_callsign: "TRACKEV",
|
|
ssid: "0",
|
|
received_at: DateTime.utc_now(),
|
|
lat: Decimal.new("45.0"),
|
|
lon: Decimal.new("-93.0"),
|
|
has_position: true,
|
|
path: "WIDE1-1"
|
|
})
|
|
|
|
{:ok, view, _html} = live(conn, "/", on_error: :warn)
|
|
render_hook(view, "map_ready", %{})
|
|
assert render_hook(view, "track_callsign", %{"callsign" => "TRACKEV"})
|
|
end
|
|
|
|
test "marker_hover_start with a path triggers RF path drawing", %{conn: conn} do
|
|
# Seed packets that match the path stations so RF path resolution finds them.
|
|
packet_fixture(%{
|
|
sender: "PATHST1",
|
|
base_callsign: "PATHST1",
|
|
ssid: "0",
|
|
received_at: DateTime.utc_now(),
|
|
lat: Decimal.new("33.5"),
|
|
lon: Decimal.new("-96.5"),
|
|
has_position: true,
|
|
path: ""
|
|
})
|
|
|
|
{:ok, view, _html} = live(conn, "/?lat=33.0&lng=-96.0&z=12", on_error: :warn)
|
|
render_hook(view, "map_ready", %{})
|
|
|
|
assert render_hook(view, "marker_hover_start", %{
|
|
"id" => "m-rf",
|
|
"path" => "PATHST1*,WIDE1-1",
|
|
"lat" => 33.0,
|
|
"lng" => -96.0
|
|
})
|
|
end
|
|
|
|
test "clear_tracking event clears the tracked callsign", %{conn: conn} do
|
|
{:ok, view, _html} = live(conn, "/?call=TRACK1", on_error: :warn)
|
|
render_hook(view, "map_ready", %{})
|
|
assert render_hook(view, "clear_tracking", %{})
|
|
end
|
|
|
|
test "update_callsign updates overlay_callsign", %{conn: conn} do
|
|
{:ok, view, _html} = live(conn, "/", on_error: :warn)
|
|
assert render_hook(view, "update_callsign", %{"callsign" => "AB1XYZ"})
|
|
end
|
|
|
|
test "update_trail_duration changes the threshold", %{conn: conn} do
|
|
{:ok, view, _html} = live(conn, "/?lat=33.0&lng=-96.0&z=10", on_error: :warn)
|
|
render_hook(view, "map_ready", %{})
|
|
assert render_hook(view, "update_trail_duration", %{"trail_duration" => "6"})
|
|
end
|
|
|
|
test "update_historical_hours changes assigns", %{conn: conn} do
|
|
{:ok, view, _html} = live(conn, "/?lat=33.0&lng=-96.0&z=10", on_error: :warn)
|
|
render_hook(view, "map_ready", %{})
|
|
assert render_hook(view, "update_historical_hours", %{"historical_hours" => "3"})
|
|
end
|
|
|
|
test "locate_me, set_location, clear_and_reload_markers events", %{conn: conn} do
|
|
{:ok, view, _html} = live(conn, "/", on_error: :warn)
|
|
assert render_hook(view, "locate_me", %{})
|
|
assert render_hook(view, "set_location", %{"lat" => 33.5, "lng" => -96.5})
|
|
assert render_hook(view, "clear_and_reload_markers", %{})
|
|
end
|
|
|
|
test "update_map_state with center and zoom", %{conn: conn} do
|
|
{:ok, view, _html} = live(conn, "/?lat=33.0&lng=-96.0&z=10", on_error: :warn)
|
|
render_hook(view, "map_ready", %{})
|
|
|
|
assert render_hook(view, "update_map_state", %{
|
|
"center" => %{"lat" => 34.0, "lng" => -95.0},
|
|
"zoom" => 11
|
|
})
|
|
end
|
|
|
|
test "marker_clicked event", %{conn: conn} do
|
|
{:ok, view, _html} = live(conn, "/", on_error: :warn)
|
|
assert render_hook(view, "marker_clicked", %{})
|
|
end
|
|
|
|
test "error_boundary_triggered event logs without crashing", %{conn: conn} do
|
|
{:ok, view, _html} = live(conn, "/", on_error: :warn)
|
|
|
|
assert render_hook(view, "error_boundary_triggered", %{
|
|
"message" => "test error",
|
|
"stack" => "test stack",
|
|
"component_id" => "comp-1"
|
|
})
|
|
end
|
|
end
|
|
|
|
describe "MapLive handle_info coverage" do
|
|
test ":cleanup_old_packets, :update_time_display, :reload_historical_packets do not crash", %{conn: conn} do
|
|
{:ok, view, _html} = live(conn, "/?lat=33.0&lng=-96.0&z=10", on_error: :warn)
|
|
render_hook(view, "map_ready", %{})
|
|
|
|
send(view.pid, :cleanup_old_packets)
|
|
send(view.pid, :update_time_display)
|
|
send(view.pid, :reload_historical_packets)
|
|
Process.sleep(50)
|
|
assert render(view) =~ "aprs-map"
|
|
end
|
|
|
|
test ":clear_rf_path message clears the RF path", %{conn: conn} do
|
|
{:ok, view, _html} = live(conn, "/?lat=33.0&lng=-96.0&z=10", on_error: :warn)
|
|
send(view.pid, :clear_rf_path)
|
|
Process.sleep(20)
|
|
assert render(view) =~ "aprs-map"
|
|
end
|
|
|
|
test "{:new_deployment, _} message updates deployed_at", %{conn: conn} do
|
|
{:ok, view, _html} = live(conn, "/", on_error: :warn)
|
|
send(view.pid, {:new_deployment, %{deployed_at: DateTime.utc_now()}})
|
|
Process.sleep(20)
|
|
assert render(view) =~ "aprs-map"
|
|
end
|
|
|
|
test "{:drain_connections, n} message processes drain", %{conn: conn} do
|
|
{:ok, view, _html} = live(conn, "/", on_error: :warn)
|
|
send(view.pid, {:drain_connections, 0})
|
|
Process.sleep(20)
|
|
assert render(view) =~ "aprs-map"
|
|
end
|
|
|
|
test ":initialize_replay message reaches handle_info_initialize_replay", %{conn: conn} do
|
|
{:ok, view, _html} = live(conn, "/?lat=33.0&lng=-96.0&z=10", on_error: :warn)
|
|
render_hook(view, "map_ready", %{})
|
|
send(view.pid, :initialize_replay)
|
|
Process.sleep(20)
|
|
assert render(view) =~ "aprs-map"
|
|
end
|
|
|
|
test "{:load_rf_path_station_packets, _} message hits get_latest_packets_for_callsigns", %{conn: conn} do
|
|
{:ok, view, _html} = live(conn, "/?lat=33.0&lng=-96.0&z=12", on_error: :warn)
|
|
send(view.pid, {:load_rf_path_station_packets, ["FOO", "BAR"]})
|
|
Process.sleep(20)
|
|
assert render(view) =~ "aprs-map"
|
|
end
|
|
|
|
test "{:show_error, message} message sets a flash", %{conn: conn} do
|
|
{:ok, view, _html} = live(conn, "/", on_error: :warn)
|
|
send(view.pid, {:show_error, "boom"})
|
|
Process.sleep(20)
|
|
assert render(view) =~ "aprs-map"
|
|
end
|
|
|
|
test "{:load_historical_batch, _} backward-compat message", %{conn: conn} do
|
|
{:ok, view, _html} = live(conn, "/?lat=33.0&lng=-96.0&z=10", on_error: :warn)
|
|
render_hook(view, "map_ready", %{})
|
|
send(view.pid, {:load_historical_batch, 0})
|
|
Process.sleep(20)
|
|
assert render(view) =~ "aprs-map"
|
|
end
|
|
|
|
test "{:load_historical_batch, _, generation} matching current generation", %{conn: conn} do
|
|
{:ok, view, _html} = live(conn, "/?lat=33.0&lng=-96.0&z=10", on_error: :warn)
|
|
render_hook(view, "map_ready", %{})
|
|
|
|
# Use the loading_generation that the LV currently has (default 0).
|
|
send(view.pid, {:load_historical_batch, 0, 0})
|
|
Process.sleep(20)
|
|
assert render(view) =~ "aprs-map"
|
|
end
|
|
|
|
test "{:load_historical_batch, _, stale_gen} with stale generation is ignored", %{conn: conn} do
|
|
{:ok, view, _html} = live(conn, "/?lat=33.0&lng=-96.0&z=10", on_error: :warn)
|
|
render_hook(view, "map_ready", %{})
|
|
|
|
# Use a wildly mismatched generation — the handler should ignore it.
|
|
send(view.pid, {:load_historical_batch, 0, 99_999})
|
|
Process.sleep(20)
|
|
assert render(view) =~ "aprs-map"
|
|
end
|
|
|
|
test "{:historical_loading_timeout, _} for stale generation no-ops", %{conn: conn} do
|
|
{:ok, view, _html} = live(conn, "/?lat=33.0&lng=-96.0&z=10", on_error: :warn)
|
|
send(view.pid, {:historical_loading_timeout, 99_999})
|
|
Process.sleep(20)
|
|
assert render(view) =~ "aprs-map"
|
|
end
|
|
|
|
test "{:process_pending_bounds} message", %{conn: conn} do
|
|
{:ok, view, _html} = live(conn, "/?lat=33.0&lng=-96.0&z=10", on_error: :warn)
|
|
send(view.pid, {:process_pending_bounds})
|
|
Process.sleep(20)
|
|
assert render(view) =~ "aprs-map"
|
|
end
|
|
|
|
test "Logger.debug-laden bounds-update path runs format strings at :debug level", %{conn: conn} do
|
|
original_level = Logger.level()
|
|
Logger.configure(level: :debug)
|
|
|
|
try do
|
|
{:ok, view, _html} = live(conn, "/?lat=33.0&lng=-96.0&z=10", on_error: :warn)
|
|
render_hook(view, "map_ready", %{})
|
|
|
|
# Send bounds events so that Logger.debug calls in process_bounds_update,
|
|
# handle_info_process_bounds_update, and process_bounds_from_params get
|
|
# their string interpolations evaluated.
|
|
send(view.pid, {:process_bounds_update, %{north: 34.0, south: 32.0, east: -95.0, west: -97.0}})
|
|
|
|
render_hook(view, "bounds_changed", %{
|
|
"bounds" => %{"north" => 34.0, "south" => 32.0, "east" => -95.0, "west" => -97.0}
|
|
})
|
|
|
|
render_hook(view, "update_map_state", %{
|
|
"center" => %{"lat" => 33.5, "lng" => -96.5},
|
|
"zoom" => 11,
|
|
"bounds" => %{"north" => 34.0, "south" => 32.0, "east" => -95.0, "west" => -97.0}
|
|
})
|
|
|
|
Process.sleep(50)
|
|
after
|
|
Logger.configure(level: original_level)
|
|
end
|
|
end
|
|
|
|
test "postgres_packet via PubSub", %{conn: conn} do
|
|
{:ok, view, _html} = live(conn, "/?lat=33.0&lng=-96.0&z=12", on_error: :warn)
|
|
render_hook(view, "map_ready", %{})
|
|
|
|
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: "WIDE1-1"
|
|
}
|
|
|
|
send(view.pid, {:postgres_packet, packet})
|
|
Process.sleep(50)
|
|
assert render(view) =~ "aprs-map"
|
|
end
|
|
end
|
|
end
|